Skip to main content

Determine on which row the cell renderer to operate

7 replies [Last post]
miroslav
Offline
Joined: 2006-06-10

Hallo everybody,
I have a serious problem with the cell rederer. This to some extand is because I cannot understand it right.
I have a table with the following information. User name document and Status columns and is smth like an email. The user send an email and the status column serves to signalize whether somebody has answered(green-yes, red-refuse, yellow-promise)
I do the coloring with a cell renderer and the problems are the following.
1.I know how to color a cell but I don't know how to determine the row of this cell. The renderer colors all the ros in the column. The only condition that I can put is in accordance with the information from the other cell. For example to user A has been answered find user A, doc R and color the cell in the status bar with green. How can one determine the this row in the cell rednderer.
2. The secon problem is with the sorting. I have done the sorting like in the swing tutorial but when I put
fireTableDateChanged() in the cell renderer it stops working. Without it on the other hand the cell rendere does not change the colors dynamically.
3. And one more question. How can make the sorter works also for the status column. To order first the green then yellow and red color.

I would be very greatful if somebody can help me bacause I have the feeling that I am going in a circle.

Miroslav

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
shan-man
Offline
Joined: 2006-02-17

Hi Miroslav,

Miroslav wrote:
> Thank you both a lot and the code was very helpful. I
> hav echanged a little bit my idea and I will put
> lables in the status column.
> But I was only curious if it is possible in this
> his status column to have only colors with no text or
> something like that.

Of course it's possible :)

> Then it would be much more
> difficult to make the renderer because the only
> identifiers where to change the color will be the
> information from the other cells in the row.

It won't be more difficult at all. Just write your renderer differently. I don't know what you mean about having to fetch the information from other cells in the row. Keep it simple - maintain data in the model for the "status" column. Then change it there and the renderer will update itself appropriately.

> I mean I have a methode that returns change to
> to this user(String) and the document that he has
> sent(String)that he has refused (color red). Then
> there should be a methode that to finds that row in
> the cell renderer and then chenge the color.
> Once again thank you a lot :-)

Again, don't worry about searching for the renderer. Change data in the model. Here's another runnable example that combines what we've talked about. It uses a custom cell renderer based on JLabel that renders only the color associated with a particular Status object (no text).

There's also a button at the bottom of the GUI. When you click it, setValueAt() will be called on the table with new status values for the first two rows. Notice that they update appropriately.
[code]
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.table.TableCellRenderer;

public class RendererTest extends JFrame {

// objects we'll use for the status column
public static class Status implements Comparable {
public static final int YES = 1;
public static final int PROMISE = 2;
public static final int REFUSE = 3;
private int value;

public Status(int value) {
this.value = value;
}

public int compareTo(Object o) {
return ((Status) o).value - value;
}

// return the color to use in the renderer
public Color toColor() {
switch(value) {
case YES: return Color.GREEN;
case PROMISE: return Color.YELLOW;
case REFUSE: return Color.RED;
}

// should not happen
return Color.BLACK;
}
}

// extend JLabel instead of DefaultCellRenderer
class RGYRenderer extends JLabel implements TableCellRenderer {
public RGYRenderer() {
setOpaque(true);
}

public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus,
int row, int column) {

setBackground(((Status)value).toColor());

if (isSelected) {
setBackground(getBackground().darker());
}

return this;
}
}

public RendererTest() {
super("RendererTest");

Object[][] data = {{"Foo", "Foo", new Status(Status.YES)},
{"Foo", "Bar", new Status(Status.REFUSE)},
{"Foo", "Bar", new Status(Status.PROMISE)},
{"Bar", "Bar", new Status(Status.YES)}};
String[] names = {"Name", "Document", "Status"};

final JTable table = new JTable(data, names) {
// we haven't provided an appropriate editor for the status column
// so we'll disable editing it to prevent exceptions
public boolean isCellEditable(int row, int column) {
return column == 2 ? false : super.isCellEditable(row, column);
}
};

table.getColumn("Status").setCellRenderer(new RGYRenderer());

getContentPane().add(new JScrollPane(table), BorderLayout.CENTER);

JButton button = new JButton("Update");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
table.setValueAt(new Status(Status.REFUSE),0, 2);
table.setValueAt(new Status(Status.YES), 1, 2);
}
});

getContentPane().add(button, BorderLayout.SOUTH);
}

public static void main(String[] args) {
RendererTest test = new RendererTest();
test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
test.setSize(400, 400);
test.setLocationRelativeTo(null);
test.setVisible(true);
}
}
[/code]
Regards,
Shannon

zander
Offline
Joined: 2003-06-13

> > The easiest way to do that is to alter the above
> > usage of strings and put a different object in
> there
> > that has a toString method that returns
> > refuse/promise/yes, and implements the Comparable
> > interface to return them sorterd.
>
> You'd also need to fix, or subclass the TableSorter
> to deal with Comparables in the
> "compareRowsByColumn". Currently, it will simply call
> "toString" on them and compare the Strings.

Oops, you're right. I did that with our own version of the tableSorter some time ago; including many other fixes.
If you download the version from the UICompiler project my suggestion will work immediately.

See http://uic.sf.net
and for the direct code:
http://cvs.sourceforge.net/viewcvs.py/*checkout*/uic/uicompiler/uic/widg...

zander
Offline
Joined: 2003-06-13

Point 3:
put a comparable object in the cell.

The easiest way to do that is to alter the above usage of strings and put a different object in there that has a toString method that returns refuse/promise/yes, and implements the Comparable interface to return them sorterd.

[code]
public class Status implements Comparable {
public static final int STATUS_YES = 1;
public static final int STATUS_PROMISE = 2;
public static final int STATUS_REFUSE = 3;
private int value;

public Status(int value) {
this.value = value
}

public int getValue() {
return value;
}

public String toSting() {
if(value == STATUS_YES)
return "yes";
if(value == STATUS_PROMISE)
return "promise";
return "refuse";
}

public int compareTo(Object o) {
try {
return ((Status) o).value - value; // or the other way around??
} catch(ClassCastException e) {
return 0;
}
}
}
[/code]

Naturally the code that Shannon posted above should be changed a little to not do the color settings based on strings but based on ((Status) value).getValue() == Status.STATUS_YES) and similar..

shan-man
Offline
Joined: 2006-02-17

I like that idea zander!

zander wrote:
> Point 3:
> put a comparable object in the cell.
>
> The easiest way to do that is to alter the above
> usage of strings and put a different object in there
> that has a toString method that returns
> refuse/promise/yes, and implements the Comparable
> interface to return them sorterd.

You'd also need to fix, or subclass the TableSorter to deal with Comparables in the "compareRowsByColumn". Currently, it will simply call "toString" on them and compare the Strings.

> Naturally the code that Shannon posted above should
> be changed a little to not do the color settings
> based on strings but based on ((Status)
> value).getValue() == Status.STATUS_YES) and similar..

Definitely! What about adding a method to your Status implementation:
[code]
public Color toColor() {
switch(value) {
case STATUS_YES: return Color.GREEN;
case STATUS_PROMISE: return Color.YELLOW;
case STATUS_REFUSE: return Color.RED;
}

// should not happen
return Color.BLACK;
}
[/code]
Then, your renderer could set the color using:
[code]
comp.setBackground(((Status)value).toColor());
[/code]
Also, something I noticed: You shouldn't need to catch the ClassCastException in the "compareTo" method. It's valid to throw it.

Regards,
Shannon

miroslav
Offline
Joined: 2006-06-10

Thank you both a lot and the code was very helpful. I hav echanged a little bit my idea and I will put lables in the status column.
But I was only curious if it is possible in this status column to have only colors with no text or something like that. Then it would be much more difficult to make the renderer because the only identifiers where to change the color will be the information from the other cells in the row.
I mean I have a methode that returns change to this user(String) and the document that he has sent(String)that he has refused (color red). Then there should be a methode that to finds that row in the cell renderer and then chenge the color.
Once again thank you a lot :-)

Miroslav

zander
Offline
Joined: 2003-06-13

> But I was only curious if it is possible in this status column to have only colors with no text or something like that.

If you use the comparable code I posted above; simply make the toString return an empty string

shan-man
Offline
Joined: 2006-02-17

Hi miroslav,

A renderer's "getTableCellRendererComponent" will be called for every cell that it is going to be renderered. This method should look at the parameters that are given to it and determine how to configure the renderer. The way to accomplish what you want is to create a subclass of DefaultTableCellRenderer, override this method, and depending on the parameters, set the background color of the cell. This shouldn't be very difficult to determine since "getTableCellRendererComponent" is passed the row number, the column number, and the value for the cell (along with other information).

Here's a runnable example that does something like what you want. It sets the background color of the cell based on the text that will appear in it.
[code]
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.table.DefaultTableCellRenderer;

public class RendererTest extends JFrame {

// a custom renderer that will set the background
// color based on the value for the cell
class RGYRenderer extends DefaultTableCellRenderer {
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus,
int row, int column) {
// call into the superclass to configure the
// renderer first
Component comp = super.getTableCellRendererComponent(table, value,
isSelected, hasFocus,
row, column);

// set the background color based on the value
if (value.equals("yes")) {
comp.setBackground(Color.GREEN);
} else if (value.equals("refuse")) {
comp.setBackground(Color.RED);
} else if (value.equals("promise")) {
comp.setBackground(Color.YELLOW);
}

// darken the background when selected
if (isSelected) {
comp.setBackground(getBackground().darker());
}

return comp;
}
}

public RendererTest() {
super("RendererTest");

String[][] data = {{"Foo", "Foo", "yes"},
{"Foo", "Bar", "refuse"},
{"Foo", "Bar", "promise"},
{"Bar", "Bar", "yes"}};
String[] names = {"Name", "Document", "Status"};

JTable table = new JTable(data, names);

// set a custom renderer on the status column
table.getColumn("Status").setCellRenderer(new RGYRenderer());

getContentPane().add(new JScrollPane(table), BorderLayout.CENTER);
}

public static void main(String[] args) {
RendererTest test = new RendererTest();
test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
test.setSize(400, 400);
test.setLocationRelativeTo(null);
test.setVisible(true);
}
}
[/code]
That answers question 1. As for question 2: You should NOT be calling fireTableDataChanged() from within your renderer. If you do things the way I've suggested, you won't need it.

Finally, question 3: I haven't used the TableSorter personally, but a quick look at the source code suggests that you could override the "compareRowsByColumn" method and do custom sorting for that column.

Hope this all helps!
Shannon