Changing the Background Color for One Cell in a JTable

Discussion in 'Java' started by Hal Vaughan, Jan 19, 2008.

  1. Hal Vaughan

    Hal Vaughan Guest

    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();
    }
    });
    }
    }
     
    Hal Vaughan, Jan 19, 2008
    #1
    1. Advertising

  2. Hal Vaughan

    Hal Vaughan Guest

    Hal Vaughan wrote:

    > 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
     
    Hal Vaughan, Jan 20, 2008
    #2
    1. Advertising

  3. Hal Vaughan

    Roedy Green Guest

    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;
    }
    --
    Roedy Green, Canadian Mind Products
    The Java Glossary, http://mindprod.com
     
    Roedy Green, Jan 20, 2008
    #3
  4. Hal Vaughan

    Filip Larsen Guest

    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,
    --
    Filip Larsen
     
    Filip Larsen, Jan 20, 2008
    #4
  5. Hal Vaughan

    Hal Vaughan Guest

    Thanks for a lot of good info!

    Hal

    Filip Larsen wrote:

    > 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,
     
    Hal Vaughan, Jan 20, 2008
    #5
  6. Hal Vaughan

    Tod

    Joined:
    Oct 28, 2008
    Messages:
    1
    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: Oct 28, 2008
    Tod, Oct 28, 2008
    #6
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Tivo Escobar
    Replies:
    1
    Views:
    7,481
    manusa
    Apr 12, 2007
  2. Chanman
    Replies:
    0
    Views:
    990
    Chanman
    Jun 19, 2008
  3. Kamaljeet Saini
    Replies:
    0
    Views:
    466
    Kamaljeet Saini
    Feb 13, 2009
  4. Arthur Shapiro
    Replies:
    29
    Views:
    343
    Arthur Shapiro
    Jan 2, 2005
  5. Replies:
    1
    Views:
    154
    Martin Honnen
    Mar 27, 2005
Loading...

Share This Page