Adding a JButton inside a JTable cell rendering wrong after click
Apr 5, 2009 9:25 AM
Hello to all,
I know this question has been answered one way or the other on this forum but none of the answers really gave me what I need. Also I checked the documentation on the Swing tutorial and looks like I'm still missing something.
Basically my problem is that the JButton doesn't get rendered properly after I add it to a table that has a custom table model; I managed to reproduce the issue with a toy application so please bear with me while a publish only the related pieces (by the way I'm running this on OSX):
My custom table model holds a place for the JButton which is shared by all the rows (common functionality is there is not really storing anything):
import java.util.Date;
import java.util.List;
import java.util.Vector;
import javax.swing.JButton;
import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;
/**
* Table model for the GUI application
*
*/
finalpublicclass DataTableModel extends AbstractTableModel {
privatestaticfinallong serialVersionUID = 769042563295665904L;
/**
* Table description
*/
publicstaticenum TableHeader {
Date(Date.class),
Counter(Integer.class),
Name(String.class),
Details(JButton.class);
Class<?> currClass;
private TableHeader(Class <?>currClass) {
this.currClass = currClass;
}
public Class <?>getCurrClass() {
return currClass;
}
}
private List<DataElement> data;
private JButton button;
/**
* Parametric constructor
* @param tempList Data list
* @param aAction action used to display details
*/
public DataTableModel(final Vector<DataElement> tempList, final JButton aButton) {
if (tempList == null) {
thrownew IllegalArgumentException("Data vector cannot be empty");
}
data = tempList;
button = aButton;
}
@Override
public Class<?> getColumnClass(int columnIndex) {
return TableHeader.values()[columnIndex].getCurrClass();
}
@Override
publicint getRowCount() {
return data.size();
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
DataElement elem = data.get(rowIndex);
switch (TableHeader.values()[columnIndex]) {
case Date:
return elem.getDate();
case Counter:
return elem.getCounter();
case Name:
return elem.getName();
case Details:
return button;
default:
returnnull;
}
}
@Override
publicint getColumnCount() {
return TableHeader.values().length;
}
@Override
public String getColumnName(int column) {
return TableHeader.values()[column].name();
}
@Override
publicboolean isCellEditable(int row, int column) {
if (column == TableHeader.Details.ordinal()) returntrue; // Only the JButton cell is editable
returnfalse;
}
}
So far so good. Then I define a custon cell renderer for any element instance of JButton:
import java.awt.Component;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.swing.JButton;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;
import constant DataTableModel.TableHeader;
/**
* Custom cell renderer for the application JTable
*
*/
class GuiCellRenderer extends DefaultTableCellRenderer {
privatestaticfinallong serialVersionUID = 211718743340765799L;
private SimpleDateFormat dForm;
public GuiCellRenderer() {
super();
dForm = new SimpleDateFormat("hh:mm:ss a");
}
@Override
public Component getTableCellRendererComponent(JTable table, Object val, boolean select, boolean focus, int row, int col) {
super.getTableCellRendererComponent(table, val, select, focus, row, col); // Make sure gets called
if (val == null) {
returnthis;
}
if (val instanceof JButton) {
JButton butn = (JButton) val;
String name = (String) table.getValueAt(row, table.convertColumnIndexToView(TableHeader.Name.ordinal()));
butn.setText(name);
butn.setActionCommand(name);
return butn;
} else {
switch (DataTableModel.TableHeader.values()[table.convertColumnIndexToModel(col)]) {
case Date:
setText(dForm.format((Date)val));
break;
case Counter:
Integer iVal = (Integer) val;
setText(iVal.toString());
break;
case Name:
setText((String) val);
break;
default:
setText(val.toString());
break;
}
returnthis;
}
}
} //end class
The button gets rendered properly, but when you click on it it doesn't react so you either forward events or you create a custom cell editor and assign it to the JButton class types on the table. I first tried adding a MouseEvent listener and then converting the event using SwingUtilities.convertMouseEvent on my button but the click effect didn't worked out so I ended using the custom editor below:
Then finally I add them on my table code as follows:
ShowDetailsAction detailsAct = new ShowDetailsAction();
JButton detailsBtn = new JButton();
detailsBtn.setAction(detailsAct);
model = new DataTableModel(dataModel, detailsBtn);
table = new JTable();
table.setModel(model);
/**
* Custom cell rendering
*/
TableCellRenderer render = new GuiCellRenderer();
table.setDefaultRenderer(Date.class, render);
table.setDefaultRenderer(Double.class, render);
table.setDefaultRenderer(Long.class, render);
table.setDefaultRenderer(Integer.class, render);
table.setDefaultRenderer(String.class, render);
table.setDefaultRenderer(JButton.class, render);
table.setDefaultEditor(JButton.class, new GuiCellEditor(detailsBtn));
So here are the issues:
1) The click works and my action gets called. But the cell that holds the button goes completely blank with is annoying
2) The selected color of the cell works on all the other cells but not on the one that has the JButton. The selected row looks weird.
Sorry for the long post, I tried to be as specific as possible. I wonder if someone else already went through this and is willing to share some advise on how to implement this correctly.
Re: Adding a JButton inside a JTable cell rendering wrong after click
Apr 5, 2009 6:28 PM
(reply 2
of 15) (In reply to
#1 )
Hello camickr,
Thanks for your time. Yes, I saw your post before but none of your suggestion help with the rendering issue. If you try my code (I can post more or even send you the whole toy application) you will see that what I do is not very different to what you describe on your post.
The example showed on the Swing tutorial shows a similar glitch, except once you set the value the cell gets rendered properly.
Re: Adding a JButton inside a JTable cell rendering wrong after click
Apr 6, 2009 5:25 AM
(reply 6
of 15) (In reply to
#5 )
Helpful
The problem seems to be that you are trying to have the button be the value for the model,
rather than simply having the button render the value in the model. Basically, the check for
val instanceof JButton is false.
If you look at the link camickr provided, what he's doing there is having the button text set
to the value in the table and returning that text as the value. You have a mash of trying to
inject the button in the model and that's what is causing the problem.
Re: Adding a JButton inside a JTable cell rendering wrong after click
Apr 6, 2009 5:42 AM
(reply 7
of 15) (In reply to
#6 )
You have a mash of trying to inject the button in the model and that's what is causing the problem.
Yes, and I want to point to the fact that you are using just one instance of JButton to act as model, renderer and editor simultaneously for all the rows !!
I suggest you relook at camickr's code which is short and easy to understand.
@camickr:
Thanks for the link to the code, that really helped me....
Re: Adding a JButton inside a JTable cell rendering wrong after click
Apr 6, 2009 8:36 PM
(reply 8
of 15) (In reply to
#7 )
T.B.M, thanks for your time and help so far.
camickr is using one button. There is no problem with that, as the renderer and editor get called exactly once per cell, right?
That is exactly what camickr wrote on the example code:
// Create button column
ButtonColumn buttonColumn = new ButtonColumn(table, 4); // Only one ButtonColum renderer / editor, created on the constructor of the main class
And then the renderer /editor uses one single button as well (check the constructor of ButtonColum):
JButton editButton;
String text;
public ButtonColumn(JTable table, int column)
{
super();
this.table = table;
renderButton = new JButton();
editButton = new JButton();
editButton.setFocusPainted( false );
editButton.addActionListener( this );
Finally, his code (http://forums.sun.com/thread.jspa?threadID=680674) also shows the same issue; I made a small video and posted it on YouTube that shows the problem on OSX (please excuse the background sounds, I think the TV was playing Medium while I was recording :)):
http://www.youtube.com/watch?v=xucEZSl5coc
And my code (with the same issue).
http://www.youtube.com/watch?v=UmDwF2D3avc
I admit than adding the Button to the model was a bad design idea, since then I fixed the model and created a separated renderer (I'll post the new code shortly).
Re: Adding a JButton inside a JTable cell rendering wrong after click
Apr 6, 2009 8:39 PM
(reply 9
of 15) (In reply to
#6 )
JayDS, thanks for trying to answer my question,
Yes, adding the Button to the model was a bad idea and I removed it from my test code. Still, I managed to reproduce the bug on camicker code as you can see here:
Re: Adding a JButton inside a JTable cell rendering wrong after click
Apr 6, 2009 8:40 PM
(reply 10
of 15) (In reply to
#1 )
Camickr, thanks for sharing your code!. Yes, I looked at your example early on but I think it has the same problem (please check the video links I posted below).
Do you see this issue on Windows or Linux as well? (I haven't got the time to try it on a different platform).
Re: Adding a JButton inside a JTable cell rendering wrong after click
Apr 6, 2009 8:50 PM
(reply 12
of 15) (In reply to
#10 )
Do you see this issue on Windows
I use Windows and no I don't see the issue. I wouldn't post or recommend the code if I did ;-)
I don't know anything about OSX. A simple renderer should not cause a problem. The complicated part of the code was trying to get it to respond to a mouse event. I see no reason why the button should turn white just because the row is selected.
Did you try to debug the code at all? Does the logic go through the "isSelected" part of the renderer. Do the two Colors happen to be White?
Re: Adding a JButton inside a JTable cell rendering wrong after click
Apr 7, 2009 3:15 PM
(reply 13
of 15) (In reply to
#12 )
Looks like is an OSX specific issue; I ran my code on Linux (Redhat Enteprise Server 4) and it works. I will run it through Eclipse on the MAC and I will post my findings here.
Thanks for your suggestions, will try them while I run this through the debugger.