需要无效的Swing组件的高度 [英] Need the height of an invalidated Swing component

查看:126
本文介绍了需要无效的Swing组件的高度的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

基本设置是这样的:我有一个垂直的JSplitPane,我想要一个固定大小的底部组件和一个调整大小的顶部组件,这是我通过调用setResizeWeight(1.0)完成的.在此应用程序中,有一个按钮可以还原默认"窗口配置.窗口的默认高度是桌面高度,默认的分隔线位置是从拆分窗格底部开始的100像素.

The basic setup is this: I have a vertical JSplitPane that I want to have a fixed-size bottom component and a resizing top component, which I accomplished 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 divider location is 100 pixels from the bottom of the split pane.

要将分隔线位置设置为100px,我将JSplitPane的高度设置为100.问题是,在此之前,我调整了JFrame的大小,并且由于代码位于按钮回调中,因此JSplitPane已无效但尚未调整大小. .因此,分隔线的位置设置不正确.

To set the divider location to 100px, I take the JSplitPane height - 100. The problem is, just before this I resize the JFrame, and since the code is in a button callback, the JSplitPane has been invalidated but not yet resized. So the divider location is set incorrectly.

这是SSCCE.单击两次按钮以查看问题.第一次单击将调整窗口的大小,但分隔线的位置保持不变(相对于窗口底部).第二次单击可以正确移动分隔线,因为窗口大小没有变化.

Here is a SSCCE. Click the button twice to see the problem. The first click will resize the window, but the divider location remains the same (relative to the bottom of the window). The second click properly moves the divider, since the window size didn't 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));
    }

}

我想到了一些解决方法,但它们似乎都有些破烂.到目前为止,我最好的主意是在设置帧大小和设置分隔线位置之间调用f.validate(),但是我担心强制过早进行验证可能会有副作用.

I have thought of a few ways I might get around this, but they all seem sort of hackish. So far the best idea I've had has been to call f.validate() in between setting the frame size and setting the divider location, but I'm concerned there might be side effects to forcing validation early.

我想到的另一个选项是使用EventQueue.invokeLater()将调用设置为在事件队列的末尾设置除法器位置.但这对我来说似乎很冒险-我假设JSplitPane将在那时进行验证,并且我担心这可能是一个错误的假设.

The other option I thought of is to use EventQueue.invokeLater() to put the call to set the divider location at the end of the event queue. But that seems risky to me - I'm assuming the JSplitPane will have been validated at that point, and I'm concerned that may be a faulty assumption to make.

有更好的方法吗?

推荐答案

花点时间(可能是因为这里很早:-)来了解问题所在,所以请确保我明白了:

Took a while (probably due to being early morning here :-) to understand the problem, so just to make sure I got it:

  • 底部组件的大小可以是用户始终决定的大小
  • 调整框架大小时,所有高度变化都应发生在顶部组件上
  • 有一个还原为默认大小的选项,与之前的任何设置无关
  • 默认"表示底部组件的高度必须固定为xx

如果是这样,解决方案是将框架调整大小与底部组件的大小分开.您的第二个选择无效:重新调整框架大小并将底部comp调整大小包装到invokeLater中(EventQueue或SwingUtilities无关紧要).

If so, the solution is to separate the frame resizing from the sizing the bottom component. Your second option is dead on: resize the frame and wrap the bottom comp resize into a invokeLater (EventQueue or SwingUtilities, 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);  

        }
    });
}

保证可以按预期工作,因为invokeLater在所有已排队的事件之后将请求放到最后:

That's guaranteed to work as expected, because the invokeLater puts the request as last after all already 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.

这篇关于需要无效的Swing组件的高度的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆