我遇到的问题是,当我的框架显示时(登录对话框后)按钮不在正确的位置,然后在几毫秒内它们会到达正确的位置(带边框布局的面板中心)。
- 更新
在我的机器中,这个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);
答案 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>
答案 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。我已经报告了它(但由于某种原因,它仍未显示在错误报告中)。