Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

How are you supposed to set JTable height?

112 views
Skip to first unread message

Eric Douglas

unread,
May 19, 2017, 10:20:24 AM5/19/17
to
Step 1: start with a test table. I used the one in the answer on this thread:
http://stackoverflow.com/questions/31886010/how-to-show-hide-different-row-sections-in-a-jtable

Step 2: at the end of the main section, set a row height.
table.setRowHeight(2,35);

Run test and see there is a row that is taller.

Step 3: fire change.
((AbstractTableModel) table.getModel()).fireTableDataChanged();

I thought this was supposed to tell the table to repaint because a cell got a different value, but this is losing the row height I just set.

Eric Sosman

unread,
May 19, 2017, 11:22:59 AM5/19/17
to
On 5/19/2017 10:20 AM, Eric Douglas wrote:
> Step 1: start with a test table. I used the one in the answer on this thread:
> http://stackoverflow.com/questions/31886010/how-to-show-hide-different-row-sections-in-a-jtable

Aside: The code at this link is thread-unsafe in that it creates
and manipulates a whole pile of Swing components while not running on
the Event Dispatch Thread (EDT). This could cause trouble ...

... but probably doesn't cause your problem. (Evidence: Changing
things to use EventQueue.invokeLater() doesn't fix it. For me; YMMV.)

> Step 2: at the end of the main section, set a row height.
> table.setRowHeight(2,35);

"At the end" -- before the table is made visible, or after?
(Might be irrelevant: I tried it both ways, with the same outcome.)

> Run test and see there is a row that is taller.
>
> Step 3: fire change.
> ((AbstractTableModel) table.getModel()).fireTableDataChanged();

I did this by adding a "Change data" JButton which fired the event
when clicked.

> I thought this was supposed to tell the table to repaint because a cell got a different value, but this is losing the row height I just set.

JTable is a complicated beast (perhaps a victim of the urge to be
all things to all people), and the documentation is less than splendid.
Sometimes you need to read not only between the lines, but between the
squiggles as well.

So, it's a search for clues. One clue appears in the Javadoc for
fireTableDataChanged (emphasis mine):

"[...] The number of rows may also have changed and the JTable
should redraw the table *from scratch*. [...]"

Putting this hint together with your observation leads to (I think) a
rational explanation: All the old table rows -- with their heights and
background colors and whatnot -- are now junk, and will be replaced with
brand-new rows. "From scratch," as the Javadoc so helpfully puts it.

... which makes a certain kind of sense, I guess. Suppose you've
changed the height of Row 42, and then after fireTableDataChanged the
table discovers that there are now only 17 rows of data. Is the table
supposed to remember "Oh, if the model ever gives me enough more rows,
I must remember to use a special height for Row 42?"

As for what to do about it, it depends on, well, on what you're
trying to do. fireTableDataChanged is a "big hammer" (not as big as
fireTableStructureChanged, but plenty big) that smashes the old table
to fragments and builds a fresh one from the ruins (if you'll pardon
the violence of the imagery). There are other events with more limited
and less drastic effects:

- fireTableRowsUpdated notifies the table that the contents of a
range of rows may have changed. Listeners typically just repaint
those rows, leaving their decorations and whatnot intact.

- fireTableCellUpdated is an even more limited event, reporting
the possible update of one single cell (which gets repainted if it
happens to be visible).

- fireTableRowsDeleted and fireTableRowsInserted do what you'd
expect. I believe they'll leave the appearance of the surviving or
relocated rows unchanged. (Trust, but verify.)

--
eso...@comcast-dot-net.invalid
Thirteen hundred forty-two days to go.

Eric Douglas

unread,
May 19, 2017, 1:45:55 PM5/19/17
to
On Friday, May 19, 2017 at 11:22:59 AM UTC-4, Eric Sosman wrote:
> As for what to do about it, it depends on, well, on what you're
> trying to do. fireTableDataChanged is a "big hammer" (not as big as
> fireTableStructureChanged, but plenty big) that smashes the old table
> to fragments and builds a fresh one from the ruins (if you'll pardon
> the violence of the imagery). There are other events with more limited
> and less drastic effects:
>
> - fireTableRowsUpdated notifies the table that the contents of a
> range of rows may have changed. Listeners typically just repaint
> those rows, leaving their decorations and whatnot intact.
>
> - fireTableCellUpdated is an even more limited event, reporting
> the possible update of one single cell (which gets repainted if it
> happens to be visible).
>
> - fireTableRowsDeleted and fireTableRowsInserted do what you'd
> expect. I believe they'll leave the appearance of the surviving or
> relocated rows unchanged. (Trust, but verify.)
>

So I guess I've been doing it wrong. The hammer approach may not hurt if you're not using anything it smashes, and I normally don't do things like set individual row heights, but I had coded all my methods from deleting a row to changing one cell color to call the TableDataChanged. I don't recall now if there was a reason I did that, if more specific fire methods gave me a problem, or if it just seemed easier. So if anything the smaller hammer would be better, to just call fireTableRowsUpdated(0,table.getRowCount()) if we know the number of rows isn't changing. That should at least fix custom row heights. I'll try getting more specific and see what kind of trouble it brings.

Eric Sosman

unread,
May 19, 2017, 2:29:51 PM5/19/17
to
Sounds plausible (after correcting the off-by-one error). If
you can identify the updating range more closely and wind up firing
a more specific TableModelEvent, you'll also avoid those unnecessary
repaints (and/or visibility determinations) of unchanged rows and
cells.

Not so sure about that "changing one cell color" bit, though.
That's probably not a change the TableModel should be involved in;
rather, I'd suggest using a custom cell renderer (and, in extreme
circumstances, a custom JTable subclass) -- it can make a color
decision based on the cell's content, location, whatever, and make
that decision independently of other tables that could be browsing
the same TableModel for their data. See

http://docs.oracle.com/javase/tutorial/uiswing/components/table.html#renderer

Eric Douglas

unread,
May 19, 2017, 2:43:03 PM5/19/17
to
On Friday, May 19, 2017 at 2:29:51 PM UTC-4, Eric Sosman wrote:
> Sounds plausible (after correcting the off-by-one error). If
> you can identify the updating range more closely and wind up firing
> a more specific TableModelEvent, you'll also avoid those unnecessary
> repaints (and/or visibility determinations) of unchanged rows and
> cells.
>
Right, that should be fireTableRowsUpdated(0,table.getRowCount() - 1) and of course checking for zero row count.

I have been trying to avoid some unnecessary repaints. I set a flag in my custom table that says suspend updates. If a method should fire a notification and the suspend flag is set it skips it, and when the flag is set back to false it fires one, so I can update 1,000 rows with one repaint.

> Not so sure about that "changing one cell color" bit, though.
> That's probably not a change the TableModel should be involved in;
> rather, I'd suggest using a custom cell renderer (and, in extreme
> circumstances, a custom JTable subclass) -- it can make a color
> decision based on the cell's content, location, whatever, and make
> that decision independently of other tables that could be browsing
> the same TableModel for their data. See
>
> http://docs.oracle.com/javase/tutorial/uiswing/components/table.html#renderer
>
I'm testing these more specific methods and this is tedious. I call fireTableCellUpdated and it crashes saying default row sorter reports row out of range. So I have to nail that down or go back to an update all.

Eric Douglas

unread,
May 19, 2017, 4:06:23 PM5/19/17
to
On Friday, May 19, 2017 at 2:43:03 PM UTC-4, Eric Douglas wrote:
> >
> I'm testing these more specific methods and this is tedious. I call fireTableCellUpdated and it crashes saying default row sorter reports row out of range. So I have to nail that down or go back to an update all.

Now that I got that worked out I'm back to the hard part.
I'm trying to make accessible rows disappear. From what I could find searching the internet for this they just say use the RowFilter, but that makes them inaccessible. I don't want the user to be able to see them or edit them, but I want the program to call get and set methods as if they're showing normally.

I hacked the UI to not show them which is the easy part since I already implemented an extension of BasicTableUI for another custom view (cell merge), but now I have to calculate which rows aren't showing, because the built in view from getCellRect is leaving blank space for the invisible rows, so I need to subtract from y for any visible rows the height any invisible row above it should be.

This was easy when all rows were visible, but it appears the getCellRect already discounts all cells which should not be in the viewport? So I have to cascade to subtract any cells which are now invisible which otherwise should have been in the viewport? So...say we have 10 rows and the viewable area of the scrollpane shows 6 and we want only rows 1,3,7,9 to show. Y for row 3 has to subtract row 2, now Y for row 7 has to subtract row 2,4,5,6? Does 7 get a correct row for Y to start since it otherwise wouldn't have been in the area?

Eric Sosman

unread,
May 20, 2017, 7:59:28 AM5/20/17
to
On 5/19/2017 4:06 PM, Eric Douglas wrote:
> On Friday, May 19, 2017 at 2:43:03 PM UTC-4, Eric Douglas wrote:
>>>
>> I'm testing these more specific methods and this is tedious. I call fireTableCellUpdated and it crashes saying default row sorter reports row out of range. So I have to nail that down or go back to an update all.
>
> Now that I got that worked out I'm back to the hard part.
> I'm trying to make accessible rows disappear. From what I could find searching the internet for this they just say use the RowFilter, but that makes them inaccessible. I don't want the user to be able to see them or edit them, but I want the program to call get and set methods as if they're showing normally.

I'm not sure what's going on, because it seems straightforward and
should work as advertised (has worked as advertised, for me). You can
modify the contents of your TableModel without regard to whether the
cells are or aren't visible; "as if they're showing normally." The
JTable listens to its model, and when it receives a change event it
first determines whether the change is to a visible part of the table.
If the changed cells are not visible -- hidden by a RowFilter, or just
scrolled out of sight -- nothing gets repainted.

> I hacked the UI to not show them which is the easy part since I already implemented an extension of BasicTableUI for another custom view [...]

I can't help you with this part, since I've never tried to mess
with the plaf layers. On the other hand, I've never needed to: For
filtering, sorting, and custom-painting rows and cells, I've just
used RowFilter, TableRowSorter, and sometimes a customized
TableCellRenderer or TableCellEditor. These have met my needs
without recourse to mucking about in the lower levels.

Eric Douglas

unread,
May 22, 2017, 7:55:41 AM5/22/17
to
On Saturday, May 20, 2017 at 7:59:28 AM UTC-4, Eric Sosman wrote:
>
> I can't help you with this part, since I've never tried to mess
> with the plaf layers. On the other hand, I've never needed to: For
> filtering, sorting, and custom-painting rows and cells, I've just
> used RowFilter, TableRowSorter, and sometimes a customized
> TableCellRenderer or TableCellEditor. These have met my needs
> without recourse to mucking about in the lower levels.
>

RowFilter doesn't work unless getRowCount() only returns the amount of visible rows.
I want the user to be able to filter on values in one column, but I still want to get and set values in another column including the rows they don't see.

Eric Sosman

unread,
May 22, 2017, 9:00:56 AM5/22/17
to
I don't know which of the several getRowCount() methods you
refer to: That of JTable, or of TableModel, or of any of several
other table-related classes and interfaces. But in any event,
all I can say is that RowFilter and TableRowSorter have worked
just fine without me needing to worry about what's visible and
what's not. It seems probable that you're doing something far
more intricate than I've ever attempted, so I'll have to bow out.


--
eso...@comcast-dot-net.invalid
Thirteen hundred thirty-nine days to go.

Eric Douglas

unread,
May 22, 2017, 9:43:03 AM5/22/17
to
That of JTable, which has to match the filter.
I call methods on the table to get and set values, which makes sense when we want to process only what the user sees as they're seeing them. Normally sort order is not an issue. In this case I want the user to be able to filter so they only see some of the rows, but there is another process linked to the table which needs to get and update all the rows, so either it needs to get the model to work directly with the data which I don't otherwise do, or it needs methods on the table that work with model row values instead of view rows.

Eric Douglas

unread,
May 22, 2017, 12:18:33 PM5/22/17
to
Oh well, JTable doesn't allow zero row height so they don't make it easy. I guess if I can't tell them not to paint certain rows I'll just go with the RowFilter, and a flag on the table telling get and set methods to use model instead of view.

Eric Sosman

unread,
May 22, 2017, 5:15:33 PM5/22/17
to
Setting should surely be done through the model, not through the
table. You then needn't worry about whether the altered cells are or
are not filtered, are or are not scrolled out of the viewport, do or
do not belong to funny-sized rows, do or do not lie in columns that
the user has rearranged, ...

Getting should also probably be done through the model, for all
the same reasons. The only reason I can imagine for wanting to do
it through the table instead is if you need to do something different
with visible vs. non-visible cells.

With Swing components, one should "almost always" manipulate and
query data through the model rather than through the view. Do things
to the ButtonModel and the JButton responds, do things to the ListModel
and the JList responds, ... do things to the TableModel and the JTable
responds. You've revealed none of your code, but I begin to suspect
that much of your trouble arises from trying to work backwards, trying
to make the view govern the model instead of vice-versa.
0 new messages