【问题标题】:MigLayout - componentResized() called too many timesMigLayout - componentResized() 调用次数过多
【发布时间】:2019-10-07 23:52:15
【问题描述】:

我有以下场景 - 我有一个面板,它在另一个面板中包含一个组件(在本例中为标签) - 全部使用 MigLayout 定义。

当面板调整大小时,我想调整JLabel的字体大小以适合整个区域。

我将代码剥离到最低限度

public class TestClass {

    private JPanel panel;
    private JLabel displayLabel;

    private TestClass() {
        panel = new JPanel(new MigLayout(new LC().fill().gridGap("0", "0").insetsAll("0").hideMode(3)));

        displayLabel = new JLabel("some text goes here");
        displayLabel.setFont(new Font("Dialog", Font.BOLD, 9));
        panel.add(displayLabel, new CC().grow().push());

        panel.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent e) {
                scale(displayLabel);
            }
        });
    }

    private void scale(JLabel component) {
        Integer lastWidth = (Integer) component.getClientProperty("width");
        Integer lastHeight = (Integer) component.getClientProperty("height");
        if (lastWidth != null && lastHeight != null && lastWidth == component.getWidth() && lastHeight == component.getHeight()) {
            return;
        }

        component.putClientProperty("width", component.getWidth());
        component.putClientProperty("height", component.getHeight());

        int minFontSize = 9;
        Font oldFont = component.getFont();
        float size = oldFont.getSize();
        Font newFont = oldFont.deriveFont(size);
        FontMetrics fm = component.getFontMetrics(newFont);

        int availWidth = component.getWidth() - 2;
        int availHeight = component.getHeight() - 2;

        boolean found = false;
        boolean increased = false;
        boolean decreased = false;
        while (!found) {
            int stringWidth = fm.stringWidth(component.getText());
            int stringHeight = fm.getHeight();
            if (!decreased && stringWidth < availWidth && stringHeight < availHeight) {
                size++;
                increased = true;
            } else if (stringWidth > availWidth || stringHeight > availHeight) {
                size--;
                decreased = true;
            } else {
                found = true;
            }
            if (increased && decreased) {
                found = true;
            }
            newFont = oldFont.deriveFont(size);
            fm = component.getFontMetrics(newFont);
        }

        if (size < minFontSize) {
            size = minFontSize;
            newFont = oldFont.deriveFont(size);
        }

        component.setFont(newFont);
    }


    public JPanel getPanel() {
        return panel;
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame("");
        JPanel pnl = new JPanel(new MigLayout(new LC().fill()));
        pnl.add(new TestClass().getPanel(), new CC().grow().push());
        frame.add(pnl, BorderLayout.CENTER);
        frame.setSize(1024, 700);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

当我通过拖动右边缘来放大它来调整框架的大小时 - 它工作得很好,但是,当我尝试通过拖动右边缘的左边来减小它的大小时,应用程序很慢并且结果没有反映立即在 GUI 中。请运行它以了解我的意思。

您知道如何改进此代码以使其顺利运行吗?

更新:我添加了一些日志记录,我观察到当增加宽度时效果很好,但是当减小面板宽度时,我看到 componentResized() 方法被调用了很多与放大时相比,次数更多。

问题的原因似乎是 MigLayout 与 grow() 结合使用。我已经用 BorderLayout 替换了所有布局,并且似乎工作正常。

但是,在我的情况下,我真的不能这样做,因为我需要我的面板在我的布局中增长。

【问题讨论】:

  • 我试过了,心想“好吧,这很难修复”,但是……是的,好吧。 似乎 MigLayout 在内部对组件大小进行了某种“摆动”:Wenn 组件变得更小,它尝试“迭代地”调整所有包含的组件的大小,直到它坐下并适合.但我还没有找到防止这种情况的方法。 (我仍然需要检查您提出的最小尺寸解决方案)

标签: java swing miglayout


【解决方案1】:

我不知道为什么,但似乎为 JLabel 设置一个最小尺寸可以解决问题。

private TestClass() {
    panel = new JPanel(new MigLayout(new LC().fill().gridGap("0", "0").insetsAll("0").hideMode(3)));

    displayLabel = new JLabel("some text goes here");
    displayLabel.setFont(new Font("Dialog", Font.BOLD, 9));
    displayLabel.setMinimumSize(new Dimension(30, 10));
    panel.add(displayLabel, new CC().grow().push());

    panel.addComponentListener(new ComponentAdapter() {
        @Override
        public void componentResized(ComponentEvent e) {
            scale(displayLabel);
        }
    });
}

【讨论】:

    【解决方案2】:

    由于该方法被多次调用,因此尝试只计算一次该值。您可以将最后计算的宽度和高度存储在组件中,并在这些值之一发生更改时计算大小:

    private void scale(JLabel component) {
        int minFontSize = 9;
        float size = Float.MAX_VALUE;
        size = Math.min(size, component.getWidth());
        size = Math.min(size, component.getHeight());
    
        Integer lastWidth = (Integer) component.getClientProperty("width");
        Integer lastHeight = (Integer) component.getClientProperty("height");
        if (lastWidth != null && lastHeight != null && lastWidth.intValue() == component.getWidth() && lastHeight.intValue() == component.getHeight()) {
            return;
        }
    
        component.putClientProperty("width", component.getWidth());
        component.putClientProperty("height", component.getHeight());
    
        component.putClientProperty("lastSize", new Integer((int) size));
    
        Font oldFont = component.getFont();
        Font newFont = oldFont.deriveFont(size);
        FontMetrics fm = component.getFontMetrics(newFont);
    
        int availWidth = component.getWidth() - 2;
        int availHeight = component.getHeight() - 2;
    
        while ((size > minFontSize) && ((fm.stringWidth(component.getText()) >= availWidth) || (fm.getHeight() >= availHeight))) {
            newFont = oldFont.deriveFont(--size);
            fm = component.getFontMetrics(newFont);
        }
    
        component.setFont(newFont);
    }
    

    您也可以尝试像二进制搜索一样查找size 变量,而不是一一递减。对于[9 - size=min(width, height)] 范围,首先尝试(size - 9)/2,如果它适合在[(size - 9)/2 - size] 范围内搜索。如果不适合在[9 - (size - 9)/2]之间搜索。

    这是一个不断增长的 GridBagLayout 替代方案:

    private TestClass() {
        panel = new JPanel(new GridBagLayout());
    
        displayLabel = new JLabel("some text goes here");
        displayLabel.setFont(new Font("Dialog", Font.BOLD, 9));
        GridBagConstraints gc = new GridBagConstraints();
        gc.fill = GridBagConstraints.BOTH;
        gc.weightx = 1;
        gc.weighty = 1;
        panel.add(displayLabel, gc);
    
        panel.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent e) {
                scale(displayLabel);
            }
        });
    }
    

    【讨论】:

    • 嗨,hkn,感谢您花时间和回答。不幸的是,您的解决方案还不够。我实际上已经尝试用二进制搜索替换我原来的while循环,以及一个算法,它将使用最后一个字体大小并尝试增加/减小它直到它适合,以使其更有效,但是问题仍然复制。
    • 你能试试只计算一次值的代码吗?
    • 我做到了。我还用我目前使用的方法更新了 scale 方法。请看一下。再一次,问题似乎出在 MigLayout(填充 + 增长)上,将其替换为 BorderLayout 它工作顺利
    • 如前所述,将两个面板的 LayoutManager 更改为另一个(非 MigLayout)将是一种有效的解决方法。但是,就我而言,我真的不想这样做,因为所有进一步的影响。
    猜你喜欢
    • 2015-03-15
    • 2014-11-21
    • 1970-01-01
    • 2021-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-10
    相关资源
    最近更新 更多