java - 计算 JDialog 组件的大小,在使可见之前设置总大小

标签 java swing layout size components

我有一个 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]”。显然,组件的大小已调整为填充对话窗口的整个客户区/内容 Pane 。很好,正如我所愿。

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

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

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

最佳答案

我现在发现可以这样做:

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 中读出正确的大小,要么实现自定义布局管理器,要么等待调整大小的事件并调用重绘,这样组件就会被绘制两次,但它应该可以工作。应用程序包括要显示的组件数量取决于大小的情况(如在我的地理 map 应用程序中)。


如果用户最大化或调整框架/对话框的大小,我认为流程是这样的:

  • frame/dialog.setSize()
  • LayoutManager.layoutContainer(frame/dialog) 使用实际大小
  • 使用布局尺寸的框架/对话框 paint()
  • 为所有组件等触发 Resized() 事件。

pack() 可能只是调用 setSize(layout.preferredLayoutSize()) 作为第一步。

因此,例如,如果您必须根据大小添加或删除组件,那么覆盖 setSize() 并在那里监听更改可能是个好主意。我最初是在监听 Resized() 事件,但它们对于第一张图来说来得太晚了。

关于java - 计算 JDialog 组件的大小,在使可见之前设置总大小,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13046508/

相关文章:

html - 如何水平和垂直居中元素

html - Flexbox 子项中的 CSS 可滚动内容

java - 在 Android 应用程序中显示较大的格式化文本文章

java - 如何在 CompletableFuture 完成后通过垃圾收集进行回收?

java - Android 如何在我的 baseadapter 中获取 ArrayList Hashmap 的值

java - removeMouseListener() 不起作用(Java)

java - JList:如何获取 LineBorder "between"单元格?

java - 如何使用 RedisTemplate 访问由 spring redis session 存储的散列 key ?

java - 添加到其他 JPanel 时 JPanel 不呈现

layout - Excel 宏 - 宏运行时的屏幕