• Post Reply Bookmark Topic Watch Topic
  • New Topic

Tandem Scrolling of JTables

 
Stevens Miller
Bartender
Posts: 1422
29
C++ Java Netbeans IDE Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I would like to have one horizontal scrollbar control the scrolling of two JScrollPane objects (each of which contains a JTable with similar models).

The line of code that I hoped would give me what I want is this:

It does exactly what I want, except that the scrollbar appears on-screen in the spUpper JScrollPane, while the spLower JScrollPane no longer displays a scrollbar at all. That's the opposite of what I expected. Apparently, spUpper's setHorizontalScrollBar is "stealing" spLower's scrollbar.

An alternative is just to have each scrollbar use the same model, like this:

This links the two scrollbars such that either will control both JScrollPanes (and also the other scrollbar), but it leaves them both visible, which is not what I want. I would have thought this line would make one of them invisible:


However, it seems to have no effect.

The full code using the first approach is below. My questions are these:
  1. Am I correct that setHorizontalScrollBar not only sets which JScrollBar will control a JScrollPane, but also determines where it appears on the screen?
  2. If so, what happens to the original JScrollBar? Is it replaced or am I drawing one on top of the other (or something else)?
  3. Why doesn't setVisible(false) make the scrollbar disappear (and how can I do that)?




 
Tony Docherty
Saloon Keeper
Posts: 3142
72
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Am I correct that setHorizontalScrollBar not only sets which JScrollBar will control a JScrollPane, but also determines where it appears on the screen?

Yes and a Swing component knows were its location is on the screen and it can't be in two places so it moves to the new location.
If so, what happens to the original JScrollBar? Is it replaced or am I drawing one on top of the other (or something else)?

It moves to the location specified by the component you have added it to.
Why doesn't setVisible(false) make the scrollbar disappear (and how can I do that)?

Have you tried setting the scroll panes horizontal scroll bar policy to HORIZONTAL_SCROLLBAR_NEVER
 
Stevens Miller
Bartender
Posts: 1422
29
C++ Java Netbeans IDE Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Tony Docherty wrote:
If so, what happens to the original JScrollBar? Is it replaced or am I drawing one on top of the other (or something else)?

It moves to the location specified by the component you have added it to.

That explains why the scrollbar appears where it does, but I wasn't clear: I meant to be asking about the fate of the one it replaces. Does it co-exist, or, assuming nothing is still holding a reference to it, does it just die and wait for the GC to get it?
Why doesn't setVisible(false) make the scrollbar disappear (and how can I do that)?

Have you tried setting the scroll panes horizontal scroll bar policy to HORIZONTAL_SCROLLBAR_NEVER

Ah, that works! I assume, from that, that the JScrollPane's policy setting is causing it to call the JScrollbar's setVisible() method when it wants it shown, thus clobbering the effect of my own call.

Thanks for the help.
 
Tony Docherty
Saloon Keeper
Posts: 3142
72
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Stevens Miller wrote:That explains why the scrollbar appears where it does, but I wasn't clear: I meant to be asking about the fate of the one it replaces. Does it co-exist, or, assuming nothing is still holding a reference to it, does it just die and wait for the GC to get it?

I would imagine if you set a new scrollbar the reference to the old one is dropped and so if nothing else references it it will be garbage collected. I would also imagine (I haven't looked at the source code to see) before dropping the reference to the original one all listeners are removed, it's set to not visible etc etc.

BTW You might be able to add a listener to the scrollbar and then manually set the position of the other list's enclosing scrollpane's viewport so they both scroll in sync.
 
Stevens Miller
Bartender
Posts: 1422
29
C++ Java Netbeans IDE Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Tony Docherty wrote:You might be able to add a listener to the scrollbar and then manually set the position of the other list's enclosing scrollpane's viewport so they both scroll in sync.

I saw that suggestion in a few places while Googling for an answer before posting here. It certainly seems reasonable and has the advantage that the various models used by both JTables are decoupled. The shared-scrollbar approach would probably fall apart if the two tables had, say, different column models. In my case I know both tables will have identical column models, identical cell-renderers, and so on. I'm coping with a problem it seems lots of others have addressed as well, which is keeping a row of cells locked at the top of a sort of spreadsheet, while all the rows below it are allowed to scroll. I found a few approaches based on having the scrollbars share a model (as I mentioned in my original post), some suggestions that one could use listeners (as you've suggested), and some others that put the locked stuff in the JScrollPane borders. Trouble was, pretty much everything I found was either more general in what it could do than I need (and was therefore more complicated than I wanted it to be), or seemed to address some other need than I really have.

Now, one approach I never found, but experimented with myself, was using a custom header renderer in the JTable. One can actually just use an instance of the same custom cell renderer one might be using in the body of the JTable. When called to render the header, it just gets passed a row index of -1. The JTable header never scrolls vertically, so it matched my need.

However... by experimenting, I discovered that the occurrence of calls to the header renderer's getTableCellRendererComponent method (which serves as a sort of quasi-factory method called by the JTable, I think) far outnumber calls to that method for actual cells. Both headers and actual cells have their renderers' paint methods called comparable numbers of times, so, if you do your heavy lifting there, the overhead of all those calls to getTableCellRendererComponent is probably negligible. But, I would prefer the option to do some preparatory work in that method before paint is actually called. (Also, it's just kind of disturbing that all those calls seem to be made for no reason. I'd be curious if anyone could explain their purpose to me.)

Looking ahead, notwithstanding what I said about keeping it simple, I actually might want to lock more than one row, which would be a nightmare to implement in just the header row. So, the setHorizontalScrollbar(GetHorizontalScrollbar()) trick started looking pretty good. It's just that I hadn't actually seen that one online before, and that always makes me wonder why I'm so smart and everyone else is so dumb (translation: Why am I not seeing the blazingly obvious reason that this is a doomed approach that no one else ever uses?). However, at the moment, it actually looks effective. If anyone sees a flaw in it, please tell me.
 
Tony Docherty
Saloon Keeper
Posts: 3142
72
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I've had a quick look at the scroll pane code and the method that does the work is:


This is called by the ChangeListener that is added to the scroll bar's model. So if you add your own ChangeListener to the model and run the above code passing in the viewport of the other scroll pane it could just do what you want. Where it would become a little bit tricky is if the two scroll panes are different widths as you would have to scale the amount to move. Note the above code is complicated by the fact that it handles both left to right and right to left orientations, if you know you are always going to be left to right you can drop the else clause.
 
Stevens Miller
Bartender
Posts: 1422
29
C++ Java Netbeans IDE Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hey, thanks! That would be a good place to cope with tables of different topologies. I was actually just debugging a control I'm working on that uses these linked tables, and realize my bug was entirely due to the fact that I had let one table grow to a different width than its partner. Might have to return to the listener option if that turns out to be a bug I can't make stay dead.
 
Tony Docherty
Saloon Keeper
Posts: 3142
72
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
This intrigued me so I've knocked up a quick demo. In the demo I've used JLabels with text for ease but the code should handle any component.
Sorry there are no comments but it should be fairly obvious how it works.
The SplitScrollPane class uses a JSplitPane to create areas for top and bottom components and of course this has the added advantage of you being able to drag the split bar to see more (vertically) of one of the components. If the components being added are JScrollPanes the horizontal bar is disabled else the components are placed in JScrollPanes.


Note: this is only a demo, may contain bugs and certainly isn't production standard code. I've also ignored right to left orientation for ease.
 
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!