4

I am trying to create row headers for my JTable. I've done my research here and here, but there is almost nothing out there. What I found did not keep the system look'n feel.

This is what I've found so far:

enter image description here

The first column (the one with no name) is the best I could achieve. I took the table header renderer and applied it on the row cell. It partly worked. As you can see the little arrow icon for the descending/ascending sort appears in every cell of the column and the the cell is now grey. Other than that its a complete fail. Furthermore, I don't want any arrow icon in my row headers.

The Col 1 is what happened when I tried to apply the look and feel from the UIManager. Obviously it did not work.

Col 2 is photoshop, it is what I am trying to do.

I did nothing to the other 2 column so its the default setup.

The question is : How can I give the look and feel of the system (not only windows) default table header to a row cell in order to create a row header and of course no little arrow icon.

Here is the code

import java.awt.Component;
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumnModel;
import sun.swing.table.DefaultTableCellHeaderRenderer;

public class RowHeaderTest extends JFrame
{

    public RowHeaderTest()
    {
        initComponents();
    }

    private void initComponents()
    {
        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        JScrollPane scrollPane = new JScrollPane();
        JTable table = new JTable();
        table.setAutoCreateRowSorter(true);
        table.getTableHeader().setReorderingAllowed(false);
        table.setModel(new DefaultTableModel(
                new Object[][]
                {
                    {
                        "Row 1", "Data 2", "Data 3", "Data 4", "Data 5"
                    },
                    {
                        "Row 2", "Data 6", "Data 7", "Data 8", "Data 9"
                    },
                    {
                        "Row 3", "Data 10", "Data 11", "Data 12", "Data 13"
                    }
                },
                new String[]
                {
                    "", "Col 1", "Col 2", "Col 3", "Col 4"
                }));
        table.getColumnModel().getColumn(0).setHeaderRenderer(HeaderRenderer.THIS);
        table.getColumnModel().getColumn(0).setCellRenderer(HeaderRenderer.THIS);
        table.getColumnModel().getColumn(1).setHeaderRenderer(HeaderRendererUI.THIS);
        table.getColumnModel().getColumn(1).setCellRenderer(HeaderRendererUI.THIS);
        table.getColumnModel().getColumn(3).setHeaderRenderer(RowTableHeaderRendere.THIS);
        table.getColumnModel().getColumn(3).setCellRenderer(RowTableHeaderRendere.THIS);
        scrollPane.setViewportView(table);
        setSize(new Dimension(400, 200));
        setLocationRelativeTo(null);
        add(scrollPane);
    }

    public static void main(String args[])
    {
        try
        {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        }
        catch (ClassNotFoundException | InstantiationException | IllegalAccessException | javax.swing.UnsupportedLookAndFeelException ex)
        {
            java.util.logging.Logger.getLogger(RowHeaderTest.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        java.awt.EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                new RowHeaderTest().setVisible(true);
            }
        });
    }
}

final class HeaderRenderer extends DefaultTableCellHeaderRenderer
{

    public static final HeaderRenderer THIS = new HeaderRenderer();

    @Override
    public Component getTableCellRendererComponent(
            JTable table, Object value, boolean isSelected,
            boolean hasFocus, int row, int column)
    {
        TableCellRenderer renderer = table.getTableHeader().getDefaultRenderer();
        return renderer.getTableCellRendererComponent(table, value, isSelected,
                hasFocus, row, column);
    }
}

final class HeaderRendererUI extends DefaultTableCellHeaderRenderer
{

    public static final HeaderRendererUI THIS = new HeaderRendererUI();

    @Override
    public Component getTableCellRendererComponent(
            JTable table, Object value, boolean isSelected,
            boolean hasFocus, int row, int column)
    {
        setText(value.toString());
        //TableHeader.ancestorInputMap
        setBackground(isSelected ? UIManager.getColor("TableHeader.focusCellBackground") : UIManager.getColor("TableHeader.background"));
        setBorder(UIManager.getBorder("TableHeader.cellBorder"));
        setFont(UIManager.getFont("TableHeader.font"));
        setForeground(UIManager.getColor("TableHeader.foreground"));
        return this;
    }
}

final class RowTableHeaderRendere extends DefaultTableCellHeaderRenderer
{

    public static final RowTableHeaderRendere THIS = new RowTableHeaderRendere();

    @Override
    public Component getTableCellRendererComponent(
            JTable table, Object value, boolean isSelected,
            boolean hasFocus, int row, int column)
    {
        JTable t = new JTable(new DefaultTableModel(
                null,
                new String[]
                {
                    value.toString()
                }));
        return t.getTableHeader();
    }
}

EDIT

I just added partial solution to the code. Col 3 now has the good look but still lack the feel (It has no mouse over) and I am not quite sure creating a new table just to get the header is the best way to do it.

Community
  • 1
  • 1
AyoyeSnif
  • 130
  • 2
  • 6
  • Welcome to the wonderful world of I hate you look and feel. The implementation of the `DefaultTableCellHeaderRenderer` is not based on the implementation begin used by default by the `TableHeader`. – MadProgrammer Jul 30 '13 at 03:40
  • 1. here are a few attempts, incl. good, and very good, 2. question whats real goal 3. there could be issue with RowRorter ets. (not tried) 4. +1 for posting nice question, with image and SSCCE, 5. could be answerable so easy --> don't to set whatever in Renderer, renderer is decorator not property or value generator 6. has nothing to do with L&F, but with keys for UIManager repeatly called from Renderer (bunch of event put there System.out.println), the same issue in another L&Fs too – mKorbel Jul 30 '13 at 07:50
  • basically, you can't get the exact visuals as in the header: the lafs configure the renderer biased on being used as columnHeader. To get rid off the sort icon, feed in a null table. A dirty trick (didn't try in this context) is to use an image: make the laf believe it's configuring the renderer as a columnHeader, then draw that image into the cell renderer. – kleopatra Jul 30 '13 at 10:02
  • Is there a way to create a single header and simple return it has the rendering component? – AyoyeSnif Jul 30 '13 at 18:54
  • OK I just created an empty table and got the header from it. Now I have the good look but it still lack the feel. I'll post the code as a partial answer. – AyoyeSnif Jul 30 '13 at 19:07
  • A JTable is not a spreadsheet. You might want to do a Google search for a Swing spreadsheet component. – Gilbert Le Blanc Jun 16 '14 at 17:18

1 Answers1

3

To keep the System LookAndFeel you need to change your renderer a little bit.

First extends your renderer with UIRessource. You need this marker interface to receive UI changes:

class HeaderRenderer extends DefaultTableCellHeaderRenderer 
                     implements javax.swing.plaf.UIResource {

Then get and hold the original renderer from your JTableHeader (e.g. as field defRenderer passed by a constructor). In your getTableCellRendererComponent call the defRenderer.getTableCellRendererComponent, modify and return this JLabel.

private final TableCellRenderer defRenderer;

HeaderRenderer (TableCellRenderer defRenderer) {
  this.defRenderer = defRenderer;
}

@Override
public Component getTableCellRendererComponent(
        JTable table, Object value, boolean isSelected,
        boolean hasFocus, int row, int column) {
  Component c = defRenderer.getTableCellRendererComponent (...);
  if ( c instanceof JLabel ) {
    JLabel lbl = (JLabel)c;
    // do anything you want...
  }
  return c;
}

At least override the updateUI() method in your Renderer and delegete the updateUI call to the defRenderer:

@Override
public void updateUI() {
  TableCellRenderer locDefRenderer = defRenderer;
  if (locDefRenderer instanceof JComponent) {
    ((JComponent) locDefRenderer).updateUI();
  } else {
    super.updateUI();
  }
}

With this receipt it's possible to change many things. Plain paintComponent-rendering is a little bit more complicated ;-) - for own paints use a JLayer (introduced in Java 7)

Josh
  • 869
  • 10
  • 10