Swing MigLayout 无法增长填充列以填充容器长度

Swing MigLayout cannot grow fill column to fill container length

我使用 MigLayout 的时间很长 window。

我希望"push"第二列和第四列填满整个window的所有长度,但我无法实现。列约束中没有 push 选项,只有 growfill

这里有一个 SCCEE,正如有人曾经建议的那样,我已经忘记了他的名字:

package com.WindThunderStudio.MigLayoutTest;

import java.awt.Cursor;
import java.awt.EventQueue;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

import net.miginfocom.swing.MigLayout;

public class MigLayoutTest extends JFrame{
private JFrame mainFrame;
private JPanel panel;

private JLabel lblResumenAuto;
private JLabel lblResumenAutoResult;
private JLabel lblResumenRazonSocial;
private JLabel lblResumenRazonSocialResult;
private JLabel lblResumenPeriodo;
private JLabel lblResumenPeriodoResult;
private JLabel lblResumenFechaHora;
private JLabel lblResumenFechaHoraResult;

public MigLayoutTest(){
    run();
}

public void run(){
    mainFrame = new JFrame();
    mainFrame.setBounds(0, 0, 1250, 500);
    mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

    JPanel p = new JPanel();
    p.setSize(mainFrame.getSize());

    p.setLayout(new MigLayout("fill","[max!, grow]","[50:20:30]10[100::]10[20::]10[50!]10[20!]"));
    mainFrame.setContentPane(p);

    panel = new JPanel();
    panel.setLayout(new MigLayout("fillx", "[left, 15%]10[left, grow, 35%]10[left, 15%]10[left, grow, 35%]", "[center]10[center]"));

    lblResumenAuto = new JLabel("MY LABEL 1111111111111");
    lblResumenAutoResult = new JLabel("1111111111111111111111");

    panel.add(lblResumenAuto);
    panel.add(lblResumenAutoResult);

    lblResumenRazonSocial = new JLabel("MY LABEL 2222222222");
    lblResumenRazonSocialResult = new JLabel("2222222222222222222222");

    panel.add(lblResumenRazonSocial);
    panel.add(lblResumenRazonSocialResult,"wrap");

    lblResumenPeriodo = new JLabel("MY LABEL 33333333333333");
    lblResumenPeriodoResult = new JLabel("3333333333333333333333333333333333333333333333333333333");

    panel.add(lblResumenPeriodo);
    panel.add(lblResumenPeriodoResult);
    //poner el texto como html puede tener otra linea, porque es muy largo
    lblResumenFechaHora = new JLabel("<html>MY LABEL <br /> 4444444444444444</html>");
    lblResumenFechaHoraResult = new JLabel("4444444444444444444444444");

    panel.add(lblResumenFechaHora);
    panel.add(lblResumenFechaHoraResult);

    p.add(panel,"cell 0 0");

    getContentPane().setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
    setBounds(0, 0, 1250, 500);
    getContentPane().add(mainFrame.getContentPane());

    pack();
    setVisible(true);
    setLocationRelativeTo(null);
    setResizable(true);
}

public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {

        @Override
        public void run() {
            MigLayoutTest test = new MigLayoutTest();

        }
    });
}
}

如果您 运行 代码,您会注意到列的宽度随着其包含文本长度的变化而增加。但它永远不会填满其容器的整个宽度。

可取的是,将第0列和第2列固定15%整个宽度,让13列占据其余部分,35%, 前两列占整个宽度的50%。

我是不是漏掉了什么?我不想指定每一列的宽度,设置 pre:min:max,因为这是不好的做法,正如 this post 所建议的那样,它获得了很多投票。

panel.setLayout(new MigLayout("fillx", 
                              "[left, 15%]10[left, grow, 35%]10[left, 15%]10[left, grow, 35%]", 
                              "[center]10[center]"));

但是,如果我设置pref:min:max,它可以填满整个宽度。

先是代码,再是解释。试试这个:

import java.awt.Cursor;
import java.awt.EventQueue;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

import net.miginfocom.swing.MigLayout;

public class MigLayoutTest extends JFrame {
  private JPanel panel;

  private JLabel lblResumenAuto;
  private JLabel lblResumenAutoResult;
  private JLabel lblResumenRazonSocial;
  private JLabel lblResumenRazonSocialResult;
  private JLabel lblResumenPeriodo;
  private JLabel lblResumenPeriodoResult;
  private JLabel lblResumenFechaHora;
  private JLabel lblResumenFechaHoraResult;

  public MigLayoutTest() {
    run();
  }

  public void run() {
    panel = new JPanel();
    panel.setLayout(new MigLayout("debug, fill",
        "[left, 15%]10[left, 35%]10[left, 15%]10[left, 35%]", "[center]10[center]"));

    lblResumenAuto = new JLabel("MY LABEL 1111111111111");
    lblResumenAutoResult = new JLabel("1111111111111111111111");

    panel.add(lblResumenAuto, "sg label");
    panel.add(lblResumenAutoResult, "sg value");

    lblResumenRazonSocial = new JLabel("MY LABEL 2222222222");
    lblResumenRazonSocialResult = new JLabel("2222222222222222222222");

    panel.add(lblResumenRazonSocial, "sg label");
    panel.add(lblResumenRazonSocialResult, "sg value, wrap");

    lblResumenPeriodo = new JLabel("MY LABEL 33333333333333");
    lblResumenPeriodoResult = new JLabel("3333333333333333333333333333333333333333333333333333333");

    panel.add(lblResumenPeriodo, "sg label");
    panel.add(lblResumenPeriodoResult, "sg value");
    // poner el texto como html puede tener otra linea, porque es muy largo
    lblResumenFechaHora = new JLabel("<html>MY LABEL <br /> 4444444444444444</html>");
    lblResumenFechaHoraResult = new JLabel("4444444444444444444444444");

    panel.add(lblResumenFechaHora, "sg label");
    panel.add(lblResumenFechaHoraResult, "sg value");

    setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    getContentPane().setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
    getContentPane().add(panel);

    pack();
    setVisible(true);
    setLocationRelativeTo(null);
    setResizable(true);
  }

  public static void main(String[] args) {
    MigLayoutTest test = new MigLayoutTest();
  }

}

我的改动说明:

现在除了简化代码和布局外,我在布局约束内使用“调试”来查看布局实际发生了什么。我建议在布局出现问题时使用它 - 它使 MigLayout 绘制组件和单元格的边界,从而可视化潜在的问题。

我删除了不必要的 mainframep - 如果您真的需要它用于嵌套布局,请在您根据自己的喜好解决内部布局后尝试添加它。

至于 ppanel - 您可能需要两种不同的布局,一个嵌套在另一个布局中,但这是问题的实际根源。 p 也有自己的网格布局,

p.add(panel,"cell 0 0");

你把 panel 放在 p 的左上角单元格中 - 这就是为什么 panel 没有分布在整个 window 而是坐在左上角的原因角落.

如您所见,没有 p 它很好地位于屏幕中间,没有任何恒定大小,仍然显示所有组件,但更重要的是它有 50% 的 window 大小用于第一列和最后两列的 50%。这是通过给组件一个“sizegroup”来实现的:

Gives the component a size group name. All components that share a size group name will get the same BoundSize (min/preferred/max). It is used to make sure that all components in the same size group gets the same min/preferred/max size which is that of the largest component in the group. An empty name "" can be used.

而且它还可以按应有的方式调整大小!

嵌套布局也可能是另一个问题的根源——不知道你是否注意到它或者它只是没有出现在你的机器上,但如果我试图调整你的 window 即使我缩小 window,panel 变得越来越宽(从不变窄)。在某些时候,它变得比 window 本身更宽,甚至在每次调整大小时都进一步增长。

接下来 - 将尺寸设置为常量值没有意义,因为在 pack 之后布局管理器开始根据 window 及其内容的首选大小调整所有内容的大小。此外,您永远不知道用户的屏幕尺寸是多少,因此如果有效使用,任何固定尺寸都可能同样糟糕。最好通过内容和可用的运行时环境来驱动大小。在我的机器上使用你的代码,它占用了我两个屏幕 (2 x 1280) 的所有可用水平 space,并且看起来不漂亮。

我也认为你不需要使用EventQueue.invokeLater开始框架,只需创建一个MigLayoutTest就可以了。

在 OP 自己的回答后进行编辑

pack 之前使用 setBounds(0, 0, 1250, 500) 设置大小无法正常工作(我的意思是让 window 成为那个大小)。即使在 OP 自己的答案下方的屏幕截图中,它也不是 500px 高。这是我在 Windows 7 运行 JDK 1.8.0_91:

上得到的

我的屏幕尺寸是 1280 x 1024,程序的尺寸 window 是 914 x 301。

我建议使用以下其中一项:

要将其设置为 1250 x 500 像素的恒定大小,请将 setSize 移动到 packsetVisible 之间:

...
pack();
setSize(1250, 500);

我会使用 setSizesetBounds 没有任何意义,因为通过调用 setLocationRelativeTo(null) 你无论如何都会在屏幕上居中程序的 window,所以origin 被立即解雇。

要水平最大化并让高度为 500 像素,请在 pack 之前设置主要 window 的首选大小:

Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
setPreferredSize(new Dimension(screenSize.width, 500));

并水平最大化并让首选高度保持原样:

Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
setPreferredSize(new Dimension(screenSize.width, getPreferredSize().height));

当然,您也可以通过设置其首选大小而不是使用 setSize 来使 window 1250 x 500 变大。

调整大小的问题现在不是那么大,但它仍然存在 - 让 window 变宽,而不是缩小它一点点。您会注意到 panel 变宽了,即使 window 变窄了。问题是 panel 组件最初不够大,无法填充 p 的一列(顺便说一句,您可以将 'debug' 标志添加到每个 MigLayout,也是 panel - 然后它也会概述所有内部组件)。

要使其填充父容器,请像这样添加它:

p.add(panel, "cell 0 0, grow");

现在它从一开始就是 p 的全宽,并且按预期调整大小。

关于使用 invokeLater 启动 JFrame - 我们启动我们的主要 windows 通常没有它并且从未遇到过问题,因为在第一帧之前没有与 Swing 交互可见,但我刚刚注意到它被认为是最佳实践 - 即使在 Oracle 的教程中也是如此。看来我在这里也学到了一些东西:-)。

帧的window添加和不添加"grow"

的比较

测试场景:启动应用程序并将其调整为更宽。

正如您在第一个屏幕截图中看到的那样,组件尺寸小于列宽 - 看起来组件在调整大小时位于列尺寸后面。在第二个屏幕截图中,组件宽度始终与列宽保持相同。正如我之前所说,原因可能是 Java and/or 操作系统组合,我不知道。但显然它的行为有所不同,并且在我的机器上表现不佳。

感谢@Tomasz Stanczak,我终于解决了。但是,我发现他说的部分是相关的,而其他部分则不是。对于以后可能看到的读者,我必须说得更清楚一些。

最终的工作代码是:

import java.awt.Cursor;
import java.awt.EventQueue;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

import net.miginfocom.swing.MigLayout;

public class MigLayoutMySCCEE extends JFrame{
private JFrame mainFrame;
private JPanel panel;

private JLabel lblResumenAuto;
private JLabel lblResumenAutoResult;
private JLabel lblResumenRazonSocial;
private JLabel lblResumenRazonSocialResult;
private JLabel lblResumenPeriodo;
private JLabel lblResumenPeriodoResult;
private JLabel lblResumenFechaHora;
private JLabel lblResumenFechaHoraResult;

public MigLayoutMySCCEE(){
    run();
}

public void run(){
    JPanel p = new JPanel();
    p.setLayout(new MigLayout("debug, fill","[grow]","[50:20:30]10[100::]10[20::]10[50!]10[20!]"));

    panel = new JPanel();
    panel.setLayout(new MigLayout("fillx", "[left, 15%]10[left, grow, 35%]10[left, 15%]10[left, grow, 35%]", "[center]10[center]"));

    lblResumenAuto = new JLabel("MY LABEL 1111111111111");
    lblResumenAutoResult = new JLabel("1111111111111111111111");

    panel.add(lblResumenAuto);
    panel.add(lblResumenAutoResult);

    lblResumenRazonSocial = new JLabel("MY LABEL 2222222222");
    lblResumenRazonSocialResult = new JLabel("2222222222222222222222");

    panel.add(lblResumenRazonSocial);
    panel.add(lblResumenRazonSocialResult,"wrap");

    lblResumenPeriodo = new JLabel("MY LABEL 33333333333333");
    lblResumenPeriodoResult = new JLabel("3333333333333333333333333333333333333333333333333333333");

    panel.add(lblResumenPeriodo);
    panel.add(lblResumenPeriodoResult);
    //poner el texto como html puede tener otra linea, porque es muy largo
    lblResumenFechaHora = new JLabel("<html>MY LABEL <br /> 4444444444444444</html>");
    lblResumenFechaHoraResult = new JLabel("4444444444444444444444444");

    panel.add(lblResumenFechaHora);
    panel.add(lblResumenFechaHoraResult);

    p.add(panel,"cell 0 0");

    setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    getContentPane().setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
    setBounds(0, 0, 1250, 500);
    getContentPane().add(p);

    pack();
    setVisible(true);
    setLocationRelativeTo(null);
    setResizable(true);
}

public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {

        @Override
        public void run() {
            MigLayoutMySCCEE test = new MigLayoutMySCCEE();

        }
    });
}
}

而 window 看起来像:

一些注意事项:

  1. debug 技巧非常有用,促使我再次阅读 DOC。不过,我希望它能在“快速启动”页面中获得更多关注和重视。

  2. 不必要的嵌套是个问题,Tomasz 让我重新审视层次结构,太棒了!我更改了嵌套部分以使其更清晰。但这无关紧要。

  3. sizeGroup部分是个好主意,我决定在未来的开发中尽可能多地使用它,但这与我的情况无关。我没用就解决了

  4. 我在 Tomasz 的提示后发现了越来越宽的问题,但这是由于 [max!] 结合将面板添加到网格布局的第一个单元格,而不是 frame/panel嵌套。我删除了 [max!] 并将其更改为 [grow] 并且宽度不再扩大。我没有触及 p.add(panel, "cell 0 0") 部分。根据观察和定义,

    p.setLayout(new MigLayout("debug, fill","[grow]","[50:20:30]10[100::]10[20::]10[50!]10[20!]"));

面板的第一行只有一列,如果我没看错的话。

在 Tomasz 编辑后编辑

我肯定比你学到的更多 :) 我试图摆脱 setBounds() 部分并将 add(panel, "cell 0 0") 更改为 add(panel, "grow"),但我看不出有什么区别,是吗我在这里遗漏了什么? 然而 "grow" 几乎总是更好的选择和理想。

这里有 2 个 GIF 展示了我得到的东西:(来自 ScreenToGif,一个轻量级但功能强大的工具,对展示特别有用)