1

If I use the following to display a JTable:

package jtable.fontsize;
import java.awt.BorderLayout;
import java.awt.Font;
import java.lang.reflect.InvocationTargetException;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;

import rcutil.swing.table.LastColumnChangesWidthJTable;


public class JTableCellPlay extends JFrame
{
  public JTableCellPlay()
  {
    super("setting Table font size");
    setDefaultCloseOperation(EXIT_ON_CLOSE);
  }

  public static void main(String ... arguments) throws InvocationTargetException, InterruptedException
  {
    JTableCellPlay mainScreen = new JTableCellPlay();
    mainScreen.go();
  }

  public void go() throws InvocationTargetException, InterruptedException
  {
    SwingUtilities.invokeAndWait(new Runnable() { public void run() { createScreen(); } } );
    setVisible(true);
  }

  public void createScreen()
  {
    LastColumnChangesWidthJTable aLittleJTable = new LastColumnChangesWidthJTable(new LittleTableModel());
    aLittleJTable.setDefaultRenderer(Integer.class, new IntegerCellRenderer());
    JScrollPane scrollPane = new JScrollPane(aLittleJTable);
    add(scrollPane, BorderLayout.CENTER);
    pack();
  }
}

with the following renderer:

package jtable.fontsize;

import java.awt.Color;
import java.awt.Component;

import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.table.TableCellRenderer;


public class IntegerCellRenderer extends JLabel implements TableCellRenderer
{
  @Override
  public Component getTableCellRendererComponent
                      (JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
  {
    String resultString = String.format("~%d~", (Integer)value);
    setText(resultString);
    setHorizontalAlignment(JLabel.CENTER);
//    if (isSelected) 
//    { 
//      setOpaque(true);  // evidently necessary for JLabel as a component in a JTable cell
//      setBackground(Color.GRAY);
//    }
    return this;
  }
}

and the following model (so that your code will run, yes I know there are other ways to do this part):

package jtable.fontsize;

import javax.swing.table.AbstractTableModel;

public class LittleTableModel extends AbstractTableModel
{
  public Class<? extends Object> getColumnClass(int column)
  {
    Class<? extends Object> c = null;
    switch (column)
    {
    case 0: c = String.class; break;
    case 1: c = Integer.class; break;
    case 2: c = String.class; break;
    }
    return c;
  }

  String[] columnNames = { "first", "second", "third" };
  Object[][] data = { { "one", 2, "three" }, { "four", 5, "six" } };

  public int getColumnCount() { return 3; }
  public int getRowCount() { return 2; }
  public String getColumnName(int index) { return columnNames[index]; }

  public Object getValueAt(int row, int column)
  {
    Object result = data[row][column];
    return result;
  }
}

Then when I select one of the rows, the two String columns get a selection highlight but the "Integer" column does not.

If I comment out the line that sets the "Integer" renderer, then clicking on a row selects the entire row as expected.

I know I can use the commented-out lines in the renderer to highlight the cell, adding setOpaque(true) and setBackground(Color) and even play games to get the selected and non-selected background colors correct, but I suspect there is some way to use the same renderer that was used originally for things like that, and just use the code in my renderer to do the special stuff I need to do. Can someone explain how that works?

arcy
  • 12,845
  • 12
  • 58
  • 103
  • Several approaches are suggested in [*Highlight cell when row is selected*](https://stackoverflow.com/q/32301649/230513). – trashgod Sep 24 '17 at 13:33

1 Answers1

2

Why not simply give your renderer an else block where you undo the changes made in t he if block?

if (isSelected) {
    setOpaque(true); 
    setBackground(Color.GRAY);
} else {
    setOpaque(false); // allow underlying color to show
    setBackground(null); // no color added
}

Or another option is to have your renderer class extend DefaultTableCellRenderer rather than implement TableCellRenderer, allowing you to use the innate highlighting abilities of the default renderer:

@SuppressWarnings("serial")
class IntegerCellRenderer2 extends DefaultTableCellRenderer {
    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
            int row, int column) {
        String resultString = String.format("~%d~", (Integer) value);
        setText(resultString);
        setHorizontalAlignment(JLabel.CENTER);
        return super.getTableCellRendererComponent(table, resultString, isSelected, hasFocus, row, column);
    }
}

Note that you can set some properties of the renderer within its constructor. For example:

@SuppressWarnings("serial")
class IntegerCellRenderer2 extends DefaultTableCellRenderer {
    public IntegerCellRenderer2() {
        setHorizontalAlignment(JLabel.CENTER);
    }

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
            int row, int column) {
        String resultString = String.format("~%d~", (Integer) value);
        setText(resultString);
        // setHorizontalAlignment(JLabel.CENTER);
        return super.getTableCellRendererComponent(table, resultString, isSelected, hasFocus, row, column);
    }
}
  • Well, I don't know what the opaque setting and background are, a priori, any more than I know that the selection color is gray. And as I said, I know I can make calls to find out what those are, but I wanted to know a better way. Your second method appears to work, though it seems dodgy to me -- what if the default renderer set the alignment? I dislike having code in my class that depends on the internals of its superclass. – arcy Sep 24 '17 at 13:40
  • @arcy: what *exactly* are you trying to do? – DontKnowMuchBut Getting Better Sep 24 '17 at 13:41
  • @arcy; If you use the Default renderer, you can query the super's component as to its background state and its opaque property state easy enough, but again, what eventually are you trying to do with this? – DontKnowMuchBut Getting Better Sep 24 '17 at 13:45
  • I am trying to use the default behavior of the default renderer instead of attempting to duplicate it. – arcy Sep 24 '17 at 13:45
  • @arcy: my second call is SOP (standard operating procedure) for extending a cell renderer. And you're setting the alignment in the renderer, so no problem. Nothing dodgy about. – DontKnowMuchBut Getting Better Sep 24 '17 at 13:46
  • If the default renderer were to set the alignment, then my setting it would not work. So the code that sets alignment depends on the fact that the default renderer does not. We know that works, but was there any reason to think that it would? Does the default renderer contract specify that it does not and never will set alignment? That's the dodgy part. – arcy Sep 24 '17 at 13:48
  • @arcy: you can also set properties of the super object in your override's constructor, as per above's edit. – DontKnowMuchBut Getting Better Sep 24 '17 at 13:51
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/155195/discussion-between-arcy-and-dontknowmuchbut-getting-better). – arcy Sep 24 '17 at 13:52
  • 2
    @arcy, If you are just creating a custom renderer to format the data then it is easier to override the `setValue(...)` method of the default renderer as demonstrated in the Swing tutorial on [Using Custom Renderers](http://docs.oracle.com/javase/tutorial/uiswing/components/table.html#renderer). And if you want to be fancier you can create a reusable renderer that accepts a Formatter as a parameter as demonstrated in [Table Format Renderers](https://tips4java.wordpress.com/2008/10/11/table-format-renderers/) – camickr Sep 24 '17 at 17:54