JavaSwingJTable and TableModel: Cell sizes


Cell sizes

Calculating (fixed) row heights based on prototypes

By default, JTable has a fixed row height of 16 pixels. This is always wrong. (How does it know this is enough for the current font - even if the default renderers are used?). It is also much more critical than having proper column widths, for these shrink/grow to fit the width given to the JTable.

Use RowHeightUpdater.java (only a sketch) to calculate row heights (optionally dynamically based on which columns are visible).

For variable row heights, things are much more complicated.

Calculating initial column widths based on contents

The following code sets the preferred size of a column to the maximum preferred size of all cells in a column. I am not really satisfied with this code since it is not configurable and scans the whole TableModel. That a) may be too slow for many rows, b) is biased towards the values that happen to be in the TableModel, c) may result in huge widths if the data happens to be (unexpectly?) wide. It may be better to use some prototype values instead.

Regard it as suggestion that must be adapted to your needs.

As a) default renderers were introduced and b) the columnMargin policy was changed with JDK1.3, the code must be changed for older JDKs.

public static void calcColumnWidths(JTable table)
{
    JTableHeader header = table.getTableHeader();

    TableCellRenderer defaultHeaderRenderer = null;

    if (header != null)
        defaultHeaderRenderer = header.getDefaultRenderer();

    TableColumnModel columns = table.getColumnModel();
    TableModel data = table.getModel();

    int margin = columns.getColumnMargin(); // only JDK1.3

    int rowCount = data.getRowCount();

    int totalWidth = 0;

    for (int i = columns.getColumnCount() - 1; i >= 0; --i)
    {
        TableColumn column = columns.getColumn(i);
            
        int columnIndex = column.getModelIndex();
            
        int width = -1; 

        TableCellRenderer h = column.getHeaderRenderer();
          
        if (h == null)
            h = defaultHeaderRenderer;
            
        if (h != null) // Not explicitly impossible
        {
            Component c = h.getTableCellRendererComponent
                   (table, column.getHeaderValue(),
                    false, false, -1, i);
                    
            width = c.getPreferredSize().width;
        }
       
        for (int row = rowCount - 1; row >= 0; --row)
        {
            TableCellRenderer r = table.getCellRenderer(row, i);
                 
            Component c = r.getTableCellRendererComponent
               (table,
                data.getValueAt(row, columnIndex),
                false, false, row, i);
        
                width = Math.max(width, c.getPreferredSize().width);
        }

        if (width >= 0)
            column.setPreferredWidth(width + margin); // <1.3: without margin
        else
            ; // ???
            
        totalWidth += column.getPreferredWidth();
    }

// only <1.3:   totalWidth += columns.getColumnCount() * columns.getColumnMargin();


    /* If you like; This does not make sense for two many columns!
    Dimension size = table.getPreferredScrollableViewportSize();

    size.width = totalWidth;

    table.setPreferredScrollableViewportSize(size);
    */

    // table.sizeColumnsToFit(-1); <1.3; possibly even table.revalidate()

    // if (header != null)
    //     header.repaint(); only makes sense when the header is visible (only <1.3)
}

(C) 2001-2009 Christian Kaufhold (swing@chka.de)