我现在发现可以这样做:
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,所以组件被绘制了两次,但它应该可以工作。应用程序包括要显示的组件数量取决于大小的情况(如在我的地理地图应用程序中)。
只是为了完成图片,我认为如果用户最大化或调整框架/对话框的大小,流程是这样的:
- 框架/对话框.setSize()
- LayoutManager.layoutContainer(frame/dialog) 使用实际大小
- 使用布局大小的框架/对话框paint()
- 为所有组件等触发 Resized() 事件。
而 pack() 可能只是调用 setSize(layout.preferredLayoutSize()) 作为第一步。
因此,如果您必须根据大小添加或删除组件,例如,覆盖 setSize() 并在那里监听更改可能是个好主意。我最初是在监听 Resized() 事件,但它们到达第一次绘图时为时已晚。