我现在发现可以这样做:
JDialog dialog = new JDialog();
JLabel label = new JLabel("Test");
dialog.add(label);
// pack(), setSize(), validate() in this order will
// set sizes on all components as wished
dialog.pack();
dialog.setSize(800, 600);
dialog.validate();
System.out.println(label.getSize());
这里的输出也是“[width=784,height=562]”,但对话框尚不可见。重要的部分是按此顺序组合 pack()、setSize(desiredSize) 和 validate()。 pack() 可能确定对话框的新大小(所有组件的首选大小),这就是为什么这里必须在之后设置大小,而 validate() 负责调整组件的大小。可能达到相同大小的 setVisible(true) 在内部做类似的事情。
多次调整组件大小似乎有点浪费,但没有 pack() 也 setSize() 和 validate() 没有任何效果。
我猜其他答案是基于一些误解,因为他们总是隐含地假设您想要拥有首选尺寸,但也有一些情况,例如如果用户调整了对话框的大小,或者如果对话框的大小从一开始就固定,您无法获得首选大小,并且某些组件只需要填充可用空间。
这就是这里的布局问题,给定对话框的全局大小,并在组件填充可用空间时确定组件的大小。 LayoutManagers 很好地解决了这个问题,但通常只在 setVisible(true) 之后。
我测试了更多:
// new dialog
JDialog dialog = new JDialog();
// new label, prints messages if resized or painted
JLabel label = new JLabel("Test") {
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("Component painted.");
}
};
label.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
System.out.println("Resized: " + e.getComponent().getSize());
}
});
dialog.add(label);
System.out.println("Size after new JLabel: " + label.getSize());
// pack dialog - necessary for setSize/validate to work
dialog.pack();
System.out.println("Size after pack: " + label.getSize());
// set a size and validate changes sizes
dialog.setSize(800, 600);
dialog.validate();
System.out.println("Size after setSize and validate: " + label.getSize());
// set visible would have also done the trick
dialog.setVisible(true);
System.out.println("Size after setVisible(true): " + label.getSize());
// and another resizing (no validation neccessary)
dialog.setSize(300, 200);
// dispose
dialog.dispose();
输出是
- 新 JLabel 后的大小:java.awt.Dimension[width=0,height=0]
- 打包后的尺寸:java.awt.Dimension[width=116,height=16]
- setSize 和验证后的大小:java.awt.Dimension[width=784,height=562]
- setVisible(true) 后的大小:java.awt.Dimension[width=784,height=562]
- 调整大小:java.awt.Dimension[width=284,height=162]
- 调整大小:java.awt.Dimension[width=284,height=162]
- 绘制的组件。
我更多地了解了 Swing 的内部工作原理:
- ComponentResized 事件不会在 setVisible(true) 之前触发,即使组件已调整大小(它们的大小发生变化)
- 即使大小相同的 ComponentResized 事件也可以连续触发多次
- 如果组件彼此跟随的速度足够快,则在调整大小之间可能不会绘制组件
- 无论如何,第一次绘制都在 setVisible(true) 之后,届时组件将具有所需的大小(首选大小或由此处的其他约束定义)。
- 如果由于某种原因您必须在第一次绘制之前知道组件的大小,请使用 pack()、setSize()、validate() 来完成
我测试了更多,也使用最大化的帧,现在可以将所有结果组合成:第一个 painComponent() 总是具有正确的大小,并且相关的 componentResized() 事件总是在之后发生,有时是两次。但是 LayoutManager必须事先知道,否则示例将无法正确绘制。所以如果一个人自己绘制背景,要么在每个paintComponent中读出正确的大小,要么实现一个自定义布局管理器,或者等待resized事件并调用repaint,所以组件被绘制了两次,但它应该可以工作。应用包括要显示的组件数量取决于大小的情况(如在我的地理地图应用程序中)。
为了完成图片,如果用户最大化或调整框架/对话框的大小,我认为流程是这样的:
- frame/dialog.setSize()
- LayoutManager.layoutContainer(frame/dialog) 使用实际大小
- 使用布局尺寸的框架/对话框paint()
- 为所有组件等触发 Resized() 事件。
而 pack() 可能只是调用 setSize(layout.preferredLayoutSize()) 作为第一步。
因此,如果您必须根据大小添加或删除组件,例如,覆盖 setSize() 并在那里侦听更改可能是个好主意。我最初是在监听 Resized() 事件,但它们到达第一次绘图时为时已晚。