我想在JXTreeTable
中使用JComboBox
作为单元格编辑器。它可以与标准DefaultCellEditor
(即用a 单击计数开始等于2)的工作正常。
现在,我希望该列在上可以编辑,只有一个单击。因此,我在代码中添加了cellEditor.setClickCountToStart(1);
语句。
这是我的SSCCE:
import java.awt.BorderLayout;
import java.awt.Dimension;
import javax.swing.DefaultCellEditor;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import org.jdesktop.swingx.JXTreeTable;
import org.jdesktop.swingx.treetable.DefaultMutableTreeTableNode;
import org.jdesktop.swingx.treetable.DefaultTreeTableModel;
public class TestCellEditorForJXTreeTable {
/** The JXTreeTable */
JXTreeTable treeTable;
/** The model */
DefaultTreeTableModel treeTableModel;
/** Constructor */
public TestCellEditorForJXTreeTable() {
treeTable = new JXTreeTable();
treeTableModel = new DefaultTreeTableModel() {
@Override
public String getColumnName(int column) {
switch (column) {
case 0:
return "A";
case 1:
return "B";
}
return null;
}
@Override
public Object getValueAt(Object node, int column) {
switch (column) {
case 0:
return ((DefaultMutableTreeTableNode) node).getUserObject();
case 1:
return "Value in B";
}
return null;
}
@Override
public int getColumnCount() {
return 2;
}
@Override
public boolean isCellEditable(Object node, int column) {
return column == 1;
}
};
treeTable.setTreeTableModel(treeTableModel);
}
public static void main(String[] args) {
TestCellEditorForJXTreeTable test = new TestCellEditorForJXTreeTable();
// Root node
DefaultMutableTreeTableNode root = new DefaultMutableTreeTableNode("root");
test.treeTableModel.setRoot(root);
// New nodes/rows
DefaultMutableTreeTableNode node = new DefaultMutableTreeTableNode("child_node");
test.treeTableModel.insertNodeInto(node, root, 0);
DefaultMutableTreeTableNode node2 = new DefaultMutableTreeTableNode("child_node2");
test.treeTableModel.insertNodeInto(node2, root, 1);
// Showing the frame
showTable(test.treeTable);
// Setting the cell editor
DefaultCellEditor cellEditor = new DefaultCellEditor(new JComboBox(new String[]{"1", "2", "3"}));
cellEditor.setClickCountToStart(1);
test.treeTable.getColumn(1).setCellEditor(cellEditor);
}
/** Shows a JXTreeTable in a frame */
private static void showTable(JXTreeTable table) {
JFrame frame = new JFrame("Testing cell editor for JXTreeTable");
frame.setPreferredSize(new Dimension(640, 480));
frame.setLayout(new BorderLayout());
frame.add(table, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
但是现在看起来很丑:
当我单击一个可编辑的单元格时,它将打开JComboBox
弹出菜单( Great!这是我所期望的!),但是此弹出菜单立即关闭( erf!。闪烁。我必须在选定的单元格上单击第二次才能确定打开。
每次我在"可编辑"列中选择另一个单元格时,问题会重复。
首次单击后,如何获得JComboBox
弹出菜单真正打开?
谢谢。
编辑2014-01-24
这是同一示例,但使用JTable
。JComboBox
弹出菜单不 flash 。
import java.awt.BorderLayout;
import java.awt.Dimension;
import javax.swing.DefaultCellEditor;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
public class TestCellEditorForJTable {
/** The JTable */
JTable table;
/** The model */
DefaultTableModel tableModel;
/** Constructor */
public TestCellEditorForJTable() {
table = new JTable();
tableModel = new DefaultTableModel(new String[] {"A", "B"}, 0) {
@Override
public boolean isCellEditable(int row, int column) {
return column == 1;
}
};
table.setModel(tableModel);
}
public static void main(String[] args) {
TestCellEditorForJTable test = new TestCellEditorForJTable();
// New rows
test.tableModel.insertRow(0, new String[] {"Value1 in A", "Value1 in B"});
test.tableModel.insertRow(1, new String[] {"Value2 in A", "Value2 in B"});
// Showing the frame
showTable(test.table);
// Setting the cell editor
DefaultCellEditor cellEditor = new DefaultCellEditor(new JComboBox(new String[]{"1", "2", "3"}));
cellEditor.setClickCountToStart(1);
test.table.getColumnModel().getColumn(1).setCellEditor(cellEditor);
}
/** Shows a table in a frame */
private static void showTable(JTable table) {
JFrame frame = new JFrame("Testing cell editor for JTable");
frame.setPreferredSize(new Dimension(640, 480));
frame.setLayout(new BorderLayout());
frame.add(table, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
我忘了提到我正在使用Java 1.6。
编辑2014-01-24(2)
使用Kleopatra答案的ContainerListener
和FocusListener
,并运行相同的执行流,我使用JXTreeTable
SSCCE获得以下输出:
// first click
24.01.2014 13:10:59 my.pkg.TestCellEditorForJXTreeTable$2 componentAdded
INFO: java.awt.event.ContainerEvent[COMPONENT_ADDED...JXTreeTable...
24.01.2014 13:10:59 my.pkg.TestCellEditorForJXTreeTable$3 focusGained
INFO: java.awt.FocusEvent[FOCUS_GAINED...JXTreeTable...
24.01.2014 13:10:59 my.pkg.TestCellEditorForJXTreeTable$2 componentRemoved
INFO: java.awt.event.ContainerEvent[COMPONENT_REMOVED...JXTreeTable...
24.01.2014 13:10:59 my.pkg.TestCellEditorForJXTreeTable$3 focusLost
INFO: java.awt.FocusEvent[FOCUS_LOST...JXTreeTable...
// second click
24.01.2014 13:11:02 my.pkg.TestCellEditorForJXTreeTable$2 componentAdded
INFO: java.awt.event.ContainerEvent[COMPONENT_ADDED...JXTreeTable...
24.01.2014 13:11:02 my.pkg.TestCellEditorForJXTreeTable$3 focusGained
INFO: java.awt.FocusEvent[FOCUS_GAINED...JXTreeTable...
24.01.2014 13:11:02 my.pkg.TestCellEditorForJXTreeTable$2 componentRemoved
INFO: java.awt.event.ContainerEvent[COMPONENT_REMOVED...JXTreeTable...
24.01.2014 13:11:02 my.pkg.TestCellEditorForJXTreeTable$3 focusLost
INFO: java.awt.FocusEvent[FOCUS_LOST...JXTreeTable...
棘手的错误 - 我认为这确实是一个核心问题。
让我们首先定义发生什么/何时发生的情况:以平原桌子示例(顺便说一句:
- 运行
- 单击单元格(1,1),即最后一行,第二列:表开始编辑,Combo的弹出窗口正在显示
- 在仍在编辑的同时(请注意,对于不是不重要的单击之间的其他任何地方),单击单元格(0,1):表开始编辑该单元格,Combo的弹出窗口被隐藏
挖掘揭示了可能的原因:在 之后,这是一个不排序的焦点块,再次添加组合作为编辑组件。要查看,将集装箱列列器注册到表格,然后将重点放置器注册到组合并打印事件
ContainerListener containerL = new ContainerListener() {
@Override
public void componentRemoved(ContainerEvent e) {
LOG.info("" + e);
}
@Override
public void componentAdded(ContainerEvent e) {
LOG.info("" + e);
}
};
table.addContainerListener(containerL);
FocusListener focusL = new FocusListener() {
@Override
public void focusGained(FocusEvent e) {
LOG.info("" + e);
// following line is a hack around: force the popup open
// ((JComboBox) cellEditor.getComponent()).setPopupVisible(true);
}
@Override
public void focusLost(FocusEvent e) {
LOG.info("" + e);
}
};
cellEditor.getComponent().addFocusListener(focusL);
输出:
// first click
24.01.2014 12:13:44 org.jdesktop.swingx.table.TestCellEditorForJTable$2 componentAdded
INFO: java.awt.event.ContainerEvent[COMPONENT_ADDED,child=null] on javax.swing.JTable...
24.01.2014 12:13:44 org.jdesktop.swingx.table.TestCellEditorForJTable$3 focusGained
INFO: java.awt.FocusEvent[FOCUS_GAINED,permanent,opposite=javax.swing.JTable
// second click
24.01.2014 12:13:49 org.jdesktop.swingx.table.TestCellEditorForJTable$2 componentRemoved
INFO: java.awt.event.ContainerEvent[COMPONENT_REMOVED,child=null] on javax.swing.JTable
24.01.2014 12:13:49 org.jdesktop.swingx.table.TestCellEditorForJTable$2 componentAdded
INFO: java.awt.event.ContainerEvent[COMPONENT_ADDED,child=null] on javax.swing.JTable
// here's the problem: focusLost _after_ added again
24.01.2014 12:13:49 org.jdesktop.swingx.table.TestCellEditorForJTable$3 focusLost
INFO: java.awt.FocusEvent[FOCUS_LOST,permanent,opposite=javax.swing.JTable
24.01.2014 12:13:49 org.jdesktop.swingx.table.TestCellEditorForJTable$3 focusGained
INFO: java.awt.FocusEvent[FOCUS_GAINED,permanent,opposite=javax.swing.JTable
快速攻击可能是迫使焦点列表中的弹出窗口打开。不过,没有检查副作用。
有趣的事实
如果您从ComboBox选择一个值,则编辑器按预期工作(下一次点击可编辑单元将按照您的意愿打开该单元格的组合),但是如果您在不选择值的情况下关闭Combobox而不选择一个值你描述。因此,这个问题似乎是Combobox在选择一个值或聚焦在其他组件中之前不会停止编辑。因此,第一次单击另一个单元格会使自己的编辑器请求焦点而不是开始编辑。
解释
仔细观察DefaultCelleditor实现的问题是,仅将ActionListener附加到Combobox上,从而导致fireEdingingStopped()通过EditordeLegate调用(当选择> combobox都关闭或取消或取消时,都没有发生任何事情。不选择值:
public DefaultCellEditor(final JComboBox comboBox) {
editorComponent = comboBox;
comboBox.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE);
delegate = new EditorDelegate() {...}
comboBox.addActionListener(delegate); // delegate is the ActionListener
}
protected class EditorDelegate implements ActionListener, ItemListener, Serializable {
...
public void actionPerformed(ActionEvent e) {
DefaultCellEditor.this.stopCellEditing(); // This will finally call fireEditingStopped();
}
}
解决方案
使用ComboBox作为编辑器制作自己的tablecelleditor,然后根据需要将弹出式列表列为fireeditingStopped()或FireEditingCanceLed()。例如:
class ComboBoxEditor extends AbstractCellEditor implements TableCellEditor {
private JComboBox editor;
private int clickCountToStart = 2;
private Object selectedValue;
public ComboBoxEditor(Object[] selectableValues) {
editor = new JComboBox(selectableValues);
editor.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
selectedValue = editor.getSelectedItem();
ComboBoxEditor.this.fireEditingStopped();
}
});
editor.addPopupMenuListener(new PopupMenuListener() {
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
// Nothing to do here, it's not relevant to your purpose
}
@Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
ComboBoxEditor.this.fireEditingStopped();
}
@Override
public void popupMenuCanceled(PopupMenuEvent e) {
ComboBoxEditor.this.fireEditingCanceled();
}
});
}
public void setClickCountToStart(int clickCountToStart) {
this.clickCountToStart = clickCountToStart;
}
public int getClickCountToStart() {
return clickCountToStart;
}
// Rest of implementation is up to you, look into DefaultCellEditor implementation
}