MigLayout - componentResized () вызывается слишком много раз - PullRequest
1 голос
/ 21 мая 2019

У меня есть следующий сценарий - у меня есть панель, которая содержит компонент (в данном случае метку) внутри другой панели - все определяется с помощью 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);
    }
}

Когда я изменяю размер кадра, перетаскиваяправый край, чтобы увеличить его - он работает просто отлично, однако, когда я пытаюсь уменьшить его размер, перетаскивая его влево от правого края, приложение вяло и результат не сразу отражается в графическом интерфейсе.Пожалуйста, запустите его, чтобы понять, что я имею в виду.

У вас есть идеи, как я могу улучшить этот код, чтобы он работал гладко?

enter image description here

Обновление : я добавил некоторые записи в журнал и заметил, что при увеличении ширины он работает нормально, но при уменьшении ширины панели я вижу, что вызывается метод componentResized ()во много раз больше, чем при увеличении.

Причиной проблемы, по-видимому, является MigLayout в сочетании с grow ().Я заменил все макеты с BorderLayout и, кажется, работает нормально.

Однако в моем случае я не могу этого сделать, поскольку мне нужно, чтобы моя панель увеличивалась в макете.

Ответы [ 2 ]

0 голосов
/ 23 мая 2019

Я не уверен, почему, но кажется, что установка минимального размера для 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);
        }
    });
}
0 голосов
/ 22 мая 2019

Поскольку метод вызывается несколько раз, попробуйте рассчитать значение только один раз. Вы можете сохранить последние вычисленные ширину и высоту в компоненте и рассчитать размер, когда одно из этих значений изменилось:

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);
        }
    });
}
...