Java 以前选择的JTable单元格会在按键时触发编辑器,即使在明确取消选择时也是如此
下面是一个很好的例子,我制作了一个小演示,它以一种公认的复杂方式完成了一项简单的任务。下面显示的GUI显示一列表示真/假值的图标。如果单击某个图标,它会将该值更改为与其相反的值。与复选框非常相似,但外观不同,扩展性更强(例如,我可以在将来将其更改为循环使用十几个符号,而不仅仅是两个布尔符号) 我使用了一个自定义编辑器,它是JComponent的虚拟扩展。但是,您甚至从来没有看到过这个虚拟组件,因为一旦它拾取一个Java 以前选择的JTable单元格会在按键时触发编辑器,即使在明确取消选择时也是如此,java,swing,user-interface,jtable,listener,Java,Swing,User Interface,Jtable,Listener,下面是一个很好的例子,我制作了一个小演示,它以一种公认的复杂方式完成了一项简单的任务。下面显示的GUI显示一列表示真/假值的图标。如果单击某个图标,它会将该值更改为与其相反的值。与复选框非常相似,但外观不同,扩展性更强(例如,我可以在将来将其更改为循环使用十几个符号,而不仅仅是两个布尔符号) 我使用了一个自定义编辑器,它是JComponent的虚拟扩展。但是,您甚至从来没有看到过这个虚拟组件,因为一旦它拾取一个MousePressed事件,它就会导致编辑器fireEditingStopped(
MousePressed
事件,它就会导致编辑器fireEditingStopped()
。它工作得很好,除了我发现的一个奇怪的错误。如果您单击某个符号进行更改,然后将鼠标移动到屏幕上的其他位置并按键盘键,它将在最后单击的单元格中显示虚拟编辑器(这将有效地清空该单元格),并一直保持该编辑器,直到您将鼠标移动到该单元格或单击其他单元格为止
为了解决这个问题,我在渲染器中添加了一行代码,在渲染后总是取消选择整个表。这非常有效,我已经验证了整个表确实是取消选择的。但是,尽管如此,如果按键盘键,它仍会执行上一个编辑单元格的编辑器。如何防止这种行为?我的应用程序中还有其他键盘侦听器,如果没有选择任何单元格,我认为不应该执行它们的任何编辑器
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.imageio.ImageIO;
import javax.swing.AbstractCellEditor;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
public class JTableBooleanIcons {
private JFrame frame;
private DefaultTableModel tableModel;
private JTable table;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
JTableBooleanIcons window = new JTableBooleanIcons();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public JTableBooleanIcons() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 450, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
tableModel = new DefaultTableModel(new Object[]{"Words", "Pictures"},0);
table = new JTable(tableModel);
table.setRowHeight(40);
tableModel.addRow(new Object[]{"click icon to change", false});
tableModel.addRow(new Object[]{"click icon to change", true});
tableModel.addRow(new Object[]{"click icon to change", false});
tableModel.addRow(new Object[]{"click icon to change", true});
tableModel.addRow(new Object[]{"click icon to change", false});
tableModel.addRow(new Object[]{"click icon to change", true});
tableModel.addRow(new Object[]{"click icon to change", false});
tableModel.addRow(new Object[]{"click icon to change", true});
frame.getContentPane().add(table, BorderLayout.CENTER);
table.getColumn("Pictures").setCellRenderer(new BooleanIconRenderer());
table.getColumn("Pictures").setCellEditor(new BooleanIconEditor());
}
@SuppressWarnings("serial")
private class BooleanIconRenderer extends DefaultTableCellRenderer implements TableCellRenderer {
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int col) {
String iconFilename = null;
if ((boolean) value) {
iconFilename = "yes.png";
value = true;
} else {
iconFilename = "no.png";
value = false;
}
try {
setIcon(new ImageIcon(ImageIO.read(BooleanIconRenderer.class.getResourceAsStream(iconFilename))));
} catch (Exception e) {
System.err.println("Failed to load the icon.");
}
if (isSelected) {
this.setBackground(Color.white);
} else {
this.setBackground(Color.white);
}
table.getSelectionModel().clearSelection();
return this;
}
}
@SuppressWarnings("serial")
private class BooleanIconEditor extends AbstractCellEditor implements TableCellEditor, MouseListener {
private BooleanComponent boolComp;
public BooleanIconEditor() {
boolComp = new BooleanComponent(false);
boolComp.addMouseListener(this);
}
@Override
public Object getCellEditorValue() {
return boolComp.getValue();
}
@Override
public Component getTableCellEditorComponent(JTable table,
Object value, boolean isSelected, int row, int column) {
boolComp.setValue(! (boolean) value);
return boolComp;
}
@Override
public void mouseClicked(MouseEvent e) {
this.fireEditingStopped();
}
@Override
public void mousePressed(MouseEvent e) {
this.fireEditingStopped();
}
@Override
public void mouseReleased(MouseEvent e) {
this.fireEditingStopped();
}
@Override
public void mouseEntered(MouseEvent e) {
this.fireEditingStopped();
}
@Override
public void mouseExited(MouseEvent e) {
this.fireEditingStopped();
}
}
@SuppressWarnings("serial")
private class BooleanComponent extends JComponent {
private boolean value;
public BooleanComponent(boolean value) {
this.value = value;
}
public boolean getValue() {
return value;
}
public void setValue(boolean value) {
this.value = value;
}
}
}
出现问题的原因是您实际上没有在编辑器中渲染任何内容。您依赖的是这样一个事实,即用户只会“单击”单元格以更改其值。我认为这有点短视,但这是你的计划。 要立即修复它,只需添加
@Override
public boolean isCellEditable(EventObject e) {
return (e instanceof MouseEvent);
}
给你booleanicodetor
旁注
在单元渲染器中,不应加载图像。这些应该作为构造函数的一部分预先缓存,或者更好,作为静态
字段变量。你可能正在这样做,但只是以防万一
已更新
当我谈到这个话题的时候。应该避免在渲染器中更改表的状态。这是非常不安全的,当表一次又一次地试图重新呈现您所做的更改时,可能会让您陷入无限的地狱循环
如果确实要隐藏选择(我不知道为什么),可以将表格的选择设置为与表格背景颜色匹配,或者将其设置为透明颜色。也不要忘记更改选择前景;)
更新#2
具有键盘支持的示例;)-忍不住
public class JTableBooleanIcons {
private JFrame frame;
private DefaultTableModel tableModel;
private JTable table;
private ImageIcon yesIcon;
private ImageIcon noIcon;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
JTableBooleanIcons window = new JTableBooleanIcons();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public JTableBooleanIcons() {
try {
yesIcon = (new ImageIcon(ImageIO.read(BooleanIconRenderer.class.getResourceAsStream("/yes.png"))));
noIcon = (new ImageIcon(ImageIO.read(BooleanIconRenderer.class.getResourceAsStream("/no.png"))));
} catch (Exception e) {
e.printStackTrace();
}
initialize();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 450, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
tableModel = new DefaultTableModel(new Object[]{"Words", "Pictures"}, 0);
table = new JTable(tableModel);
table.setRowHeight(40);
tableModel.addRow(new Object[]{"click icon to change", false});
tableModel.addRow(new Object[]{"click icon to change", true});
tableModel.addRow(new Object[]{"click icon to change", false});
tableModel.addRow(new Object[]{"click icon to change", true});
tableModel.addRow(new Object[]{"click icon to change", false});
tableModel.addRow(new Object[]{"click icon to change", true});
tableModel.addRow(new Object[]{"click icon to change", false});
tableModel.addRow(new Object[]{"click icon to change", true});
frame.getContentPane().add(table, BorderLayout.CENTER);
table.getColumn("Pictures").setCellRenderer(new BooleanIconRenderer());
table.getColumn("Pictures").setCellEditor(new BooleanIconEditor());
}
@SuppressWarnings("serial")
private class BooleanIconRenderer extends DefaultTableCellRenderer implements TableCellRenderer {
public BooleanIconRenderer() {
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int col) {
super.getTableCellRendererComponent(table, null, isSelected, hasFocus, row, col);
if ((boolean) value) {
setIcon(yesIcon);
} else {
setIcon(noIcon);
}
return this;
}
}
@SuppressWarnings("serial")
private class BooleanIconEditor extends AbstractCellEditor implements TableCellEditor, MouseListener {
private BooleanComponent boolComp;
private boolean isMouseEvent;
public BooleanIconEditor() {
boolComp = new BooleanComponent(false);
boolComp.addMouseListener(this);
InputMap im = boolComp.getInputMap(JComponent.WHEN_FOCUSED);
ActionMap am = boolComp.getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "click");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "click");
am.put("click", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Clicked");
boolComp.setValue(!boolComp.getValue());
}
});
}
@Override
public boolean isCellEditable(EventObject e) {
isMouseEvent = e instanceof MouseEvent;
return true; //(e instanceof MouseEvent);
}
@Override
public Object getCellEditorValue() {
return boolComp.getValue();
}
@Override
public Component getTableCellEditorComponent(JTable table,
Object value, boolean isSelected, int row, int column) {
boolean state = (boolean) value;
if (isMouseEvent) {
state = !state;
}
boolComp.setValue(state);
boolComp.setOpaque(isSelected);
boolComp.setBackground(table.getSelectionBackground());
return boolComp;
}
@Override
public void mouseClicked(MouseEvent e) {
this.fireEditingStopped();
}
@Override
public void mousePressed(MouseEvent e) {
this.fireEditingStopped();
}
@Override
public void mouseReleased(MouseEvent e) {
this.fireEditingStopped();
}
@Override
public void mouseEntered(MouseEvent e) {
this.fireEditingStopped();
}
@Override
public void mouseExited(MouseEvent e) {
this.fireEditingStopped();
}
}
@SuppressWarnings("serial")
private class BooleanComponent extends JLabel {
private boolean value;
public BooleanComponent(boolean value) {
this.value = value;
}
public boolean getValue() {
return value;
}
public void setValue(boolean value) {
this.value = value;
if (value) {
setIcon(yesIcon);
} else{
setIcon(noIcon);
}
}
}
}
@Mad's展示了如何实现鼠标处理和键绑定,另一种方法是使用JToggleButton
、JCheckBox的父级以及合适的图标。切换按钮已经知道如何显示所选的图标
和处理事件。修改后的ValueRenderer
如下所示;ValueEditor
不变;文本是可选的
private static class ValueRenderer extends JToggleButton
implements TableCellRenderer {
private static final Color hilite = new Color(0xE8E8E8);
private static final Icon YES = UIManager.getIcon("InternalFrame.maximizeIcon");
private static final Icon NO = UIManager.getIcon("InternalFrame.closeIcon");
public ValueRenderer() {
this.setOpaque(true);
this.setIcon(NO);
this.setSelectedIcon(YES);
}
...
}
非常感谢您的修复和其他评论!我不想让单元格显示为可选的原因是,该布局在用户界面中并不是真正想要看起来像一个“表”。在我看来,表将是实现它的最方便的方式。想象一下Photoshop中的“图层选项卡”,在该选项卡中,图层名称的左侧或右侧有几个图标,每个图标都在图层上执行命令(查看/隐藏/等等)。我希望我的图标能被听到,但我不希望单元格高亮显示,因为它们看起来不像单元格。希望这是有意义的。所有这些听起来都很合理,它很简单,将表格的选择背景设置为与背景相同,并对选择前景执行相同的操作(将其与表格的前景匹配)-这将很容易让其他渲染器破坏您的设计;)+1个用于;还要考虑<代码> uimaNavig.GeTyCon()/Case>。