0

Basically: my JTable wants special row-heights for each row. Each renderer used writes its requested row-height for each cell to a map in the table, the table has a method updateRowHeights which uses the map to set the row-heights. The problem is that the map with row-heights is only filled with heights by the renderer. Hence, the first time the table is rendered the map is still empty. Therefore, when the table is displayed the first time the row-heights are not set.

As a not-so-pretty workaround I have added a component listener to my table which updates the row-heights. This works fine most of the time, but for example it is not called when the table lives in a scrollpane inside a tabbedpane. Preferably I would have a method onRenderingFinished or something like that, in which I can call the row-heights method.

I am doing something like in the first answer of How to set the RowHeight dynamically in a JTable. There it is mentioned that you should not change the rowheights of your table from within the renderer (I agree with that) and that "you set it (call updateRowHeight) once after the initialization is completed and whenever any of the state it depends on is changed."

So my question is: where/when do I call updateRowHeight?

Is there an event that tells me the renderer is finished?

Community
  • 1
  • 1
Nieke Aerts
  • 209
  • 2
  • 11

1 Answers1

2

If you only want to update table row height depended on the highest cell height, you only need to call this method after the table content is set.

public static void updateRowHeight(JTable table, int margin) {
    final int rowCount = table.getRowCount();
    final int colCount = table.getColumnCount();
    for (int i = 0; i < rowCount; i++) {
        int maxHeight = 0;
        for (int j = 0; j < colCount; j++) {
            final TableCellRenderer renderer = table.getCellRenderer(i, j);
            maxHeight = Math.max(maxHeight, table.prepareRenderer(renderer, i, j).getPreferredSize().height);
        }
        table.setRowHeight(i, maxHeight + margin);
    }
}

Margin is optional parameter to provide additional space for each row (may be 0 or positive).

Please note: this method can provoke performance problems when table is very large and/or contains lots of HTML strings.

Sergiy Medvynskyy
  • 11,160
  • 1
  • 32
  • 48
  • This is a nice workaround. However, working with JTextArea as a base (instead of JLabel) the getPreferredSize() method does not work as expected and the method I use instead is not very fast (on a 20000 row table the difference is a few seconds). Secondly, the method updateRowHeight also has to be called after sorting the table by a different column. In my case, updating the rowheights on some componentEvent and pre-calculating the heights in the renderer is _much_ faster. (it is ugly though... and when cells are changeable the componentEvent way becomes unusable) – Nieke Aerts Jan 03 '17 at 12:29
  • Also, there are so many places where this should be invoked if it is not naturally after rendering (e.g., when rows are added, deleted, changed, sorting). As this solution is not very time-efficient it does, unfortunately, not solve my problem (not even for the case where there is no JTextArea cell). – Nieke Aerts Jan 03 '17 at 15:12
  • Please provide a [SSCCE](http://sscce.org), so I can understand your problem. And of course, with JTextArea this approach cannot work because JTextArea requires at least fixed width to determine its height. You can use HTML labels for small tables or panels with composition of plain text labels for large tables. – Sergiy Medvynskyy Jan 03 '17 at 17:22