wood burning stoves 2.0*
The moose likes Swing / AWT / SWT and the fly likes Problem with TableCellRenderer Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Java » Swing / AWT / SWT
Bookmark "Problem with TableCellRenderer" Watch "Problem with TableCellRenderer" New topic
Author

Problem with TableCellRenderer

ram shyam
Ranch Hand

Joined: May 04, 2007
Posts: 77
Hi all,

I have a customised tableCellRenderer to display a button in one of the columns in the table. I find that this renderer class is being called continously when the corresponding screen/table is invoked. Using the debug mode in Eclipse IDE and setting a breakpoint in the renderer class, I find that the debugger hits the breakpoint continously. And I find CPU usage to the maximum whenever this table is invoked.
Is this the right behavior of the table cell renderer as I have not seen such performance issues in other cases.

Please clarify.

Thanks in advance!!
Brian Cole
Author
Ranch Hand

Joined: Sep 20, 2005
Posts: 862
Originally posted by ram shyam:
I have a customised tableCellRenderer to display a button in one of the columns in the table. I find that this renderer class is being called continously when the corresponding screen/table is invoked. Using the debug mode in Eclipse IDE and setting a breakpoint in the renderer class, I find that the debugger hits the breakpoint continously. And I find CPU usage to the maximum whenever this table is invoked.
Is this the right behavior of the table cell renderer as I have not seen such performance issues in other cases.

Please clarify.


Certainly not. Either there's a problem with your custom cell renderer
or there's a problem elsewhere in your code. Why don't you show us your
renderer so we can take a look?


bitguru blog
ram shyam
Ranch Hand

Joined: May 04, 2007
Posts: 77
Hi,

Many thanks for your reply!

Here is the code for the renderer-
=========================================================

class ButtonRenderer extends DefaultTableCellRenderer implements TableCellRenderer {


public ButtonRenderer() {
setOpaque(true);
}

public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column)
{
String newDate = null;
String newValue = null;
String oldDate = null;
Boolean newReset = new Boolean(false);
if (isSelected) {
setForeground(table.getSelectionForeground());
setBackground(table.getSelectionBackground());
} else{
setForeground(table.getForeground());
setBackground(UIManager.getColor("Button.background"));
}

TableModel model = table.getModel();
try{
if(model.getValueAt(row, 2) != null && !(model.getValueAt(row, 2).toString().trim().equals("")))
{
if (model.getValueAt(row, 2) instanceof Date)
newDate = DateUtils.getStringFromDate((Date) (model.getValueAt(row, 2)));
else
newDate = model.getValueAt(row, 2).toString();
}
}
catch(Exception e)
{
e.printStackTrace();
}
newValue = model.getValueAt(row, 1).toString();
Vector oldData = new Vector();

if(table.getName().equals(DEDICATED_ACCOUNTS_SECURITY_KEY))
{
if (Session.getCurrentController().getCurrentModel() != null)
{
oldData = (Vector) Session.getCurrentController().getCurrentModel().getOriginalValue(AccountAdminModel.DEDICATED_ACCOUNTS);
model.setValueAt(undoButton, row, 9);
if (oldData.elementAt(row) != null)
oldDate = DateUtils.getStringFromDate((Date) (((Vector)oldData.elementAt(row)).elementAt(2)));

if (( newValue != null && !newValue.equals(((Vector)oldData.elementAt(row)).elementAt(1).toString()))
|| (newDate != null && !newDate.equals(oldDate)) )
return undoButton;

}

}
else
{
newReset = (Boolean)model.getValueAt(row, 5);
if (Session.getCurrentController().getCurrentModel() != null)
{
oldData = (Vector) Session.getCurrentController().getCurrentModel().getOriginalValue(AccountAdminModel.ACCUMULATORS);
model.setValueAt(undoButton, row, 6);
if (oldData.elementAt(row) != null)
oldDate = DateUtils.getStringFromDate((Date) (((Vector)oldData.elementAt(row)).elementAt(2)));
if ((newValue != null && !newValue.equals(((Vector)oldData.elementAt(row)).elementAt(1).toString()))
|| (newDate != null && !newDate.equals(oldDate))
|| !newReset.equals(((Vector)oldData.elementAt(row)).elementAt(5)))
return undoButton;
}

}

return null;
}

}


Please clarify!

Thanks in advance!!!
Brian Cole
Author
Ranch Hand

Joined: Sep 20, 2005
Posts: 862
Originally posted by ram shyam:
Here is the code for the renderer-


ok, there's some weird stuff in there.

First, why does this extend from DefaultTableCellRenderer?
This has nothing to do with your problem, but if you don't plan
to use the inherited JLabel stuff that DefaultTableCellRenderer
does, what's the point? Might as well just extend Object and
implement TableCellRenderer.

Second, what's the deal with calling model.setValueAt(undoButton, row, n)
from within getTableCellRendererComponent()? This could easily be
causing the infinite-loopish behavior that's chewing up all your CPU
cycles. Also, I can't tell from the code what type undoButton is,
but if it's a JButton it's a strange thing to put in a table model.

Third, you have logic in your renderer that tests some conditions and
returns either undoButton or null. This probably doesn't have anything
to do with your problem, but IMHO this isn't how things should be done.
That logic should be in your table model, which can return some special
value [of any Object type you like, including an Enumeration or something
like that] in getValueAt(). Then your cell renderer can look at that value
(that's what the value parameter is for in getTableCellRendererComponent())
and quickly decide whether or not to return null.

btw, this is the second post I've seen today which has a button-ish
table cell renderer which ignores its value parameter. Weird.

[edit: btw, you should put your code in CODE tags on this forum so
you don't lose your indentation.]
[ April 24, 2008: Message edited by: Brian Cole ]
ram shyam
Ranch Hand

Joined: May 04, 2007
Posts: 77
Hi,

Many thanks for your reply!

I found that the issue is because of point 2 as you mentioned and I can find that there is no performance issue now. But, as you have mentioned, I am doing some condition checking based on which I am rendering the JButton UndoButton. But after removing the model.setValueAt() now, and doing some changes on the table, I couldnt get the undoButton displayed unless I click some other row. The button is not getting reflected immediately after making any changes although I have the following code in my tableModel and gets displayed only after clicking some other row-

=================================================================
Vector rowSet = (Vector) dedicatedAccountsData.elementAt(row);
rowSet.setElementAt(aValue,col);
fireTableCellUpdated(row, col)

=================================================================

Please clarify where I am going wrong.

Thanks in advance!!
Brian Cole
Author
Ranch Hand

Joined: Sep 20, 2005
Posts: 862
Originally posted by ram shyam:
I am doing some condition checking based on which I am rendering the JButton UndoButton. But after removing the model.setValueAt() now, and doing some changes on the table, I couldnt get the undoButton displayed unless I click some other row. The button is not getting reflected immediately after making any changes although I have the following code in my tableModel and gets displayed only after clicking some other row-

=================================================================
Vector rowSet = (Vector) dedicatedAccountsData.elementAt(row);
rowSet.setElementAt(aValue,col);
fireTableCellUpdated(row, col)


When you do this, how many table cells do you expect should change
their appearance? If only that one cell at row/col, then your code
should be fine. If other cells in the same row may change, then you
need to either call fireTableCellUpdated() more times, or just call
fireTableRowsUpdated(row, row) to fire an event for the entire row.
If cells in other rows may also change, you need to make sure they
too are covered by the events you fire.

It sounds like this is the problem you are having, but it's hard to
tell for sure.
ram shyam
Ranch Hand

Joined: May 04, 2007
Posts: 77
Hi,

Many thanks for your reply!

The problem is solved now after using fireTableRowsUpdated(row, row); method.

Thanks a lot once again!!
ram shyam
Ranch Hand

Joined: May 04, 2007
Posts: 77
Hi,

Regarding your reply for my initial issue, I thought of re-doing the design of my tableCellRenderer, but got struck up in between. As per my code, if there are any changes done in any of the rows, a JButton will be displayed in the corresponding row (in a particular column). To achieve this, I used the logic in the TableCellRenderer itself which you suggested that this has to be done in TableModel. Inside getValueAt(int row, int col), the value has to be returned for all the rows and columns. But in my case, the value of "Jbutton" has to be returned only in specific cases and at the sametime, return the other values in other columns. How can this be achieved in getValueAt()?


Also, I have one more problem in the existing code itself. I am using a TableSorter to the sort each column on clicking the corresponding column header. In my case, sorting happens correctly, but after sorting, the renderer is called in which, my logic of comparing the changed values is executed which causes an issue.

From the following code in the tablecellRenderer-

=====================================================================

oldData = (Vector) Session.getCurrentController().getCurrentModel().getOriginalValue(AccountAdminModel.DEDICATED_ACCOUNTS);
if (oldData.elementAt(row) != null)
oldDate = DateUtils.getStringFromDate((Date) (((Vector)oldData.elementAt(row)).elementAt(2)));
if ((newValue != null && !newValue.contains(((Vector)oldData.elementAt(row)).elementAt(1).toString()))
|| (newDate != null && !newDate.equals(oldDate)) )
return undoButton;

=========================================================================

The newValue and newDate fields are the ones obtained from the tablemodel and the oldValue and oldDate are obtained from a Vector. So, after sorting, when this renderer is called, the tablemodel returns sorted list of newValue and newDate, but the oldValue and oldDate list remains the same, thereby, the condition checked above fails and returns the buttons in wrong rows.

Is there any solution to overcome this issue?

Please clarify!

Many thanks in advance!!
Brian Cole
Author
Ranch Hand

Joined: Sep 20, 2005
Posts: 862
Originally posted by ram shyam:
I used the logic in the TableCellRenderer itself which you suggested that this has to be done in TableModel. Inside getValueAt(int row, int col), the value has to be returned for all the rows and columns. But in my case, the value of "Jbutton" has to be returned only in specific cases and at the sametime, return the other values in other columns. How can this be achieved in getValueAt()?


I didn't say that it must be done in the table model, but I do
think it usually works out better that way.

I'm not sure why you are worried about getValueAt(), though. It can return
any Object, so there should be no problem returning a specific object for
your button. You could do it this way:

public static final Object SPECIAL_VALUE_ONLY_FOR_BUTTON = ...something...;

Then getValueAt() can return that value for cetains row/column combinations,
and getTableCellRendererComponent() can do something like
if ( value.equals(SPECIAL_VALUE_ONLY_FOR_BUTTON) return undoButton;

There are also other approaches.


Also, I have one more problem in the existing code itself. I am using a TableSorter to the sort each column on clicking the corresponding column header. In my case, sorting happens correctly, but after sorting, the renderer is called in which, my logic of comparing the changed values is executed which causes an issue.


There has always been a distinction between the view column and the model
column in JTable, and now with table sorting there is a similar distinction
between the view row and the model row. JTable has methods to convert back
and forth if you need them.

btw, if you do the logic in the model (as above) then you might not even
have to worry about it.
ram shyam
Ranch Hand

Joined: May 04, 2007
Posts: 77
Hi,

Many thanks for your reply!

To overcome the issue of sorting, if I go ahead with the implementation in the model rather than in the renderer, this will be the new code getting into the getValueAt() in the tablemodel -


==================================================================

public Object getValueAt(int row, int col)
{
Object value = "";
Object spcl_value = "";
try
{
Vector rowSet = (Vector) dedicatedAccountsData.elementAt(row);
if(col != 9) //for the button column
value = rowSet.elementAt(col);
else
// code to check with the Vector "oldData"
// and compare the values
// and return spcl_value
// else return null
}
catch(ClassCastException cce)
{}
return value ;
}


====================================================================

And as you suggested, in the renderer, I could check for the value and return undoButton. But, when I try to sort the columns, getValueAt() will again be called everytime (Same as the renderer), and again the same issue still exists as I had already mentioned in my previous post, ie, the new values returned from the rowSet will be in sorter order, whereas the values with which these will be compared will not be and hence, the spcl_value will be returned in incorrect places.

Please let me know if I am missing anything here.

Thanks in advance!!
[ May 06, 2008: Message edited by: ram shyam ]
Brian Cole
Author
Ranch Hand

Joined: Sep 20, 2005
Posts: 862
Originally posted by ram shyam:
And as you suggested, in the renderer, I could check for the value and return undoButton. But, when I try to sort the columns, getValueAt() will again be called everytime (Same as the renderer),


Yes, JTable will call getValueAt() whenever it needs to, including after
sorting the rows. And the renderer will be invoked whenever cells need
to be painted.

Exactly what kind of sorting are you doing here? Is this the built-in JDK6
row sorting, or something else?

and again the same issue still exists as I had already mentioned in my previous post, ie, the new values returned from the rowSet will be in sorter order, whereas the values with which these will be compared will not be and hence, the spcl_value will be returned in incorrect places.


I'm not sure I understand. What rowSet? Are you saying the issue with
your "code to check with oldData" is the sort order of the old data? Is
that beyond your control?
[ May 06, 2008: Message edited by: Brian Cole ]
ram shyam
Ranch Hand

Joined: May 04, 2007
Posts: 77
Hi,

Now, I have fixed the issue of sorting. But, I have one doubt in tableCellEditor. Is it advisable to use model.setValueAt() inside getCellEditorValue() method?

The problem is that I have an editor which is used to open a dialog-pop up window on clicking any of the rows in the table. On entering data in the pop-up and pressing OK, the entered data should be set in the corresponding row from which the pop-up is opened. There are two values that could be changed but in getCellEditorValue() method, both the values cannot be returned at a time.

Here is the code of the getCellEditorValue() in my TableCellEditor()
=====================================================================

public Object getCellEditorValue()
{
currentAmount = balanceChangeDialog.getAmount();
currentDate = balanceChangeDialog.getExpiryDate();
TableModel model = aTable.getModel();
model.setValueAt(currentAmount, aTable.getSelectedRow(), 1);
model.setValueAt(currentDate, aTable.getSelectedRow(), 2);

if (theColumn.getHeaderValue().equals(Settings.getLocalString(SALabels.DEDICATED_ACCOUNTS_BALANCE)
+ " (" + Session.getPrimaryCurrencySymbol() + ")"))
return currentAmount;
else if (theColumn.getHeaderValue().equals(Settings.getLocalString(SALabels.DEDICATED_ACCOUNTS_EXPIRY)))
return currentDate;
else if (theColumn.getHeaderValue().equals(Settings.getLocalString(SALabels.DEDICATED_ACCOUNTS_ID)))
return model.getValueAt(aTable.getSelectedRow(), 0);
else if (theColumn.getHeaderValue().equals(Settings.getLocalString(SALabels.DEDICATED_ACCOUNTS_BALANCE)
+ " (" + Session.getSecondaryCurrencySymbol() + ")"))
return model.getValueAt(aTable.getSelectedRow(), 3);
else if (theColumn.getHeaderValue().equals(Settings.getLocalString(SALabels.DEDICATED_ACCOUNTS_DESCR)))
return model.getValueAt(aTable.getSelectedRow(), 4);
else
return null;
}


=========================================================================

In the above code, currentAmount and currentDate are the two values which has to be returned at a time which is impossible. So, I have used model.setValueAt() to set them in the corresponding rows and columns.

Please let me know if this is the correct design or any other solution to overcome this problem.

Thanks in advance!!
Brian Cole
Author
Ranch Hand

Joined: Sep 20, 2005
Posts: 862
Originally posted by ram shyam:
Is it advisable to use model.setValueAt() inside getCellEditorValue() method?

The problem is that I have an editor which is used to open a dialog-pop up window on clicking any of the rows in the table. On entering data in the pop-up and pressing OK, the entered data should be set in the corresponding row from which the pop-up is opened.


The way that I usually handle this is that the editor that pops up
the dialog pretty much only pops up the dialog. (Since it's an
editor it will go through getCellEditorValue/setValue, but then
I have my table model ignore the set value.) Then I have the dialog
itself call setValue(), or perhaps some other method I've provided
that mutates the table data and fires events.

I can't think of any real problems with calling setValue() from
getCellEditorValue(), but it does seem kind of strange.

There are two values that could be changed but in getCellEditorValue() method, both the values cannot be returned at a time.


Well they could be. I'm not necessarily saying you should do this,
but setValue() is under your control so it can do anything you want.
If you wanted it could do something like
if (value instanceof SomeClassThatHoldsMultipleValuesForMeToSet) {
// set multiple values
}

so all getCellEditorValue() would have to do is
return new SomeClassThatHoldsMultipleValuesForMeToSet(...);

Again, it might not make sense to do it this way, but it's certainly possible.
Saumya Venkatram
Greenhorn

Joined: Apr 20, 2010
Posts: 3
Hi guys,
I am facing a similar problem. I have a JTable and I am using it as a heatmap / color grid.
I have an ArrayList of Objects and depending on the number of objects that fall into my selection parameter, i am coloring (darkening the color of) the corresponding cell.
I am using the TableCellRenderer for this purpose. However, every time i even resize the window, the table cell renderer gets to work and the count is incremented. Any help will be greatly appreciated.



Also, I am invoking the table.repaint() method on a button click, since the button click changes the values of my input for the table.

Any help will be appreciated.
Thanks!!
Saumya Venkatram
Greenhorn

Joined: Apr 20, 2010
Posts: 3
Thanks guys!
I studied the prev posts and moved the logic to the tabel model. worked like a charm
Thanks!
Darryl Burke
Bartender

Joined: May 03, 2008
Posts: 4523
    
    5

Zombie alert!


luck, db
There are no new questions, but there may be new answers.
Saumya Venkatram
Greenhorn

Joined: Apr 20, 2010
Posts: 3
Darryl Burke wrote:Zombie alert!


I hope you are refering to the thread!
Rob Spoor
Sheriff

Joined: Oct 27, 2005
Posts: 19653
    
  18

Darryl is referring to DontWakeTheZombies, but that rule has been softened lately. The FAQ entry even reflects that change.


SCJP 1.4 - SCJP 6 - SCWCD 5 - OCEEJBD 6
How To Ask Questions How To Answer Questions
 
 
subject: Problem with TableCellRenderer
 
Similar Threads
Adding checkbox to some selected row of a column.
disabling rows in JTable
customizng JTableHeader
Problem with JTable
Color Some particular rows in JTable