2

我有一个 JDialog,我希望它具有一定的给定大小:

JDialog dialog = new JDialog();
dialog.setSize(800, 600);
dialog.setResizable(false);

然后我添加一个组件:

JLabel label = new JLabel("Test");
dialog.add(label);

现在我可以使对话框可见并检查组件的大小

dialog.setVisible(true);
System.out.println(label.getSize());

答案是“[width=784,height=562]”。显然,该组件已调整大小以填充对话窗口的整个客户区/内容窗格。这很好,正如我想要的那样。

问题:如何在调用 setVisible(true) 之前获得组件的最终大小?

  • getPreferredSize() 不会是我想要的大小,因为我希望组件适应给定对话框的大小
  • dialog.pack() 也不是正确的,因为它将对话框调整为我不想要的组件的首选大小
  • dialog.validate() 在这里没有任何用处,组件的大小仍然为 0
  • dialog.getLayout().layoutContainer(dialog) 也没有设置组件的大小

所以我在这里不知所措。我想让布局管理器在显示对话框之前计算所有组件和子组件的正确大小,以适应对话框的整体大小。但我不知道怎么做。

4

2 回答 2

3

我现在发现可以这样做:

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() 事件,但它们到达第一次绘图时为时已晚。

于 2012-10-25T06:29:19.453 回答
2

顶级容器返回自己的Size, PreferredSize,Bounds在两种情况下(如果是的话)

  • 已经可见

  • 通话后pack(),在你的情况下dialog.pack();

  • 必须计算BordersToolBar来自Native OS

  • 必须从Top-Level Containers#getContentPane().getWhatever

  • 大部分都是JComponents自己退货的PreferredSize,那就没有理由选尺码了Standard Layout Manager

于 2012-10-24T09:38:31.927 回答