tags:

views:

675

answers:

3

Apologies for the somewhat unclear question - couldn't think of a better way of putting it.

I use a JXTaskPane (from the Swing labs extension API) to display some information.

The user can "click" the title to expand the panel. The JXTaskPane is in a container JPanel, which is then added to a JFrame, my main application window.

I want my application window to resize to the size of the expanded task pane. To achieve this, I added a component listener to my container JPanel which would set size to the now expanded panel.

panel.addComponentListener(new ComponentListener()
{   
    public void componentResized(ComponentEvent e)
    {
        Dimension newSize = ((JXTaskPane)e.getSource()).getSize();
        reSizeFrame(newSize);
    }
}

private void reSizeFrame(Dimension newSize)
{   
    if ((newSize.height < maxSize.height) && (newSize.width < maxSize.width))
    {
        containerPanel.setSize(newSize);
        appFrame.setSize(containerPanel.getSize());
        appFrame.pack();
    }
}

The problem is that the componentResized method is called as the task pane expands, as a result the resizeFrame method is called lots of times, and looks really awful on the screen.

How can I detect when the JXTaskpane has finished resizing? I thought of two approaches:

  • Put the resizeFrame() method in a SwingUtilities.invokeLate(..) call.

  • Put in a timer resizeFrame call, so any subsequent calls do not do anything until the timer fires. This should give enough time for the panel to resize.

What is the best way forward?

Also - This is my first serious Java GUI app after years of server side program. StackOverflow has been very helpful. So thanks!

+1  A: 

As a suggestion, have you tried overriding the paint method, first calling super and then putting your resize code at the end of that if (and only if) the size has changed significantly.

James B
James - thanks. I will give this a try. Presumably, I would override the paint method on the JFrame?
Luhar
Yes, that's what I think, or in the JPanel (extending the JPanel would give you a bit more control over how things are set out on it anyway and it tends to be how I construct my GUIs, although I can't profess to be an expert). Hope it goes well for you!
James B
Agree w/ Gonzo, overriding paint() in this way is not recommended
Matt McHenry
A: 

I'm not familiar with JXTaskPane, but my first reaction is that maybe you're handling the wrong event. You want the frame to resize when the user clicks on the header - so why not handle that event (perhaps using EventQueue.invokeLater() to resize the frame after the task pane has been resized)?

But if that doesn't work and you need to use the approach you've outlined above, using a javax.swing.Timer is probably best. Set it for 200 milliseconds or so and just restart() it every time componentResized() fires.

+3  A: 

I know you've already selected an answer, but overriding the paint method is definitely not correct, and while you may be able to hack something in place, it won't be ideal.

Looking at the source for JXTaskPane and specifically looking in setExpanded() (line 387), you can see it calls JXCollapsiblePane.setCollapsed(...) and then fires a property change event for expanded. A listener on that property won't be correct, because it'll fire before the animation is complete. So, if you go into JXCollapsiblePane and look at setCollapsed(...) (line 470) you'll see that if it's animated, it sets the paramaters and starts a timer. We want to know when the animation ends, so in that file, look at the animator (line 620, and specifically 652-667), which shows that when the animation ends, it fires a property change for ANIMATION_STATE_KEY with a value of "collapsed" or "expanded". This is the event you actually want. However, you don't have access to JXCollapsiblePane, so go back to JXTaskPane and search for ANIMATION_STATE_KEY, and you find line 208, which shows that JXTaskPane creates a listener on JXCollapsiblePane.ANIMATION_STATE_KEY and refires it as it's own event.

Since you do have access to JXTaskPane, you can listen for that event, so doing ...

taskPane.addPropertyChangeListener(JXCollapsiblePane.ANIMATION_STATE_KEY, new PropertyChangeListener() {
  public void propertyChange(PropertyChangeEvent e) {
    if(e.getNewValue().equals("expanded") {
      ...
    }
    else if(e.getNewValue().equals("collapsed") {
      ...
    }
  }
}

should get your event exactly when you want it.

The correct way to listen for events in Swing is through property listeners. Unfortunately, the only way to find out what the correct properties and values are is by digging through source code.

Milan Ramaiya
Fiar enuffski, I said I wasn't an expert, glad there is a less hacky way to do what the OP needs
James B