Java 作为JTable单元格编辑器的自定义JComponent子类不会获取鼠标/键盘事件
最近,我创建了两个非常简单但非常有用的组件,称为AttributesEditableView和AttributesEditor。AttributesEditableView设计为仅显示任意数量的属性,而AttributesEditor使编辑属性成为可能 所以,假设我们有三个属性,它们可以取值“Y”、“N”和“?”(其中“?”表示“未指定”)。该视图类似于Java 作为JTable单元格编辑器的自定义JComponent子类不会获取鼠标/键盘事件,java,swing,jtable,tablecelleditor,Java,Swing,Jtable,Tablecelleditor,最近,我创建了两个非常简单但非常有用的组件,称为AttributesEditableView和AttributesEditor。AttributesEditableView设计为仅显示任意数量的属性,而AttributesEditor使编辑属性成为可能 所以,假设我们有三个属性,它们可以取值“Y”、“N”和“?”(其中“?”表示“未指定”)。该视图类似于[[Y][N][?]]: 在编辑器中,用户可以使用箭头键在属性之间左右跳跃,并通过按空格键切换特定(当前选定)属性的值 (我想你们中熟悉JTab
[[Y][N][?]]
:
在编辑器中,用户可以使用箭头键在属性之间左右跳跃,并通过按空格键切换特定(当前选定)属性的值
(我想你们中熟悉JTable
及其特性的人已经了解我现在的处境了)
它在表单中运行得很好,但是当我想用这个AttributeEditor制作一个TableCellEditor
时,我就卡住了,因为JTable
要么“吃掉”所有事件,要么是我做错了什么,所以我的AttributeEditor根本不获取事件…:(
我的逻辑如下:当编辑器组件为JTextField
时,为什么defaultcelldeditor
会获取这些事件(例如按左键或右键),而我的单元格编辑器不会
我知道我没有给出这里所有的类,但我相信下面的两个类应该给那些已经遇到这个问题的人足够的信息,让他们能够告诉我如何使这个单元格编辑器按预期工作
更新:我通过重构AttributeEditor类解决了这个问题。现在它使用键绑定而不是KeyListener
,一切正常。吸取的教训
您可以在下面的屏幕截图上看到AttributesEditableView、AttributesEditor和AttributesCellEditor正在运行(我也有渲染器,但无法将其打开,它看起来就像AttributesEditableView):
在上面的屏幕截图中,第一个属性可能有'Y'、'N'和'?'值,第二个属性可能有'A'、'B'、'C'和'D'值,第三个属性可能有'T'和'F'值
以下是课程:
属性编辑器
/**
* Authors: Dejan Lekic , http://dejan.lekic.org
* License: MIT
*/
public class AttributesCellEditor extends AbstractCellEditor
implements TableCellEditor, TreeCellEditor {
private AttributesEditor attributesEditor;
private AttributesColumnModel attrColModel;
private AttributesModel model;
private int clickCountToStart = 2;
DefaultCellEditor dfe;
public AttributesCellEditor(AttributesColumnModel argModel) {
super();
attrColModel = argModel;
model = new AttributesModel(attrColModel.getAllowedValues());
model.setDefaultValues(attrColModel.getDefaultValues());
attributesEditor = new AttributesEditor(model);
attributesEditor.setBorder(new BevelBorder(BevelBorder.LOWERED));
}
// ::::: CellEditor method implementations :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
@Override
public Object getCellEditorValue() {
System.out.println("getCellEditorValue()");
// before we return the value, we should also modify the attributes column model and set the
// selected attribute index to the index of what we last edited.
attrColModel.setSelectedAttributeIndex(model.getSelectedAttributeIndex());
return model.getAttributes();
}
// ::::: TableCellEditor method implementations ::::::::::::::::::::::::::::::::::::::::::::::::::::::::
@Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row,
int column) {
String val = (value == null) ? "" : value.toString();
model.setAttributes(val);
model.setSelectedAttributeIndex(attrColModel.getSelectedAttributeIndex());
attributesEditor.setModel(model);
return attributesEditor;
}
// ::::: TreeCellEditor method implementations ::::::::::::::::::::::::::::::::::::::::::::::::::::::::
// TODO: implement these
@Override
public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected,
boolean expanded, boolean leaf, int row) {
//To change body of generated methods, choose Tools | Templates.
throw new UnsupportedOperationException("Not supported yet.");
}
// ::::: AbstractCellEditor method overrides ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
@Override
public boolean isCellEditable(EventObject e) {
if (e instanceof MouseEvent) {
return ((MouseEvent) e).getClickCount() >= clickCountToStart;
}
return true;
}
/**
* {@inheritDoc}
* @return
*/
@Override
public boolean stopCellEditing() {
boolean ret = super.stopCellEditing();
return ret;
}
} // AttributesCellEditor class
属性编辑器
/**
* Authors: Dejan Lekic , http://dejan.lekic.org
* License: MIT
*/
public class AttributesEditor
extends AttributesEditableView
implements PropertyChangeListener, FocusListener, KeyListener {
public AttributesEditor() {
super();
editorComponent = true;
setFocusable(true);
setBackground(Color.white);
// Let's copy the border from the TextField
Border b = (Border) UIManager.getLookAndFeelDefaults().get("TextField.border");
setBorder(b);
// Let's copy the insets from the TextField
Insets insets = (Insets) UIManager.getLookAndFeelDefaults().get("TextField.margin");
getInsets().set(insets.top, insets.left, insets.bottom, insets.right);
addKeyListener(this);
// listeners...
addFocusListener(this);
}
public AttributesEditor(AttributesModel argModel) {
this();
setOpaque(true);
setFocusable(true);
setBackground(Color.white);
repaint();
setModel(argModel);
}
// :::: PropertyChangeListener method implementations :::::::::::::::::::::::::::::::::::::::::::::::::
@Override
public void propertyChange(PropertyChangeEvent evt) {
String str = (String) evt.getNewValue();
System.out.println("CALL: AttributesEditor.propertyChange() : " + str);
updateView();
}
// :::: FocusListener method implementations ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
@Override
public void focusGained(FocusEvent e) {
int sel = model.getSelectedAttributeIndex();
if (sel < 0) {
model.setSelectedAttributeIndex(0);
}
sel = model.getSelectedAttributeIndex();
labels[sel].setBackground(model.getSelectedBackgroundColor());
}
@Override
public void focusLost(FocusEvent e) {
model.setSelectedAttributeIndex(-1);
updateView();
}
// :::: KeyListener method implementations ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
@Override
public void keyTyped(KeyEvent e) {
// Weird thing is, arrow keys do not trigger keyTyped() to be called,
// so we have to use keyPressed here, or keyReleased
}
/**
* Weird thing is, arrow keys do not trigger keyTyped() to be called, so we have to use keyPressed/keyReleased here.
* @param e
*/
@Override
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
switch (key) {
case KeyEvent.VK_UP:
model.previous(model.getSelectedAttributeIndex());
break;
case KeyEvent.VK_DOWN:
case KeyEvent.VK_SPACE:
model.next(model.getSelectedAttributeIndex());
break;
case KeyEvent.VK_LEFT:
model.previousAttribute();
updateView();
break;
case KeyEvent.VK_RIGHT:
model.nextAttribute();
updateView();
break;
default:
int idx = model.getSelectedAttributeIndex();
char chr = Character.toUpperCase(e.getKeyChar());
if (model.isValid(idx, chr)) {
model.setValue(idx, chr);
}
} // switch
} // keyPressed() method
@Override
public void keyReleased(KeyEvent e) {
// nothing
}
} // AttributesEditor class
/**
*作者:Dejan Lekic,http://dejan.lekic.org
*执照:麻省理工学院
*/
公共类属性编辑器
扩展AttributesEditableView
实现PropertyChangeListener、FocusListener、KeyListener{
公共属性编辑器(){
超级();
editorComponent=true;
设置聚焦(真);
挫折地面(颜色:白色);
//让我们从文本字段复制边框
Border b=(Border)UIManager.getLookAndFeelDefaults().get(“TextField.Border”);
命令(b);
//让我们从文本字段复制插图
Insets Insets=(Insets)UIManager.getLookAndFeelDefaults().get(“TextField.margin”);
getInsets().set(insets.top、insets.left、insets.bottom、insets.right);
addKeyListener(此);
//听众。。。
addFocusListener(此);
}
公共属性编辑器(属性模型argModel){
这个();
set不透明(true);
设置聚焦(真);
挫折地面(颜色:白色);
重新油漆();
setModel(argModel);
}
//::PropertyChangeListener方法实现:::::::::::::::::::::::::::::::::::::
@凌驾
公共作废属性更改(属性更改事件evt){
String str=(String)evt.getNewValue();
System.out.println(“调用:AttributeEdit.propertyChange():”+str);
updateView();
}
//:::FocusListener方法实现::::::::::::::::::::::::::::::::::::
@凌驾
获得公共无效焦点(焦点事件e){
int sel=model.getSelectedAttributeIndex();
如果(sel<0){
model.setSelectedAttributeIndex(0);
}
sel=model.getSelectedAttributeIndex();
标签[sel].setBackground(model.getSelectedBackgroundColor());
}
@凌驾
公共无效焦点丢失(焦点事件e){
model.setSelectedAttributeIndex(-1);
updateView();
}
//:::KeyListener方法实现:::::::::::::::::::::::::::::::::::::
@凌驾
public void keyTyped(KeyEvent e){
//奇怪的是,箭头键不会触发调用keyTyped(),
//所以我们必须在这里使用keyPressed,或者keyprelease
}
/**
*奇怪的是,箭头键不会触发调用keyTyped(),所以我们必须在这里使用keyPressed/KeyRelease。
*@param e
*/
@凌驾
按下公共无效键(按键事件e){
int key=e.getKeyCode();
开关(钥匙){
case KeyEvent.VK_UP:
model.previous(model.getSelectedAttributeIndex());
打破
case KeyEvent.VK_向下:
case KeyEvent.VK_空间:
model.next(model.getSelectedAttributeIndex());
打破
case KeyEvent.VK_左:
model.previousAttribute();
updateView();
打破
case KeyEvent.VK_RIGHT:
model.nextAttribute();
updateView();
打破
违约:
int idx=model.getSelectedAttributeIndex();
char chr=Character.toUpperCase(例如getKeyChar());
if(model.isValid(idx,chr)){
model.setValue(idx,chr);
}
}//开关
}//keyPressed()方法
@凌驾
公共无效密钥已释放(密钥事件e){
//没什么
}
}//属性编辑器类
属性编辑表视图
/**
* Authors: Dejan Lekic , http://dejan.lekic.org
* License: MIT
*/
public class AttributesEditableView
extends JComponent
implements PropertyChangeListener, MouseListener {
protected AttributesModel model;
protected JLabel[] labels;
protected boolean editorComponent;
protected boolean inTable;
public AttributesEditableView() {
super();
FlowLayout fl = new FlowLayout(FlowLayout.LEFT);
fl.setVgap(0);
setLayout(fl);
editorComponent = false;
inTable = false;
}
public AttributesEditableView(AttributesModel argModel) {
this();
setModel(argModel);
}
// :::: JComponent method overrides :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
/**
* I had to do this because it is a common problem with JComponent subclasses.
* More about it: http://docs.oracle.com/javase/tutorial/uiswing/painting/problems.html
*
* @param g
*/
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(getForeground());
}
// :::: PropertyChangeListener mthod implementations ::::::::::::::::::::::::::::::::::::::::::::::::::
@Override
public void propertyChange(PropertyChangeEvent evt) {
String str = (String) evt.getNewValue();
updateView();
}
// :::: <Interface> method implementations ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
@Override
public void mouseClicked(MouseEvent e) {
// labels have names in form of "attributelbl-3", we need to extract the ID from the name
String num = e.getComponent().getName().split("-")[1];
int idx = Integer.parseInt(num);
if (editorComponent) {
model.setSelectedAttributeIndex(idx);
}
model.next(idx);
} // mouseClicked() method
@Override
public void mousePressed(MouseEvent e) {
// do nothing
}
@Override
public void mouseReleased(MouseEvent e) {
// do nothing
}
@Override
public void mouseEntered(MouseEvent e) {
// labels have names in form of "attributelbl-3", we need to extract the ID from the name
String num = e.getComponent().getName().split("-")[1];
int idx = Integer.parseInt(num);
model.setSelectedAttributeIndex(idx);
updateView();
}
@Override
public void mouseExited(MouseEvent e) {
// labels have names in form of "attributelbl-3", we need to extract the ID from the name
String num = e.getComponent().getName().split("-")[1];
int idx = Integer.parseInt(num);
if (!editorComponent) {
model.setSelectedAttributeIndex(-1);
}
updateView();
}
/**
* Use this method to forcefully highlight specific attribute.
* @param argIndex
*/
public final void highlight(int argIndex) {
for (int i = 0; i < model.getNumberOfAttributes(); i++) {
if (i == argIndex) {
labels[i].setBackground(model.getSelectedBackgroundColor());
} else {
labels[i].setBackground(model.getBackgroundColor());
}
} // for
} // highlight() method
/**
* Extremely important method. Here we set the model, and generate JLabel objects for the attributes.
* We also set the values, and initialise the listeners.
* @param argModel
*/
public final void setModel(AttributesModel argModel) {
if (model != null) {
model.removePropertyChangeListener(this);
}
labels = null;
removeAll();
labels = new JLabel[argModel.getNumberOfAttributes()];
int w = 0;
int h = 0;
for (int i = 0; i < argModel.getNumberOfAttributes(); i++) {
String txt = "" + argModel.getValue(i);
System.out.println(txt);
JLabel lbl = new JLabel(txt);
labels[i] = lbl;
lbl.setName("attributelbl-" + i); // very important, because we will use the name to get the idx
lbl.setHorizontalAlignment(SwingConstants.CENTER);
lbl.setOpaque(true);
lbl.setBackground(argModel.getBackgroundColor());
if (isInTable()) {
lbl.setBorder(BorderFactory.createMatteBorder(0, 0, 0, 1, Color.GRAY));
}
int nh = lbl.getPreferredSize().height;
int nw = nh; // make the width to be equal to the height
lbl.setPreferredSize(new Dimension(nw, nh));
lbl.addMouseListener(this);
h = Math.max(h, lbl.getPreferredSize().height);
w = w + lbl.getPreferredSize().width;
add(lbl);
} // for
Dimension ps = new Dimension(w, h);
model = argModel;
model.addPropertyChangeListener(this);
} // setModel() method
public final AttributesModel getModel() {
return model;
}
public boolean isInTable() {
return inTable;
}
public void setInTable(boolean inTable) {
this.inTable = inTable;
}
/**
* Updates the view
*/
protected void updateView() {
String attrs = model.getAttributes();
for (int i = 0; i < model.getNumberOfAttributes(); i++) {
labels[i].setText("" + model.getValue(i));
if (model.getSelectedAttributeIndex() == i) {
labels[i].setBackground(model.getSelectedBackgroundColor());
} else {
labels[i].setBackground(model.getBackgroundColor());
}
}
}
} // AttributesEditableView class
/**
*作者:Dejan Lekic,http://dejan.lekic.org
*执照:麻省理工学院
*/
公共类AttributesEditableView
扩展JComponent