Search code examples
jtablejbuttonjcheckboxtablecellrenderertablecelleditor

Rendering JButton to get the JCheckBox behavior in a JTable by using images does not update my table


I am rendering an JButton with an image on it basically to simulate JCheckBox behavior. Why don't I just let the JTable to render JCheckbox? Simply because I need bigger checkboxes and there's no way I can resize them (as far as I know).

So, I have my own table cell renderer and editor. Both renders JButton and a checkbox images on them. What I am trying to do is to change the image from checked image to unchecked image (or vice versa) and update the cell value (boolean true or false). However, for some reason, getCellEditorValue() does not update my table. So, as soon as I release mouse, they value goes back to the original one.

Any help will be appreciated! Thanks a lot!

    public class TableExample extends JFrame {

    JScrollPane pane;
    JPanel panel;
    JTable table;
    Object[][] data;
    MyTableModel tm;
    Renderer buttonColumn;

    public TableExample(Object[][] data) throws IOException {

        String[] columns = {"Column#1", "Column#2", "Column#3", "Column#4", "Column#5", "Column#6"};
        this.data = data;
        this.setPreferredSize(new Dimension(700, 500));
        this.setMinimumSize(new Dimension(700, 500));
        this.setMaximumSize(new Dimension(700, 500));
        tm = new MyTableModel(this.data, columns);
        this.table = new JTable(tm);
        this.table.setRowHeight(50);
        table.setDefaultRenderer(Boolean.class, new Renderer(this, table, 5));
        table.setDefaultEditor(Boolean.class, new Editor(this, table, 5));
        this.pane = new JScrollPane(this.table);
        this.add(this.pane);

    }

    /**
     *
     * TABLE MODEL
     */
    public class MyTableModel extends AbstractTableModel {

        private Object[][] data;
        private String[] columns;

        public MyTableModel(Object[][] data, String[] columns) {
            this.data = data;
            this.columns = columns;

        }

        public void setColumns(String[] columns1) {
            this.columns = columns1;
        }

        @Override
        public int getRowCount() {
            return this.data.length;
        }

        @Override
        public int getColumnCount() {
            return this.columns.length;
        }

        @Override
        public String getColumnName(int columnIndex) {
            return columns[columnIndex];
        }

        @Override
        public Class<?> getColumnClass(int c) {
            return getValueAt(0, c).getClass();
        }

        @Override
        public boolean isCellEditable(int rowIndex, int columnIndex) {
            return true;
        }

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            return data[rowIndex][columnIndex];
        }

        public void setValueAt(String value, int row, int col) {

            if (DEBUG) {
                System.out.println("Setting value at " + row + "," + col
                        + " to " + value
                        + " (an instance of "
                        + value.getClass() + ")");
            }

            data[row][col] = value;
            fireTableCellUpdated(row, col);

            if (DEBUG) {
                System.out.println("New value of data:");
                printDebugData();
            }
        }

        private void printDebugData() {
            int numRows = getRowCount();
            int numCols = getColumnCount();

            for (int i = 0; i < numRows; i++) {
                System.out.print("    row " + i + ":");
                for (int j = 0; j < numCols; j++) {
                    System.out.print("  " + data[i][j]);
                }
                System.out.println();
            }
            System.out.println("--------------------------");
        }

    }
    /**
     *
     * CELL RENDERER
     */

    private class Renderer extends JButton implements TableCellRenderer {

        ImageIcon check;
        ImageIcon uncheck;
        boolean isChecked;

        public Renderer(JFrame frame, JTable table, int column) throws IOException {

            Image checkI = ImageIO.read(getClass().getResource("ukbuttonblue.png"));
            Image unCheckI = ImageIO.read(getClass().getResource("ukbuttonrev.png"));
            this.setBorderPainted(false);
            check = new ImageIcon(checkI);
            uncheck = new ImageIcon(unCheckI);

        }

        @Override
        public Component getTableCellRendererComponent(
                JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
            this.isChecked = (boolean) value;
            if (isChecked) {
                setIcon(check);

            } else {
                setIcon(uncheck);

            }

            return this;
        }
    }

    /**
     *
     * CELL EDITOR
     */

    private class Editor extends AbstractCellEditor implements TableCellEditor, ActionListener {

        private JButton editButton;

        ImageIcon check;
        ImageIcon uncheck;
        boolean isChecked;

        public Editor(JFrame frame, JTable table, int column) throws IOException {

            editButton = new JButton();
            editButton.addActionListener(this);
            editButton.setBorderPainted(false);

            Image checkI = ImageIO.read(getClass().getResource("check.png"));
            Image unCheckI = ImageIO.read(getClass().getResource("uncheck.png"));
            check = new ImageIcon(checkI);
            uncheck = new ImageIcon(unCheckI);

        }

        @Override
        public Component getTableCellEditorComponent(
                JTable table, Object value, boolean isSelected, int row, int column) {

            this.isChecked = (boolean) value;
            if (this.isChecked) {
                editButton.setIcon(uncheck);
            } else {
                editButton.setIcon(check);
            }
            System.out.println("Edit " + isChecked);

            return editButton;

        }

        @Override
        public Object getCellEditorValue() {

            System.out.println("giden " + isChecked);
            return this.isChecked;

        }

        @Override
        public void actionPerformed(ActionEvent e) {

            if (this.isChecked) {
                this.isChecked = false;
            } else {
                this.isChecked = true;
            }

            fireEditingStopped();

        }
    }

    public static void main(String[] args) throws IOException {

        Object[][] data = {
            {"1", "Allan", "Smith", false, true, false},
            {"2", "Jerry", "Tucker", true, false, true}
        };

        TableExample app = new TableExample(data);
        app.setVisible(true);

    }
}

Solution

  • After working on it for a while I realized that myTableModel's isCellEditable method does not working properly. I simply switched to DetafultTableModel overriding get column class.

        @Override
        public Class<?> getColumnClass(int c) {
            return getValueAt(0, c).getClass();
        }
    

    One problem using the DefaultTableModel is that the boolean class is rendered to regular JCheckBoxes automatically. So, instead of using a boolean class, I used Integer class to simulate true and false simply with 0 and 1.

    Here is the final program to simulate JCheckBox behavior in JTable by using a button and an image. This gives great flexibility for rendering different images and still get the JCheckBox behavior. So, Enjoy!

        public class TableExample extends JFrame {
    
        JScrollPane pane;
        JPanel panel;
        JTable table;
        Object[][] data;
        DefaultTableModel tm;
        Renderer buttonColumn;
    
        public TableExample(Object[][] data) throws IOException {
    
            String[] columns = {"Column#1", "Column#2", "Column#3", "Column#4", "Column#5", "Column#6"};
            this.data = data;
            this.setPreferredSize(new Dimension(700, 500));
            this.setMinimumSize(new Dimension(700, 500));
            this.setMaximumSize(new Dimension(700, 500));
            //tm = new DefaultTableModel(this.data, columns);
            DefaultTableModel tm = new DefaultTableModel(this.data, columns) {
                @Override
                public Class<?> getColumnClass(int c) {
    
                    return getValueAt(0, c).getClass();
                }
            };
    
            this.table = new JTable(tm);
            this.table.setRowHeight(50);
            table.setDefaultRenderer(Integer.class, new Renderer(this, table, 5));
            table.setDefaultEditor(Integer.class, new Editor(this, table, 5));
            this.pane = new JScrollPane(this.table);
            this.add(this.pane);
    
        }
    
        /**
         *
         * RENDERER
         */
        private class Renderer extends JButton implements TableCellRenderer {
    
            ImageIcon check;
            ImageIcon uncheck;
            int isChecked;
    
            public Renderer(JFrame frame, JTable table, int column) throws IOException {
    
                Image checkI = ImageIO.read(getClass().getResource("check.png"));
                Image unCheckI = ImageIO.read(getClass().getResource("uncheck.png"));
                this.setBorderPainted(false);
                check = new ImageIcon(checkI);
                uncheck = new ImageIcon(unCheckI);
    
            }
    
            @Override
            public Component getTableCellRendererComponent(
                    JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
                this.isChecked = (int) value;
                if (isChecked == 1) {
                    setIcon(check);
    
                } else {
                    setIcon(uncheck);
    
                }
    
                return this;
            }
        }
        /**
         *
         * EDITOR
         */
    
        private class Editor extends AbstractCellEditor implements TableCellEditor, ActionListener {
    
            private JButton editButton;
    
            ImageIcon check;
            ImageIcon uncheck;
            int isChecked;
    
            public Editor(JFrame frame, JTable table, int column) throws IOException {
    
                editButton = new JButton();
                editButton.addActionListener(this);
                editButton.setBorderPainted(false);
    
                Image checkI = ImageIO.read(getClass().getResource("check.png"));
                Image unCheckI = ImageIO.read(getClass().getResource("uncheck.png"));
                check = new ImageIcon(checkI);
                uncheck = new ImageIcon(unCheckI);
    
            }
    
            @Override
            public Component getTableCellEditorComponent(
                    JTable table, Object value, boolean isSelected, int row, int column) {
    
                this.isChecked = (int) value;
                if (this.isChecked == 1) {
                    editButton.setIcon(uncheck);
                    this.isChecked = 0;
                } else {
                    editButton.setIcon(check);
                    this.isChecked = 1;
                }
    
                return editButton;
    
            }
    
            @Override
            public Object getCellEditorValue() {
    
                return this.isChecked;
    
            }
    
            @Override
            public void actionPerformed(ActionEvent e) {
    
                fireEditingStopped();
    
            }
    
            public void updateValue() {
    
            }
        }
    
        public static void main(String[] args) throws IOException {
    
            Object[][] data = {
                {"1", "Allan", "Smith", false, false, 0},
                {"2", "Jerry", "Tucker", true, true, 1}
            };
    
            TableExample app = new TableExample(data);
            app.setVisible(true);
    
        }
    
    }