An invalid swing component height is required

The basic setting is as follows: I have a vertical jsplitpane. I want a fixed size bottom component and a resized top component. I complete it by calling setresizeweight (1.0) In this application, there is a button to restore the "default" window configuration The default height of the window is the desktop height, and the default separator position is 100 pixels from the bottom of the split pane

To set the separator position to 100px, I'll use jsplitpane height – 100 The problem is that I resized the JFrame before, and because the code is in the button callback, the jsplitpane has expired but has not been resized Therefore, the distributor position is set incorrectly

This is an sscce Click the button twice to view the problem The first click resizes the window, but the separator position remains the same (relative to the bottom of the window) The second click moves the separator correctly because the window size does not change

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GraphicsConfiguration;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;

import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JSplitPane;

public class SSCCE {

    /**
     * @param args unused
     */
    public static void main(String[] args) {
        new SSCCE();
    }

    private final JFrame f = new JFrame("JSplitPane SSCE");
    private final JSplitPane sp = new JSplitPane(JSplitPane.VERTICAL_SPLIT,true);

    public SSCCE() {
        f.setDefaultCloSEOperation(JFrame.EXIT_ON_CLOSE);

        sp.add(new JLabel("top"));
        sp.add(new JLabel("bottom"));
        sp.setResizeWeight(1.0);

        f.getContentPane().add(sp);
        f.getContentPane().add(new JButton(new AbstractAction("Resize to Default") {
            @Override
            public void actionPerformed(ActionEvent e) {
                restoreDefaults();
            }
        }),BorderLayout.PAGE_END);

        f.setSize(400,300);
        f.setVisible(true);
    }

    void restoreDefaults() {
        f.setSize(f.getWidth(),getDesktopRect(f.getGraphicsConfiguration()).height);
        sp.setDividerLocation(sp.getSize().height - 100);  // Does not work on first button press
    }

    Rectangle getDesktopRect(GraphicsConfiguration gc) {
        Toolkit toolkit = Toolkit.getDefaultToolkit();
        Dimension size = toolkit.getScreenSize();
        Insets insets = toolkit.getScreenInsets(gc);
        return new Rectangle(insets.left,insets.top,size.width - (insets.left + insets.right),size.height - (insets.top + insets.bottom));
    }

}

I thought of some possible ways to solve this problem, but they all look like a hackish So far, my best idea is to call f. validate() between setting the frame size and setting the separator position, but I'm worried about the side effects of early forced validation

Another option I came up with was to use eventqueue Invoker () to set the delimiter position at the end of the event queue But it seems risky to me - I assume that jsplitpane will be verified at that time, and I worry that this may be a wrong assumption

Is there a better way?

Solution

It took a while (probably because of the early morning: -) to understand the problem, so just to make sure I got it:

>The size of the bottom component can always be any size determined by the user > when the frame is resized, all height changes of the top component should occur > you can choose to restore the default size regardless of any previous Settings > "default" means that the bottom component must have a fixed height XX

If so, the solution is to separate the frame sizing from the bottom part Your second choice is dead: resize the frame and wrap the bottom comp resize into an invoker (eventqueue or swing utilities, it doesn't matter)

void restoreDefaults() {
    f.setSize(f.getWidth(),getDesktopRect(f.getGraphicsConfiguration()).height);
    SwingUtilities.invokelater(new Runnable() {
        public void run() {
            sp.setDividerLocation(sp.getSize().height - 100);  

        }
    });
}

This ensures that it works as expected because the invoker puts the request last after all queued events:

/**
 * Causes <i>doRun.run()</i> to be executed asynchronously on the
 * AWT event dispatching thread.  This will happen after all
 * pending AWT events have been processed.  [...]
 * If invokelater is called from the event dispatching thread --
 * for example,from a JButton's ActionListener -- the <i>doRun.run()</i> will
 * still be deferred until all pending events have been processed.
The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>