Home > java, swing > JSplitPainInTheAss: A less abominable fix for setDividerLocation

JSplitPainInTheAss: A less abominable fix for setDividerLocation

June 12th, 2011

Update: Some discussion of this idea and code updates are in the gist here. Hubris.

If you use Swing much there will come a time when you’d like to create a splitter, aka JSplitPane. Overall, it works fine, but it has one lame, lame, lame feature; if you want to set the divider’s initial position proportionally, you’re screwed. I say feature, because the documentation for JSplitPane.setDividerLocation clearly states that it won’t work if the component hasn’t been “displayed” yet.

I bet that 95% of the time the programmer using this function is calling it at start-up before anything is displayed. Way to cover the 5% case there.

Anyway, you’ll go to Google and try to find a solution to this problem and you’ll find a lot of discussion and many variations on the same workaround. But they inevitably rely on sub-classing (isn’t that the solution to everything in Swing?) and hacking in some logic to fix things right before the splitter gets painted the first time. Nasty.

I’ve been thinking about this in terms of Seesaw, a Clojure/Swing API I’ve been working on. I want setDividerLocation to cover the 95% case and I don’t want to sub-class anything if I can help it. It’s not composable. So I thought about it a bit and came up with a much more elegant solution that works on a vanilla JSplitPane. Since I think this will be much more interesting to the Java Swing developers toiling away out there, I’ve coded it in pure Java. Here it is, a simple function:

If you can’t see any code, look here.

By the way, this code is insanely shorter in Clojure. Just sayin’.

The basic idea is to keep putting off the call to setDividerLocation using invokeLater until the splitter is realized on the screen. No fuss, no muss. … strike that. The invokeLater approach did indeed work, but under certain circumstances, a split pane that wasn’t displayed very soon would result in an infinite cascade of invokeLater events. The main problem with this is that the processor never gets to sleep, etc, etc. So I came up with an alternate approach which takes advantage of a HierarchyChange event to detect when the split pane is displayed (learn something new every day) optionally followed by a resize listener because the hierarchy event may arrive before the split pane’s been laid out by its parent. Swing’s exhausting.

Cheers!

java, swing , ,

  1. June 14th, 2011 at 08:44 | #1

    Thanks! This was exactly what I needed for a Scala application, so I did a direct translation of your code to Scala:

    http://github.com/kjellwinblad/ScalaEdit/blob/master/src/main/scala/me/winsh/scalaedit/gui/SwingHelper.scala#L12

  2. June 14th, 2011 at 09:27 | #2

    @Kjell
    I’m glad you found it helpful. If you find any problems or corner cases, please let me know. It seems to be working in my apps, but Swing is always finding new ways to cause trouble.

  3. September 30th, 2011 at 09:07 | #3

    Excellent, thanks very much. This was driving me round the bend.

  4. Dominique
    March 2nd, 2012 at 05:50 | #4

    This has been driving me up the walls several times.
    Thanks a million!

  5. Antonino
    May 27th, 2012 at 02:47 | #5

    thanks a lot !!

  6. Max
    June 12th, 2012 at 00:11 | #6

    thanks!!

  7. Rolf
    December 21st, 2012 at 01:11 | #7

    You should use setResizeWeight(double value)

  8. Toto
    October 7th, 2013 at 09:26 | #8

    Works like a charm, thank you !

  9. Goffredo
    March 22nd, 2014 at 07:27 | #9

    Excellent Dave, thanks, just the solution I needed. Owe you a beer! ;)

  1. No trackbacks yet.