Previously selected JTable cell triggers editor on key press, even when explicitly deselected

后端 未结 2 1983
醉酒成梦
醉酒成梦 2020-12-20 02:24

Following trashgod\'s excellent example here, I put together a little demo which accomplishes a simple task in a probably admittedly convoluted way. The GUI shown below dis

相关标签:
2条回答
  • 2020-12-20 03:11

    The reason for your problem is that you are actually not rendering anything within the editor. You're relying on the fact that the user is only ever going to "click" the cell to change it's value. I'd consider this a little short-sighted, but, it's you program.

    To fix it immediately, simply add...

    @Override
    public boolean isCellEditable(EventObject e) {
        return (e instanceof MouseEvent);
    }
    

    To you BooleanIconEditor.

    On a side note.

    In you cell renderer, you shouldn't be loading images. These should have been pre-cached as part of the constructor or even better, as static field variables. You may be doing this, but just in case.

    Updated

    While I'm on the subject. You should avoid changing the state of the table from within the renderer. This is really unsafe and could end you up in an infinite loop of hell as the table tries to re-render the changes you've made, again and again...

    If you really want to hide the selection (I'm not sure why you would), you could either set the table's selection to match the tables background color OR make it a transparent color. Don't forget to change the selection foreground as well ;)

    Update #2

    Example with keyboard support ;) - couldn't resist...

    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);
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-20 03:23

    While @Mad's answer shows how to implement mouse handling and key bindings, an alternative is to use JToggleButton, the parent of JCheckBox, and a suitable Icon. The toggle button already knows how to display the chosen Icon and handle events. The modified ValueRenderer is shown below; the ValueEditor is unchanged; the text is optional.

    image

    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);
        }
        ...
    }
    
    0 讨论(0)
提交回复
热议问题