Changing the Background Color for One Cell in a JTable

H

Hal Vaughan

I've been working on this for a few hours. I've read tutorials and seen
different examples, but this is still driving me nuts.

I have a table and want to be able to change the color of one cell in it.
It seems to do this, I can't get a cell renderer and just change the color,
I have to create my own class to do that. I've tried that and a number of
other things and can't get it to work.

I finally took the SimpleTableDemo from Sun and checked to make sure it was
running. Once it was, I added four lines:

boolean isSelected = table.isCellSelected(1, 1);
DefaultTableCellRenderer defRender = (DefaultTableCellRenderer)
table.getCellRenderer(1, 1);
Component cellRenderer = defRender.getTableCellRendererComponent(table,
"Huml", isSelected, false, 1, 1);
cellRenderer.setBackground(Color.blue);

(The whole example is at the bottom of this post.)

When I use this example, it turns ALL the cells in the table blue, not just
the one at 1,1.

I've got several questions about this:
1) Do I have to create my own cell renderer to change a cell background
color, or can't I just do it like above? Even when I used my own renderer,
I still got the actual Component named cellRenderer using the same
parameters, so what difference would doing it in a separate class make?
2) The renderer needs the value that is in the cell currently. In my own
program, I got that from the DefaultTableModel I was using. Here I just
used the text that was put in that cell. Is there any way to get the cell
value directly from the table? In this example, I tried getting a
DefaultTableModel (by casting) from the table, but it didn't work, so I
couldn't get the data by reading the cell.
3) Why does this code (above) set the entire table and not just the cell at
1,1?

If anyone has a simpler example of how to set the background color in just a
few lines, I'd be glad to see it. I can't believe how much I need to do
just to change the background color in one table cell!

Thanks for any help/insight!

Hal

----------------example code----------------

/*
* SimpleTableDemo.java requires no other files.
*/

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;

import com.hal.gui.HalCellRenderer;

public class SimpleTableDemo extends JPanel {
private boolean DEBUG = false;

public SimpleTableDemo() {
super(new GridLayout(1,0));

String[] columnNames = {"First Name",
"Last Name",
"Sport",
"# of Years",
"Vegetarian"};

Object[][] data = {
{"Mary", "Campione",
"Snowboarding", new Integer(5), new Boolean(false)},
{"Alison", "Huml",
"Rowing", new Integer(3), new Boolean(true)},
{"Kathy", "Walrath",
"Knitting", new Integer(2), new Boolean(false)},
{"Sharon", "Zakhour",
"Speed reading", new Integer(20), new Boolean(true)},
{"Philip", "Milne",
"Pool", new Integer(10), new Boolean(false)}
};

final JTable table = new JTable(data, columnNames);
table.setPreferredScrollableViewportSize(new Dimension(500, 70));
// table.setFillsViewportHeight(true);

if (DEBUG) {
table.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
printDebugData(table);
}
});
}

//Create the scroll pane and add the table to it.
JScrollPane scrollPane = new JScrollPane(table);

//Add the scroll pane to this panel.
add(scrollPane);

//======================================================
//Only part I have added is the next four lines
//======================================================
boolean isSelected = table.isCellSelected(1, 1);
DefaultTableCellRenderer defRender = (DefaultTableCellRenderer)
table.getCellRenderer(1, 1);
Component cellRenderer =
defRender.getTableCellRendererComponent(table, "Huml", isSelected, false,
1, 1);
cellRenderer.setBackground(Color.blue);
}

private void printDebugData(JTable table) {
int numRows = table.getRowCount();
int numCols = table.getColumnCount();
javax.swing.table.TableModel model = table.getModel();

System.out.println("Value of data: ");
for (int i=0; i < numRows; i++) {
System.out.print(" row " + i + ":");
for (int j=0; j < numCols; j++) {
System.out.print(" " + model.getValueAt(i, j));
}
System.out.println();
}
System.out.println("--------------------------");
}

/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("SimpleTableDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

//Create and set up the content pane.
SimpleTableDemo newContentPane = new SimpleTableDemo();
newContentPane.setOpaque(true); //content panes must be opaque
frame.setContentPane(newContentPane);

//Display the window.
frame.pack();
frame.setVisible(true);
}

public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
 
H

Hal Vaughan

Hal said:
I've been working on this for a few hours. I've read tutorials and seen
different examples, but this is still driving me nuts.

I have a table and want to be able to change the color of one cell in it.
It seems to do this, I can't get a cell renderer and just change the
color,
I have to create my own class to do that. I've tried that and a number of
other things and can't get it to work.

I finally took the SimpleTableDemo from Sun and checked to make sure it
was
running. Once it was, I added four lines:

boolean isSelected = table.isCellSelected(1, 1);
DefaultTableCellRenderer defRender = (DefaultTableCellRenderer)
table.getCellRenderer(1, 1);
Component cellRenderer =
defRender.getTableCellRendererComponent(table,
"Huml", isSelected, false, 1, 1);
cellRenderer.setBackground(Color.blue);

(The whole example is at the bottom of this post.)

When I use this example, it turns ALL the cells in the table blue, not
just the one at 1,1.

I've got several questions about this:
1) Do I have to create my own cell renderer to change a cell background
color, or can't I just do it like above? Even when I used my own renderer,
I still got the actual Component named cellRenderer using the same
parameters, so what difference would doing it in a separate class make?
2) The renderer needs the value that is in the cell currently. In my own
program, I got that from the DefaultTableModel I was using. Here I just
used the text that was put in that cell. Is there any way to get the cell
value directly from the table? In this example, I tried getting a
DefaultTableModel (by casting) from the table, but it didn't work, so I
couldn't get the data by reading the cell.
3) Why does this code (above) set the entire table and not just the cell
at 1,1?

Answering my own questions:

Yes, it's necessary to create a special table cell rendering class to do
this. To change the color of any cells, you've got to create your own
renderer, then use JTable.setDefaultRenderer(Class columnClass,
TableCellRenderer renderer). What confuses me is that this takes two
arguments, the first works for me if I just use Object.class. If someone
can clue me in with what the heck goes on with that, I'd appreciate it.
The 2nd must be your special renderer. Then WITHIN your render, you have
the method getTableCellRendererComponent() with 5 arguments I won't list.
You'll never call this, but JTable does when rendering. Within your class,
in that method, put the code that will determine the conditions under which
a cell should be rendered a certain way.

In my case, I wanted cells to be rendered in certain colors if their row
number was one of the members of a LinkedList. I had to include routines
in my renderer to add row numbers to that list and I also had to check, in
getTableCellRendererComponent() to see if a row number was a member of that
list. If it was, then I specified my special background color.

It's not necessary to supply the renderer with any info about the cell
that's being rendered because the JTable will do that.

The hard part for me to understand was that I was trying to set just one or
a few cells to a different background color and, like I've found so often
in Java, you can't get there from here. To change one cell's color, you
have to write a renderer and add it to the table as a renderer and have the
code in that renderer, more specifically in the method
getTableCellRendererComponent() in that renderer, check for the cell you
want and use the special color. It's just a personal gripe, but it's
another case where, in order to do one thing in Java, I have to do an
additional 5 other things to get to the point where I can do it.

I've answered this to help others searching for this kind of info in the
future. If anyone has corrections, I hope they add them, since, as I often
point out, I'm self taught, and I often miss things that many people just
take for granted in Java.

Hal
 
R

Roedy Green

I would write a OneCellRenderer that looked something like this:

package com.mindprod.vercheck;

import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
import java.awt.*;

/**
* render JTable String column, in a selected font, colours and
alignment. Engage with: columnModel.getColumn( col
* ).setCellRenderer( new RainbowRenderer( font, foreground,
background, JLabel.CENTER ) )
*/
final class RainbowRenderer extends DefaultTableCellRenderer
implements TableCellRenderer
{
private final Color foreground;

private final Font font;

private final int horizontalAlignment;

// -------------------------- PUBLIC INSTANCE METHODS
--------------------------
/**
* constructor
*
* @param font for to render the column
* @param foreground foreground colour
* @param horizontalAlignment e.g. JLabel.CENTER
*/
public RainbowRenderer( Font font,
Color foreground,
int horizontalAlignment )
{
this.foreground = foreground;
this.font = font;
this.horizontalAlignment = horizontalAlignment;
}

public Component getTableCellRendererComponent( JTable table,
Object value,
boolean
isSelected,
boolean hasFocus,
int row,
int column )
{
JLabel template = (JLabel)
super.getTableCellRendererComponent( table, value,
isSelected, hasFocus, row, column );
if ( ???? the special cell )
{
template.setFont( specialFont );
template.setForeground( specialForeground );
}
else
{
template.setFont( font );
template.setForeground( foreground );
}
// we don't handle setting selected background here.
// We don't get called when selection changes.
// leave it up to JTable to set the background to selected or
normal.
template.setHorizontalAlignment( horizontalAlignment );
if ( value != null )
{
template.setText( value.toString() );
}
else
{
template.setText( null );
}
return template;
}
 
F

Filip Larsen

Hal Vaughan skrev:
Answering my own questions:

Yes, it's necessary to create a special table cell rendering class to do
this. To change the color of any cells, you've got to create your own
renderer, then use JTable.setDefaultRenderer(Class columnClass,
TableCellRenderer renderer). What confuses me is that this takes two
arguments, the first works for me if I just use Object.class. If someone
can clue me in with what the heck goes on with that, I'd appreciate it.

You set default renderers with JTable.setDefaultRenderer and column
renderes with TableColumn.setCellRenderer, where the table column for
instance can be found using JTable.getColumnModel().getColumn(columnIndex).

If you want to understand how JTable decides which cell renderer to use,
look in JTable.getCellRendrer (snippet below from Java 1.4 source):

public TableCellRenderer getCellRenderer(int row, int column) {
TableColumn tableColumn = getColumnModel().getColumn(column);
TableCellRenderer renderer = tableColumn.getCellRenderer();
if (renderer == null) {
renderer = getDefaultRenderer(getColumnClass(column));
}
return renderer;
}

If your table model implement the TableModel.getColumnClass method to
return the special class you have for relevant column, you can register
your special renderer as a default renderer for that class. Note that if
you use DefaultTableModel it will just return Object.class for all
columns, which again means that only the default renderer registered for
Object.class will ever be used by the table. So, for default renderers
to be found the table model must return the right class.

Alternatively you can register your renderer with the table column. This
can for instance be useful if you have two columns with same class you
want to render with different renderers or if you dont want to "polute"
your table model with knowledge of column classes.

As to the definition of the renderer itself there are several ways to do
it depending on what you want. A fairly simple solution is to make a
wrapper renderer that delegates as much as possible, like

final TableCellRenderer r = table.getDefaultRenderer(String.class);
table.setDefaultRenderer(Special.class, new TableCellRenderer() {
public Component getTableCellRendererComponent(...) {
Color bg = figureOutBackgroundColor(value);
Component c = r.getTableCellRendererComponent(...);
c.setBackground(bg);
return c;
}
});

This assumes that your Special class uses toString() to construct the
textual value for the cell. If you want to use another method, say,
Special.getBalance() which for instance returns a Float, you can do

final TableCellRenderer r = table.getDefaultRenderer(Float.class);
table.setDefaultRenderer(Special.class, new TableCellRenderer() {
public Component getTableCellRendererComponent(...) {
Color bg = figureOutBackgroundColor(value);
value = ((Special)value).getBalance();
Component c = r.getTableCellRendererComponent(...);
c.setBackground(bg);
return c;
}
});


If you want to make sure your cell renderering appears as a standard
string renderer with just the changed background, you can extend from
DefaultTableCellRenderer instead of using delegation:

table.setDefaultRenderer(Special.class, new DefaultTableCellRenderer() {
public Component getTableCellRendererComponent(...) {
Component c = super.getTableCellRendererComponent(...);
c.setBackground(figureOutBackgroundColor(value));
return c;
}
});



Regards,
 
H

Hal Vaughan

Thanks for a lot of good info!

Hal

Filip said:
Hal Vaughan skrev:


You set default renderers with JTable.setDefaultRenderer and column
renderes with TableColumn.setCellRenderer, where the table column for
instance can be found using
JTable.getColumnModel().getColumn(columnIndex).

If you want to understand how JTable decides which cell renderer to use,
look in JTable.getCellRendrer (snippet below from Java 1.4 source):

public TableCellRenderer getCellRenderer(int row, int column) {
TableColumn tableColumn = getColumnModel().getColumn(column);
TableCellRenderer renderer = tableColumn.getCellRenderer();
if (renderer == null) {
renderer = getDefaultRenderer(getColumnClass(column));
}
return renderer;
}

If your table model implement the TableModel.getColumnClass method to
return the special class you have for relevant column, you can register
your special renderer as a default renderer for that class. Note that if
you use DefaultTableModel it will just return Object.class for all
columns, which again means that only the default renderer registered for
Object.class will ever be used by the table. So, for default renderers
to be found the table model must return the right class.

Alternatively you can register your renderer with the table column. This
can for instance be useful if you have two columns with same class you
want to render with different renderers or if you dont want to "polute"
your table model with knowledge of column classes.

As to the definition of the renderer itself there are several ways to do
it depending on what you want. A fairly simple solution is to make a
wrapper renderer that delegates as much as possible, like

final TableCellRenderer r = table.getDefaultRenderer(String.class);
table.setDefaultRenderer(Special.class, new TableCellRenderer() {
public Component getTableCellRendererComponent(...) {
Color bg = figureOutBackgroundColor(value);
Component c = r.getTableCellRendererComponent(...);
c.setBackground(bg);
return c;
}
});

This assumes that your Special class uses toString() to construct the
textual value for the cell. If you want to use another method, say,
Special.getBalance() which for instance returns a Float, you can do

final TableCellRenderer r = table.getDefaultRenderer(Float.class);
table.setDefaultRenderer(Special.class, new TableCellRenderer() {
public Component getTableCellRendererComponent(...) {
Color bg = figureOutBackgroundColor(value);
value = ((Special)value).getBalance();
Component c = r.getTableCellRendererComponent(...);
c.setBackground(bg);
return c;
}
});


If you want to make sure your cell renderering appears as a standard
string renderer with just the changed background, you can extend from
DefaultTableCellRenderer instead of using delegation:

table.setDefaultRenderer(Special.class, new DefaultTableCellRenderer() {
public Component getTableCellRendererComponent(...) {
Component c = super.getTableCellRendererComponent(...);
c.setBackground(figureOutBackgroundColor(value));
return c;
}
});



Regards,
 

Tod

Joined
Oct 28, 2008
Messages
1
Reaction score
0
I have a similar problem

I am tryingto change the background color on a boolean column (aka checkbox) in a JTable. I can do this successfully with a String column, but not for the boolean column. Any advice? Here is how I do it for a String column:

class NamedColor extends Color {
String name;
public NamedColor(Color color, String name) {
super(color.getRGB());
this.name = name;
}

public String toString() {
return name;
}
}

<elsewhere in the code>
DefaultTableCellRenderer colorRenderer =
new DefaultTableCellRenderer() {
public void setValue(Object value) {
if (value instanceof NamedColor) {
NamedColor c = (NamedColor)value;
setBackground(c);
// setForeground(c.getTextColor());
setText(c.toString());
// this.setToolTipText();
} else
{
super.setValue(value);
}
}
};

pKgList.getColumnModel().getColumn(1).setCellRenderer(colorRenderer);

<elsewhere in code>
Vector vct = new Vector(7);
vct.add(Boolean.FALSE);
String cnt2char;
cnt2char = pkg.substring(0,2).toUpperCase();
NamedColor nm3 = new NamedColor(Color.WHITE, "String Text");
vct.add(nm3);
<Vector vct is then added to JTable>

<elsewhere in code, I want to make a change to the background>
c1 = new NamedColor(new Color(165, 231, 255), this.pkg.pKgdefmodel.getValueAt(loop,1).toString()); /*Blue*/
this.pkg.pKgdefmodel.setValueAt(c1, loop, 1);
<JTable cell now has a Blue color>

So how can I change this to support a boolean column? I tried making a BooleanColor Class, but I don't know how to handle the boolean value. Name tracked String value in NamedColor, but what tracks the boolean value?
 
Last edited:

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,989
Messages
2,570,207
Members
46,783
Latest member
RickeyDort

Latest Threads

Top