Search code examples
javaswingjtable

How to wrap lines in a jtable cell?


I'm trying to implement a custom TableRenderer as described in this tutorial. I'd like to have the renderer line-wrap each text that is to long for the given cell. The idea is, to use a TextArea as renderer, as it supports line wrapping. However, the following code does not behave as expected:

public class LineWrapCellRenderer  extends JTextArea implements TableCellRenderer {

    @Override
    public Component getTableCellRendererComponent(
            JTable table,
            Object value,
            boolean isSelected,
            boolean hasFocus,
            int row,
            int column) {
        this.setText((String)value);
        this.setWrapStyleWord(true);            
        this.setLineWrap(true);         
        return this;
    }

}

I set this renderer with

table.setDefaultRenderer(String.class, new LineWrapCellRenderer());

But the cell entries stay unwrapped. If I add this.setBackground(Color.YELLOW) to the getTableCellRendererComponent() method, all cells are yellow as expected, but not wrapped.

Any ideas?

UPDATE: As Michael Borgwardt stated in the comments, the problem is not the line wrap, but the row height: JTables rows are fixed size, so if a cell is getting higher (cause the text is now multi-lined), we have to increase the row height. But how much? I will check if this is worth another SO-question. If not, I will add this solution here.

Update2: The following code will determine the row height (if placed in getTableCellRendererComponent()):

int fontHeight = this.getFontMetrics(this.getFont()).getHeight();
int textLength = this.getText().length();
int lines = textLength / this.getColumns() +1;//+1, cause we need at least 1 row.           
int height = fontHeight * lines;            
table.setRowHeight(row, height);

Solution

  • The problem is that the height of rows in JTable is fixed, so it's not just a matter of having a renderer that wraps; I'm not sure why it doesn't, but if it did, the wrapped text would be cropped - or maybe that's exactly what you're seeing. To adjust row heights, you need to set them individually.

    Heres' some code for that:

    int rows = 10;
    int cols = 5;
    JTable table = new JTable(rows, cols);
    
    // Set the 1st row to 60 pixels high
    table.setRowHeight(0, 60);
    
    // Set the height of all rows to 32 pixels high,
    // regardless if any heights were assigned to particular rows
    table.setRowHeight(32);
    // the height of the 1st row is set to 32 pixels high
    
    // Returns the preferred height of a row.
    // The result is equal to the tallest cell in the row.
    public int getPreferredRowHeight(JTable table, int rowIndex, int margin) {
        // Get the current default height for all rows
        int height = table.getRowHeight();
    
        // Determine highest cell in the row
        for (int c=0; c<table.getColumnCount(); c++) {
            TableCellRenderer renderer = table.getCellRenderer(rowIndex, c);
            Component comp = table.prepareRenderer(renderer, rowIndex, c);
            int h = comp.getPreferredSize().height + 2*margin;
            height = Math.max(height, h);
        }
        return height;
    }
    
    // The height of each row is set to the preferred height of the
    // tallest cell in that row.
    public void packRows(JTable table, int margin) {
        packRows(table, 0, table.getRowCount(), margin);
    }
    
    // For each row >= start and < end, the height of a
    // row is set to the preferred height of the tallest cell
    // in that row.
    public void packRows(JTable table, int start, int end, int margin) {
        for (int r=0; r<table.getRowCount(); r++) {
            // Get the preferred height
            int h = getPreferredRowHeight(table, r, margin);
    
            // Now set the row height using the preferred height
            if (table.getRowHeight(r) != h) {
                table.setRowHeight(r, h);
            }
        }
    }