Search code examples
javaswingjtablerenderernimbus

JTable- Overriding prepareRenderer while applying the default Nimbus styling?


I'm using Nimbus. To make things simple with overriding prepareRenderer on a JTable, the first thing I usually do is get the Component from the super method, so I can tweak only the properties I want while leaving everything else the same.

The problem is once I change a style property on that component, like background color, it does every single render after that with that color because it apparently is reusing that JLabel. So every new cell that renders becomes red, not just the one I wanted.

I've always used HTML to avoid this, but I need to manipulate the JLabel's foreground/background properties. Does anybody have a block of code that sets all the default properties for a renderer, and I can use that in the applyDefaults method placeholder I have below?

I am using Nimbus so I don't know if that makes things harder. But I want the foreground/background for selected/unselected and odd/even rows to be rendered to their defaults, and everything is manipulated after that.

@Override
public Component prepareRenderer(TableCellRenderer renderer, int row, int col) {

        final Component c =super.prepareRenderer(renderer, row, col);
        applyDefaults(c, row, col); //what do I need to restore default styles here? 

        boolean highlightRed condtion == //some boolean condition
        boolean isSelected = //calculate whether row is selected

        if (c instanceof JLabel) { 
            if (isSelected == false && highlightRed) { 
                ((JLabel) c).setBackground(Color.RED);
            }
        }

UPDATE: Went with camickr's suggestion and I built a convenient default interface for Nimbus JTables. the restoreDefaultRenderer resets all the properties to the default Nimbus colors and alignments.

public interface JTableCustomRender {
    static final Color foreGround = Color.black;
    static final Color oddBackGround = new Color(255,255,255);
    static final Color evenBackGround = new Color(242,242,242);
    static final Color selectedForeGround = new Color(255,255,255);
    static final Color selectedBackGround = new Color(57,105,138);

    //static final ImmutableList<Class<?>> leftAlignTypes = ImmutableList.of(String.class, Date.class, DateTime.class);
    static final ImmutableList<Class<?>> rightAlignTypes = ImmutableList.of(Integer.class, Double.class, Float.class, BigDecimal.class);

    public Component prepareRenderer(TableCellRenderer renderer, int viewRow, int viewCol);

    public default Component restoreDefaultRenderer(JTable table, Component c, int viewRow, int viewCol) { 

        if (c instanceof JLabel) { 
            final boolean rowSelected = Arrays.stream(table.getSelectedRows()).filter(i -> i == viewRow).findAny().isPresent();

            JLabel label = (JLabel) c;
            if (rowSelected) { 
                label.setForeground(selectedForeGround);
                label.setBackground(selectedBackGround);
            }
            else { 
                if (viewRow % 2 == 0) { 
                    label.setBackground(evenBackGround);
                }
                else { 
                    label.setBackground(oddBackGround);
                }
                label.setForeground(foreGround);
            }
            Object value = table.getValueAt(viewRow, viewCol);

            if(rightAlignTypes.contains(value.getClass())) { 
                label.setHorizontalAlignment(SwingConstants.RIGHT);
            }
            else { 
                label.setHorizontalAlignment(SwingConstants.LEFT);
            }
        }

        return c;
    }

}

Solution

  • it does every single render after that with that color because it apparently is reusing that JLabel.

    Correct, which is why you need an else statement to reset the background back to the table default.

    Check out Table Row Rendering for examples of how you might do this so that you also take into account the selection color of the row as well.

    Does anybody have a block of code that sets all the default properties for a renderer

    The basic code would be:

    c.setBackground( getBackground() );
    c.setForeground( getForeground() );
    

    You would do that for any property that you are manually tying to override.