Create a gist now

Instantly share code, notes, and snippets.

Workaround JSplitPane.setDividerLocation without resorting to sub-classing and other abominations.
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JSplitPane;
import javax.swing.SwingUtilities;
public class JSplitPainInTheAss {
public static JSplitPane setDividerLocation(final JSplitPane splitter,
final double proportion) {
if (splitter.isShowing()) {
if(splitter.getWidth() > 0 && splitter.getHeight() > 0) {
splitter.setDividerLocation(proportion);
}
else {
splitter.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent ce) {
splitter.removeComponentListener(this);
setDividerLocation(splitter, proportion);
}
});
}
}
else {
splitter.addHierarchyListener(new HierarchyListener() {
@Override
public void hierarchyChanged(HierarchyEvent e) {
if((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0 &&
splitter.isShowing()) {
splitter.removeHierarchyListener(this);
setDividerLocation(splitter, proportion);
}
}
});
}
return splitter;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
final JFrame frame = new JFrame("JSplitPainInTheAss");
final JSplitPane splitter = new JSplitPane(JSplitPane.VERTICAL_SPLIT, new JLabel("TOP"), new JLabel("BOTTOM"));
setDividerLocation(splitter, 0.75);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(splitter);
frame.setSize(800, 600);
frame.setVisible(true);
}});
}
}
@grkuntzmd

If you know the containing JFrame or JDialog at the time you want to set the divider location, I believe you can use "addComponentListener(...)" on the JFrame/JDialog and implement the "componentShown" method and have it set the divider location.

@daveray
Owner
daveray commented Jun 13, 2011

That's true, but I usually don't know the top-level container, at least for larger apps, or in "declarative" contexts like Seesaw. If there was a callback for when a component becomes "displayable" that would be ideal, short of setDividerLocation just working as you'd expect in the first place :)

@grkuntzmd

It would be interesting to see how many times the "invokeLater" actually runs before it is "effective". Almost like spin-waiting.

I agree that some way to set the divider location so that it takes effect at display time would be nice. Swing has lots of these "gotchas". Setting the cursor position in a JTextComponent subclass is also a similar problem. And "addComponentListener(...)" does not work on non-top-level containers because Swing does not generate an event when they are shown.

@daveray
Owner
daveray commented Jun 13, 2011

I've just started testing it on a much much larger example. As in all Swing workaround, I've already had to add some additional hacks (the width and height checks) :)

It appears to retry twice in this larger system, but I could imagine a case where it ends up spinning if the splitpane is created but not displayed soon. It seems to cover the common case so far though. I'll keep updating this gist as I learn more...

@daveray
Owner
daveray commented Jun 13, 2011

Actually, it turns out that a HierarchyChangeListener is notified when the splitter becomes displayable. That combined with a temporary component resize listener seems to also work without having to worry about spamming the UI thread with a lot of invokeLaters. I'll experiment some more and update as needed.

@grkuntzmd

Cool! I'll have to see if that works for the JTextComponent subclasses. Check this out.

@daveray
Owner
daveray commented Jun 13, 2011

Yep. That works. In my larger test case, it doesn't work with the single event handler. In my case, the split pane is created one the fly and inserted dynamically into an already visible pane. In this case, the showing-changed event happens before the split pane is laid out in its parent so width and height are still zero. I have to listen for the following resize before setting the divider location. It's progress though :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment