Table Button Column
Posted by Rob Camick on July 12, 2009
A JTable is used to disply rows of data. There may be times when you want to do some processing on a row of data. Maybe you want to display a popup form with more details or maybe you simply want to delete a row. In these cases it may be desireable to add a button to one of the table columns so you can invoke this processing. The problem is that JTable doesn’t support a button renderer or editor.
After playing around a bit, the best solution I could come up with was to create a class that is both a renderer and an editor. The result is the ButtonColumn class. You store text (or an Icon) in the TableModel (the same way you store data for any other column) and the ButtonColumn will render the text (or the Icon) on a button. When you create the class you specify an Action that is to be invoked when the button is pressed. You also specify the column that is used to contain the button so that the renderer and editor can be automatically installed on the appropriate TableColumn of the TableColumnModel.
A table using a ButtonColumn as a renderer would look like this:
The button can be invoked by using the mouse or the keyboard. Using the Metal or Windows LAF you would use the space bar to invoke the button (I’m not sure about other LAF’s). You can also set a mnemonic to invoke the button. You can set the mnemonic using the setMnemonic() method. In the example above I used KeyEvent.VK_D as the mnemonic.
Notice how the border on the first button is a different color? This indicates that the button has focus and can be invoked by using the keyboard as mentioned above. I decided to use this approach since I couldn’t figure out how to invoke the normal “focus painted” code on a rendered button. You can change the Border using the setFocusBorder() method.
The key to the ButtonColumn class is the usage of an Action to perform the custom processing. When the button is clicked an ActionEvent is created and the actionPerformed() method of the Action is invoked. The table will be the source of the event. Also, the action command will contain the “model” row number of the clicked button. The reason for passing a row number is that multiple rows could be selected so you can’t depend on the table.getSelectedRow() method to return the appropriate row number. The reason for passing the model row is so you can access the TableModel using the same index whether the table uses a RowSorter or not.
The code to delete a row from a table using the DefaultTableModel would be:
Action delete = new AbstractAction()
public void actionPerformed(ActionEvent e)
JTable table = (JTable)e.getSource();
int modelRow = Integer.valueOf( e.getActionCommand() );
ButtonColumn buttonColumn = new ButtonColumn(table, delete, 2);