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:
Of course you would probably want to add a confirm dialog before actually deleting the row. Anyway, hopefully you will find the ButtonColumn class easy to use.Action delete = new AbstractAction()
{
public void actionPerformed(ActionEvent e)
{
JTable table = (JTable)e.getSource();
int modelRow = Integer.valueOf( e.getActionCommand() );
((DefaultTableModel)table.getModel()).removeRow(modelRow);
}
};
ButtonColumn buttonColumn = new ButtonColumn(table, delete, 2);
buttonColumn.setMnemonic(KeyEvent.VK_D);
Try The Demo
– Using Java™ Web Start (JRE 6 required)
sarcan said
Hi Rob,
always a pleasure reading your blog. There seems to be a glitch with this particular example, though. The actionCommand property generated by ButtonColumn contains the ‘view row’, not the ‘model row’. If you install a RowSorter on your table and change the row order you’ll end up deleting the wrong one, so the line in actionPerformed should probably read
int row = table.convertRowIndexToModel(Integer.valueOf( e.getActionCommand()));
best regards,
sarcan
Rob Camick said
Thanks for pointing that out. I’ve updated the example code.
I also updated the source code. I made some changes earlier this morning which I forgot to upload.
Rob Camick said
After thinking about it for a while I decided to pass the “model” row in the Action event.
Eitan said
Hi Rob,
Like Sarcan said “it is a pleasure”.
If I am not mistaken you do not need
the
public void mouseMoved(MouseEvent e) {}
Rob Camick said
Boy, I was sleeping while doing this one. Thanks, the line has been removed.
Its nice to know people actually take the time to read and understand the code.
André Uhres said
Well done. Just one minor point: you use the terms “TableButton”, “TableColumn” and “ButtonColumn” to refer to the same class, which is a little bit confusing at first reading. I suppose you changed the class name and forgot to update the text.
Rob Camick said
Wow, I was really out to lunch on that, thanks.
Anonymous said
It’s not Madge, it’s Marge ;)
Rob Camick said
Oops, not sure what I was thinking? Hope the Simpsons aren’t too mad with me :)
Peter Hon said
Hi rob,
I am new to java programming and I have read the ButtonColumn.java source code. But I have no idea
on how to use the ButtonColumn.java in my code. Can u suggest any complete example code that shows how
to use ButtonColumn.java. i.e. A Class with main() using the ButtonColumn.java Code. Thanks.
Rob Camick said
This is not a forum. If you don’t know how to create a JTable with data then post questions in a forum. Once you have a table created you create an Action. I posted the code for an example Action to delete a row from the table. I then gave you the 1 line of code needed to convert the text in a column to a button (the second line is optional). So all you have to do is copy/paste the code I gave you in this posting.
Paul Humphrey said
Excellent, Just what I needed
Thanks!
Rob Camick said
Great a comment that wasn’t about a silly mistake I made :) Thanks.
George Moralis said
Using it with JXTable doesn’t seem to work. The event is never fired :(
Rob Camick said
Sorry, the code posted here is designed to work with the base JDK classes. I’m not sure what the problem would be with JXTable.
Michael B. said
Hi, thanks for this neat article + code.
I’m using ButtonColumn right now, however I don’t think I changed the button text right. At line 142, I changed the value inside renderButton.setText() to the text value that I want.
I got the outcome that I wanted but you said the text would be stored in the TableModel.
Sorry for asking such a question and thanks in advance!
Rob Camick said
The text that you want to appear on the button is added to the TableModel the same way you add any other String for any other table column. There is no need for you change the code in the ButtonColumn class.
Michael B. said
Wow quick reply!
Yeah, that was noob of me to do so.
Thanks again sir
Sanya said
Hey Rob,
I tried using your code. I need buttons in the 2nd and 3rd column of my table. I passed 2 as a parameter for column in ButtonColumn. However an action is performed when i click on any column in that particular row. I need an action to be performed only when i click on the 2nd column. Could you help?
Regards,
Sanya
Rob Camick said
Sorry I don’t see the behaviour you describe. I updated this posting with a Webstart demo that shows the behaviour when you add the ButtonColumn to the 2nd column. If you see different behaviour then you can create a simple demo and email the SSCCE using the “Contact Us” page.
Sanya said
Hey Rob,
I tried mailing you by copy pasting the code in the message field. Please let me know in case of any problem. Thanks for your time.
Regards,
Sanya
Anabel said
Hi thnks for you code. It help me a lot.
Right now Im trying to make buttons to open pdf files, Is there a way to create an action for each button? And giving them the path of the pdf?
I hope you can help me thanks!
Rob Camick said
One way is to have a separate column in the table with the PDF path. Then the Action can just get the path from the column. Or if you don’t want a separate column then you can add a custom object to the model. The object would contain the text to display on the button as well as the path of the PDF file. Then you would need to override the toString() method of the Object to return the text. The Action could then access the path.
Anabel said
Thanks for the fast reply.
I dont know what im doing wrong….
(Rest of comment deleted…)
Rob Camick said
I can’t tell from the code you posted what you are doing. Maybe if you email a proper SSCCE through the “Contact Us” page I can take a quick look.
Anonymous said
Nice article sir.
Thanks.
Rob Camick said
Thanks for the feedback.
Umer said
Sir I got a problem here…
The problem is when i put a cell to editable false then this api is not working ….
Rob Camick said
That is not a problem. That is the way an editor works. If you want to be able to click on a button the cell must be editable.
Anonymous said
Nice post & nice comments
Javid said
It was me!
daskhatri said
Dear tell how do i use your code in my program , I developed a JTable that is filling data from database, now I need to add Jbutton in my table with each record… Button is not added yet. How proceed further , Do i have to just access your class in my table program? or first i have to add a JButton in table then access your code?
please give me just five mints
Rob Camick said
Forget about getting data from the database. First you need to understand how to use the class. So create a simple example with 3 columns like I display from the image. Once you get that working then move on to getting data dynamically from a database.
You add data to the TableModel just like you do for any other column of data. Normally the data is just displayed as text. In the image from the blog there are 3 columns of data, firstName, lastName and an untitled column. In all 3 cases you add a String to the TableModel. For the first row you add “Homer”, “Simpson”, “delete Homer”. Then you add a custom renderer and editor to the 3rd column. Again the blog show how to do this and even includes code for a simple Action.
daskhatri said
Thanks Rob.. THank you so much.. :) i did it… now I have to delete it dynamically , so i think i have to use DefaultTableModel..
BR
Das Khatri
Sindh.
Rob Camick said
That is correct you use the DefaultTableModel. I gave example code in the blog.
parvathi said
Hi Rob,
Nice article..It would be great if you could explain why do we need to add mouse listener to the editor button, when we are already adding action listener.
Rob Camick said
I explained that using comments in the code. I didn’t think it was necessary to repeat in the blog since the blog iis for an overview of the functionality, not a detailed look at each line of code.
Protocol-X (@Protocol_X) said
This code is exactly what I have been looking for, but I am having 2 issues. the first issue is the code only works if i remove the override for the following 2 lines. Also no actions are being done when i try to set the action. Any help would be appreciated.
public Component getTableCellEditorComponent(
public Object getCellEditorValue(){
Rob Camick said
If you comment out those lines you defeat the purpose of using the custom editor. The code should work as is if you use the example code I provided in the blog. If you have problems using the example code then create an SSCCE and send me the code using the Contact Us page.
Gerard said
Worked great for me …. except: On Mac OS X the “Aqua” look and feel doesn’t respect JButton colors, so clicking on the Button causes it to go all white. I modified the code in getTableCellRendererComponent as follows as a work around:
if (isSelected) { LookAndFeel lookAndFeel = UIManager.getLookAndFeel(); if (!lookAndFeel.getID().equals("Aqua")) { renderButton.setForeground(table.getSelectionForeground()); renderButton.setBackground(table.getSelectionBackground()); } }Rob Camick said
Thanks for the feedback. It should be helpful for Mac users.
Faisal said
I am new to JAVA. I just wanted to know how to run this piece of code. Does it work as a sub routine in a actionlistener.
Jimmy said
Dear sir
I found your sourse code very helpful.However, i have problems
My table have the 9th colum is button column and 8th column is interger.
How can add header sort in the 8th column, and repaint table, still keep button in the 9th column.
As far as i know, to sort column, table need to extend AbstractTable Model.
I apply:
ButtonColumn buttonColumn = new ButtonColumn(table, myAction, 9). My 9th column goes wrong.
Help me , please
I’m very appreciated
Rob Camick said
The ButtonColumn class is a custom renderer and editor which should not affect sorting so I’m not sure what your problem might be. However, if you have 9 columns then you should should be specifiy “8″, when you create the ButtonColumn class since JTable uses 0 as the offset to reference columns. To add sorting to the table I just use table.setAutoCreateRowSorter( true ) and it works for me.
whoishotter said
Great code – thanks so much for sharing! I’m using it an it works great, so all I could find was a comment typo: Optionally spelled with only one ‘l’ at line 14.
I’m only trying to contribute to your great work by helping edit for you. :) Thanks again!
-Travis
Rob Camick said
Only one typo? I’m getting better :) Thanks.
Emrah İşlek said
Could you send Demo’s source code?
Rob Camick said
The demo code shows mostly how to create a GUI. The blog shows the important couple of lines of code to use this class. You need a couple of lines of code to create an simple Action. Then you need a single line of code to create the class and use this Action. This class can be added to any column that renders text in the column.
Oliver Henning said
Dear Rob,
thanks for your code. The button shows in column and gets focus when clicked, but no event is fired.
public class ScriptTable extends JTable { ... final Action split = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { System.out.println("Clicked!"); // not fired } }; buttonColumn = new ButtonColumn(this, split, 3);Maybe you have an idea what is wrong
Thanks and greetings,
Oliver
Rob Camick said
Is the column editable? I believe it needs to be editable in order for the mouse event to be passed to the editor. Also just try a simple test by just create a normal JTable, instead of extending JTable. Maybe something in your custom table is causing a problem.
Sterling said
Thanks very much for this code. I am using it in an application where I have extended JTable to give me control over column widths. I am also using a unicode down facing triangle (basically I am going to emulate a menu opening from a cell) Your code worked basically dropping it in except that when it was initially rendered the text icon was off center to the right. when clicked it went centered and then when another button was clicked it went offset again. What I ended up doing was simply replacing the line
originalBorder = editButton.getBorder();
in the constructor with
originalBorder = new LineBorder(new Color(122, 138, 153), 1);
and this centered the text. I have no idea why and I am not saying this is a fault of your code just thought you may be interested in that use case.
Gal said
I’ve been working on my school project and needed it. I was gonna try and make such class with examples i found on the internet but now i can use this awesome class. You saved me an hour(probably even more) at class.
Thanks :D
t0th said
Hi,
i have Exception in thread “AWT-EventQueue-0″ java.lang.ArrayIndexOutOfBoundsException: 1 >= 0
in line fireEditingStopped();
can you help me?
thanks
Jay said
Awesome Awesome Awesome, thanks a bunch very easy to use. I am happy you did not provide the ‘main’ class. Could not agree more with you, the more you dig/practice/read the more you learn.
resahaw27 said
thank you so much.. this is a very big help in my school project.. more power to you!! :D
Rob Camick said
Good luck with your project.
resa said
i dont know if you can help me but how do i add an actionListener to the button.. i mean what button should i add the actionListener since there 2 jbuttons declared in the code.. i need to open a new jframe when i click the button but i dont know how to do that.. i hope you can help me with that.. thank you so much. :))
Rob Camick said
You don’t add an ActionListener to either of the JButtons defined in the ButtonColumn class. You add the ActionListener to the ButtonColumn class when you create an instance of the class. I gave a simple example. If you want to open a frame then you need to change the code in my example to create and show a frame.
teofil said
first of all, thank you for all your work!
secondly, i want to use this in multiple columns, specifically in the two last columns of the table
but when i run the project only the last column shows the button, the other one nothing!
do you see any reason for this to happen?
well maybe it has to do with the fact that the editor and renderer is in one class!
i am looking at this now and i will post if it makes any difference
Rob Camick said
It works fine for me. You need to create two ButonColumn classes and Actions:
teofil said
sorry about the fuss but you’re right
it works the way i want, i just didn’t made the right adjustments to my code for adding it
btw, i am thinking of using in another column a combobox instead of a button, does this code gives the way it can be done?
a combobox to use for a field that has multiple values
i haven’t searched anything yet, just asking
and again thanks for all your work, very helpful!
Anonymous said
Thanx so much, it’s exactly what I needed!
Jim said
this is a lifesaver! thanx a lot. i would have taken months to figure this out on my own!
Anonymous said
Hi Rob,
Thank you for sharing. I have been trying for some time now to make buttons either invisible or disabled, so that I couldn’t click on a button depending on other variable in the same row.
Could you point me in the right direction of how should I approach this problem?
Thank you,
Borys
Rob Camick said
To prevent a cell from being edited you would override the isCellEditable(…) method of your table model. If you wanted to render the button as disabled, then you would also need to add code in the getTableCellRendererComponent(…) to make the button appear disabled.
Anonymous said
thank you Rob :) this article was very helpful for my project…i want ask a question…how to add an image in the button..
Rob Camick said
Just add an Icon to the TableModel, instead of text.
Isuru said
Hi! Thanks for this class. Just what I was looking for. I have a clarification. To set a text for the button, you have to fill up the renderButton.setText( “” ); in the getTableCellRendererComponent() method, am I right? Or is there any other property or something to accomplish this?
Rob Camick said
A renderer gets the data from the TableModel.
Isuru said
Wait! I think I got it. I added “Reserve” to the end of model.addRow(new Object[] {rs.getString(“title”), rs.getString(“author”), rs.getString(“isbn”), rs.getString(“publisher”), “Reserve”}); and everything’s good now. I must have somehow missed the line ‘You store text (or an Icon) in the TableModel’ in the article. Thanks.