17

I have a JTable with a custom cell renderer. The cell is a JPanel that contains a JTextField and a JButton. The JTextField contains an integer, and when the user clicks on the JButton the integer should be increased.

The problem is that the JButton can't be clicked when I have it in a JTable cell. How can I make it click-able?

enter image description here

Here is my test code:

public class ActiveTable extends JFrame {

    public ActiveTable() {
        RecordModel model = new RecordModel();
        model.addRecord(new Record());
        JTable table = new JTable(model);
        EditorAndRenderer editorAndRenderer = new EditorAndRenderer();
        table.setDefaultRenderer(Object.class, editorAndRenderer);
        table.setDefaultEditor(Object.class, editorAndRenderer);
        table.setRowHeight(38);

        add(new JScrollPane(table));
        setPreferredSize(new Dimension(600, 400));
        pack();
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setTitle("Active Table");
        setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new ActiveTable();
            }
        });
    }

    class RecordModel extends AbstractTableModel {

        private final List<Record> records = new ArrayList<Record>();

        @Override
        public int getColumnCount() {
            return 1;
        }

        @Override
        public int getRowCount() {
            return records.size();
        }

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            return records.get(rowIndex);
        }

        public void addRecord(Record r) {
            records.add(r);
            fireTableRowsInserted(records.size()-1, records.size()-1);
        }

    }

    class Record {
        private int count = 1;
        public int getCount() { return count; }
        public void increase() { count = count + 1; }
    }

    class CellPanel extends JPanel {
        private Record record;
        private final JTextField field = new JTextField(8);
        private final Action increaseAction = new AbstractAction("+") {
            public void actionPerformed(ActionEvent e) {
                record.increase();
                field.setText(record.getCount()+"");
                JTable table = (JTable) SwingUtilities.getAncestorOfClass(JTable.class, (Component) e.getSource());
                table.getCellEditor().stopCellEditing();
            }
        };
        private final JButton button = new JButton(increaseAction);

        public CellPanel() {
            add(field);
            add(button);
        }

        public void setRecord(Record r) {
            record = r;
            field.setText(record.getCount()+"");
        }

        public Record getRecord() {
            return record;
        }
    }

    class EditorAndRenderer extends AbstractCellEditor implements TableCellEditor, TableCellRenderer {

        private final CellPanel renderer = new CellPanel();
        private final CellPanel editor = new CellPanel();

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value,
                boolean isSelected, boolean hasFocus, int row, int column) {
            renderer.setRecord((Record) value);
            return renderer;
        }

        @Override
        public Component getTableCellEditorComponent(JTable table, Object value,
                boolean isSelected, int row, int column) {
            editor.setRecord((Record) value);
            return editor;
        }

        @Override
        public Object getCellEditorValue() {
            return editor.getRecord();
        }

        @Override
        public boolean isCellEditable(EventObject ev) {
            return true;
        }

        @Override
        public boolean shouldSelectCell(EventObject ev) {
            return false;
        }
    }
}
Jonas
  • 121,568
  • 97
  • 310
  • 388
  • can you still needed JPanel (nested Whatever JComponents) inside JTable's cell ??, – mKorbel Nov 21 '11 at 23:01
  • @mKorbel: Yes, I need the cell to be a multi-line JPanel with multiple buttons. – Jonas Nov 21 '11 at 23:04
  • wait sec. I posted this code here maybe twice or more (somewhere to the JList rellated q&a), if I can't find that, then I'll start search for that in local PC .... – mKorbel Nov 21 '11 at 23:07
  • let's this idea help you http://stackoverflow.com/questions/6355544/how-to-implement-dynamic-gui-in-swing/6355910#6355910 – mKorbel Nov 21 '11 at 23:14
  • that same as you put JPanel wiht own logic to the JFrame, JTable is only the Container nothing else :-), that nothing with TableCellEditor or TableCelRenderer, please take example @trashgod, his majesty changing value from one TableCell to the another, but this not your issue, isn't – mKorbel Nov 22 '11 at 22:35
  • @mKorbel: The code you linked to solved finally my problem. You should post it or a link to is as an answer. Thanks. – Jonas Nov 22 '11 at 22:37
  • @mKorbel: The big difference between my code in the question and your code was that you use `DefaultTableModel` and I use `AbstractTableModel`. When using `AbstractTableModel` I have to implement `isCellEditable()` otherwise it doesn't work. – Jonas Nov 22 '11 at 22:39
  • ??? you can override isCellEditable() same for DefalutXxxXxx as an AbstractXxxXxx ???, thanks for offer, no accepted, please answer your question by yourself, btw my +1 – mKorbel Nov 22 '11 at 22:53
  • you need an _cellEditor_ instead of an _cellRenderer_ - see: http://download.oracle.com/javase/tutorial/uiswing/components/table.html – kleopatra Apr 05 '11 at 17:37
  • [Table Button Column](http://tips4java.wordpress.com/2009/07/12/table-button-column/) – camickr Apr 05 '11 at 18:26

3 Answers3

9

Here's one way to use ButtonColumn.

public class TableTest extends JFrame {

    public TableTest() {
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JTable table = new JTable(new TestModel());
        table.getColumnModel().getColumn(1).setPreferredWidth(3);
        table.getColumnModel().getColumn(2).setPreferredWidth(3);
        this.add(new JScrollPane(table));
        Action increase = new AbstractAction("+") {

            @Override
            public void actionPerformed(ActionEvent e) {
                JTable table = (JTable) e.getSource();
                int row = Integer.valueOf(e.getActionCommand());
                TestModel model = (TestModel) table.getModel();
                model.increment(row, 0);
            }
        };
        ButtonColumn inc = new ButtonColumn(table, increase, 1);
        Action decrease = new AbstractAction("-") {

            @Override
            public void actionPerformed(ActionEvent e) {
                JTable table = (JTable) e.getSource();
                int row = Integer.valueOf(e.getActionCommand());
                TestModel model = (TestModel) table.getModel();
                model.decrement(row, 0);
            }
        };
        ButtonColumn dec = new ButtonColumn(table, decrease, 2);
        pack();
    }

    public static void main(String[] args) {
        new TableTest().setVisible(true);
    }
}

class TestModel extends AbstractTableModel {

    List<TestRecord> records = new LinkedList<TestRecord>();

    private static class TestRecord {

        private int val = 0;
    }

    public void increment(int row, int col) {
        records.get(row).val++;
        fireTableCellUpdated(row, 0);
    }

    public void decrement(int row, int col) {
        records.get(row).val--;
        fireTableCellUpdated(row, 0);
    }

    public TestModel() {
        records.add(new TestRecord());
        records.add(new TestRecord());
    }

    @Override
    public Class<?> getColumnClass(int col) {
        if (col == 0) {
            return Integer.class;
        } else {
            return ButtonColumn.class;
        }
    }

    @Override
    public boolean isCellEditable(int row, int col) {
        return true;
    }

    @Override
    public int getColumnCount() {
        return 3;
    }

    @Override
    public int getRowCount() {
        return records.size();
    }

    @Override
    public Object getValueAt(int row, int col) {
        if (col == 0) {
            return records.get(row).val;
        } else if (col == 1) {
            return "+";
        } else {
            return "-";
        }
    }
}
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
7

I based my last example on the code provided by mKrobels answer to How to implement dynamic GUI in swing

The main difference between his and my example in the question is that he use DefaultTableModel and I use AbstractTableModel. His example does work, but not mine.

The solution I found was that I had to implement isCellEditable() in the TableModel, so with this method added, my example works:

@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
    return true;
}
Community
  • 1
  • 1
Jonas
  • 121,568
  • 97
  • 310
  • 388
3

The renderer is just a display for painting the cells. You need an editor component that allows you to make changes.

Take a look at:

http://download.oracle.com/javase/6/docs/api/javax/swing/table/TableCellEditor.html

jzd
  • 23,473
  • 9
  • 54
  • 76
  • I don't understand how I can use a `TableCellEditor` since my cell contains multiple components and only one of them, the `JButton` should be clickable. Any ideas? – Jonas Apr 05 '11 at 17:52
  • 1
    @Jonas, the getTableCellRendererComponent() can return a panel that has buttons, text, etc on it. – jzd Apr 05 '11 at 17:55