JPanel的子组件绘制/布局问题

时间:2010-03-10 17:15:18

标签: java user-interface swing layout rendering

我遇到的问题是,当我的框架显示时(登录对话框后)按钮不在正确的位置,然后在几毫秒内它们会到达正确的位置(带边框布局的面板中心)。

- 更新

在我的机器中,这个SSCCE在我运行它的10次中显示了布局问题:

import java.awt.BorderLayout;
import java.awt.Dimension;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class TEST {

    public static void main(String[] args) throws Exception {

    SwingUtilities.invokeAndWait(new Runnable() {

        public void run() {

        System.out.println("Debug test...");

        JPanel btnPnl = new JPanel();
        btnPnl.add(new JButton("TEST"));

        JFrame f = new JFrame("TEST");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.getContentPane().setLayout(new BorderLayout());
        f.getContentPane().add(btnPnl);
        f.setPreferredSize(new Dimension(800, 600));
        f.pack();
        f.setExtendedState(JFrame.MAXIMIZED_BOTH);
        f.setVisible(true);

        System.out.println("End debug test!");

        }
    });

    }

}

按钮首先出现在左上角,然后进入中心。这是一个java bug吗?

- 更新

看起来SSCCE没有为正在尝试的每个人显示问题。 也许这是我的电脑性能问题。我仍然认为Java Swing正在创建用于在幕后进行布局的新线程。但我不确定。

- 更新

问题只发生在f.setExtendedState(JFrame.MAXIMIZED_BOTH);

8 个答案:

答案 0 :(得分:2)

你的问题引起了我的兴趣。经过一些调查后,我想我确认了一些我记得设置窗口状态(最大化,恢复等)的东西,即设置状态是对操作系统的请求,并留给操作系统处理请求的一时兴起。这意味着它在设置后是异步的,或者至少在稍后完成。我确认使用日志记录并添加调整大小的侦听器,您可以看到在您的代码块退出后调整框架的大小。因此,pack()会将组件布局为其首选大小。因此,想象一下框架的尺寸为800x600,组件的位置如此(按钮水平居中于400左右)。然后,操作系统将帧的大小更改为全屏(例如1024x768) - 片刻,您​​将看到按钮仍为400.然后帧处理新的大小并重新布置组件并使按钮居中在512左右。所以你会看到在此过程中转换的闪烁。也许解决方案是不打包() - 它将保持零大小,用户将看到最小闪烁。

首先尝试此更改:

// pack()

如果看起来不错,那么您可能会遇到下一个问题......如果用户点击恢复按钮,则整个框架会缩小为黑洞。因此,尝试调整包后帧由于最大化而可预测地调整大小。像这样:

f.addComponentListener( new ComponentAdapter( ComponentEvent e ) {
  public void componentResized( Component) {
     if( f.getSize().getWidth() > 0 ) {
        e.getComponent().removeComponentListener( this );
        ((JFrame)e.getComponent()).pack();
     }
  }
}

因此,如果用户稍后单击“恢复”按钮,则框架将具有准备好的打包尺寸。

<强> - 更新

好的,最后一次尝试。虽然我认为我对问题的描述有一些道理,但我提供的解决方案却没有做任何事情。这是最后一次尝试。删除pack()和setPreferredSize()并替换为将大小设置为屏幕大小。这似乎可以减少我的系统上的闪烁。这是因为初始布局和稍后完成的最大化布局之间应该没有区别。如果在还原和最大化之间切换,则可以看到此信息。虽然在切换两者时我仍然看到一个非常轻微的闪烁,但至少在首次显示时它看起来更好。

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Toolkit;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class TEST {

    public static void main(String[] args) throws Exception {

    SwingUtilities.invokeAndWait(new Runnable() {

        public void run() {

        System.out.println("Debug test...");

        JPanel btnPnl = new JPanel();
        btnPnl.add(new JButton("TEST"));

        JFrame f = new JFrame("TEST");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.getContentPane().setLayout(new BorderLayout());
        f.getContentPane().add(btnPnl);
//        f.setPreferredSize(new Dimension(800, 600));
//        f.pack();
        f.setSize( Toolkit.getDefaultToolkit().getScreenSize() );
        f.setExtendedState(JFrame.MAXIMIZED_BOTH);
        f.setVisible(true);

        System.out.println("End debug test!");

        }
    });

-Mike

答案 1 :(得分:1)

我猜你需要在让你的框架可见之前调用pack()。

如果您在事件线程上调用上述代码而不是,那么您就有竞争条件并且所有投注都已关闭 - 您只能从EDT操作GUI(事件调度线程)。 / p>


编辑:我在我的系统上尝试过你的SSCCE而且没有表现出你所看到的行为。我尝试了大约50次,并尝试通过循环代码创建10个窗口。我在Windows XP SP3上运行1.6.0_18。

答案 2 :(得分:1)

也许您错过了frameThatContainsCentralPanel.pack()

答案 3 :(得分:1)

好吧,如果它适用于SSCCE,那么你已经证明问题不在于基本逻辑。 SSCCE和您的真实代码之间必定存在不同之处。由于我们无法访问您的真实代码,因此您需要自己进行调试以了解其中的差异。

但是,在这种情况下,更好的解决方案是使用CardLayout,它旨在让您轻松交换面板。阅读Swing教程以获取一个工作示例。

或者另一种方法是使用“登录对话框”。登录成功后,您将使用适合您应用的面板显示主框架。

答案 4 :(得分:1)

“然后在几毫秒内”部分听起来像是你需要在你的框架上调用validate()。此外,如果您使用f.pack(),则您的面板需要首选尺寸,因为pack()会为父级的组件提供首选尺寸,并根据它们调整大小。

答案 5 :(得分:0)

如果我复制了你的代码,我遇到了同样的问题,但不是很重。

我通过在包装前为您的框架设置首选尺寸来解决它。所以:

import java.awt.Dimension;

System.out.println("Debug test...");

JPanel btnPnl = new JPanel();
btnPnl.add(new JButton("TEST"));

JFrame f = new JFrame("TEST");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().setLayout(new BorderLayout());
f.getContentPane().add(btnPnl);
f.setPreferredSize(new Dimension(800, 600));
f.pack();
f.setVisible(true);
System.out.println("End debug test!");

我在Linux上运行。

这确实很奇怪......我确信它与秋千树中所有容器的大小有关。

答案 6 :(得分:0)

我希望框架在显示之前最大化,但在检查之后我确信在显示后,linux框架上的框架最大化。在调用setVisible之前,可以使帧大小等于屏幕大小,或者可以使组件不可见,直到您知道它具有首选的初始大小。这是修改后的示例,它显示了帧激活后的元素(在linux激活的事件来得太晚,不能显示“跳跃按钮”):

import javax.swing.JButton;

import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.SwingUtilities;

公共课TEST {

public static void main(String[] args) throws Exception {

    SwingUtilities.invokeAndWait(new Runnable() {

        public void run() {

            final JPanel btnPnl = new JPanel();
            btnPnl.add(new JButton("TEST"));

            final JFrame f = new JFrame("TEST");
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.setContentPane(btnPnl);
            // calculate preferred size for TEST frame
            // f.isDisplayable() will become true
            f.pack();

            // extended state, if can be applied, needs to be called after f.isDisplayable()
            WindowListener maxBoth = new WindowAdapter() {

                @Override
                public void windowOpened(WindowEvent e) {
                    f.setExtendedState(Frame.MAXIMIZED_BOTH);
                }
            };

            // after windows has been opened - maximize both
            f.addWindowListener(maxBoth);

            // initially hide the elements
            // after maximized state has been applied show them
            f.getContentPane().setVisible(false);
            f.addWindowListener(new WindowAdapter() {

                @Override
                public void windowActivated(WindowEvent e) {
                    f.getContentPane().setVisible(true);
                    // remove this listener
                    f.removeWindowStateListener(this);
                }
            });

            // set the frame visible
            f.setVisible(true);
        }
    });
}

}

答案 7 :(得分:0)

看起来像一个java bug。我已经报告了它(但由于某种原因,它仍未显示在错误报告中)。