Editor de activadores de células JTable previamente seleccionado al presionar una tecla, incluso cuando se deselecciona explícitamente

Siguiendotrashgodexcelente ejemploaquí, Armé una pequeña demostración que realiza una tarea simple de una manera probablemente muy complicada. La GUI que se muestra a continuación muestra una columna de iconos que representan valores verdaderos / falsos. Si hace clic en un icono, cambia el valor al contrario de lo que sea. Más o menos como una casilla de verificación, pero con una apariencia diferente, y más extensible (por ejemplo, podría cambiarlo en el futuro para alternar entre una docena de símbolos en lugar de solo dos símbolos booleanos).

Lo hice utilizando un editor personalizado que es una extensión ficticia de un JComponent. Sin embargo, nunca se ve este componente ficticio, porque tan pronto como recoge unMousePressed evento, hace que el editorfireEditingStopped(). Funciona muy bien, excepto por un error extraño que encontré. Si hace clic en un símbolo para cambiarlo, luego mueve el mouse a algún otro lugar de la pantalla y presiona una tecla del teclado, aparece el editor ficticio en la última celda en la que se hizo clic (lo que efectivamente deja la celda en blanco) y permanece allí hasta que Mueva el mouse a la celda o haga clic en una celda diferente.

Para solucionar esto, agregué una línea en el renderizador que siempre deselecciona toda la tabla después de renderizarla. Esto funciona muy bien, y he verificado que toda la tabla está deseleccionada. Sin embargo, a pesar de eso, si presiona una tecla del teclado,todavía Ejecuta el editor de la última celda editada. ¿Cómo puedo prevenir este comportamiento? Tengo otros oyentes de teclado en mi aplicación, y si no se selecciona ninguna celda, no creo que se deba ejecutar ninguno de sus editores.

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;
        }
    }
}

Respuestas a la pregunta(2)

Su respuesta a la pregunta