Макет с автоматическим переносом в JScrollBar - PullRequest
1 голос
/ 26 сентября 2019

Я пытаюсь создать панель с горизонтально расположенными компонентами, с автоматическим переносом, если места недостаточно, и вертикальной полосой прокрутки.

Примерно так:

+-----------------+
|[1][2][3][4][5]  |
|                 | 
+-----------------+

уменьшениеширина:

+-----------+
|[1][2][3]  |
|[4][5]     |
+-----------+

снова уменьшая ширину, появляется полоса прокрутки:

+---------+
|[1][2]  ^|
|[3][4]  v|
+---------+

Я не далеко от решения:

public class TestFlow extends JFrame {

    public TestFlow() {

        getContentPane().setLayout(new BorderLayout());

        JPanel panel = new JPanel(new FlowLayout());
        JScrollPane scroll = new JScrollPane(panel, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);

        getContentPane().add(scroll, BorderLayout.CENTER);

        panel.add(new MyComponent("A"));
        panel.add(new MyComponent("B"));
        panel.add(new MyComponent("C"));
        panel.add(new MyComponent("D"));
        panel.add(new MyComponent("E"));
        panel.add(new MyComponent("F"));
        panel.add(new MyComponent("G"));
        panel.add(new MyComponent("H"));
        panel.add(new MyComponent("I"));
        panel.add(new MyComponent("J"));
        panel.add(new MyComponent("K"));
        panel.add(new MyComponent("L"));
        panel.add(new MyComponent("M"));
        panel.add(new MyComponent("N"));
        panel.add(new MyComponent("O"));

        scroll.addComponentListener(new ComponentAdapter() {

            @Override
            public void componentResized(ComponentEvent e) {
                Dimension max=((JScrollPane)e.getComponent()).getViewport().getExtentSize();
//                panel.setMaximumSize(new Dimension(max.width,Integer.MAX_VALUE));
                panel.setPreferredSize(new Dimension(max.width,Integer.MAX_VALUE));
//                panel.setPreferredSize(max);
                panel.revalidate();
//                panel.repaint();
//                System.out.println(panel.getSize().width+"--"+max.width);
            }

        });

        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setSize(500, 200);
        setLocationRelativeTo(null);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new TestFlow().setVisible(true));
    }

    private static class MyComponent extends JLabel {

        public MyComponent(String text) {
            super(String.join("", Collections.nCopies((int)(Math.round(Math.random()*4)+4), text)));
            setOpaque(true);
            setBackground(Color.YELLOW);
        }

    }

}

, нопо-прежнему странное поведение:

С решением panel.setPreferredSize(new Dimension(max.width,Integer.MAX_VALUE));

  • после изменения размера окна панель становится пустой.Я должен вручную переместить полосу прокрутки, чтобы содержимое отображалось
  • , полоса прокрутки всегда видна, в то время как она не должна

С решением panel.setPreferredSize(max);

  • после изменения размера окна панель не выкладывается заново.Я должен вручную переместить окно во второй раз, чтобы перерисовать содержимое.
  • полоса прокрутки никогда не видна, хотя она должна.

Есть какие-либо предложения в этом коде?

1 Ответ

0 голосов
/ 27 сентября 2019

Код ниже работает для меня.

Я разделил код на компонент, который выполняет всю компоновку и тестовый код, чтобы убедиться, что он работает (в методе static main).

Вместо того, чтобы перехватывать события для изменения размера на JScrollPane, я делаю это на всем компоненте TestFlow (TF) - я считаю, что это эквивалентно, но кажется безопаснее.Всякий раз, когда размер TF изменяется, я обновляю предпочтительный размер панели, как и вы, но есть два шага, которые вы не предприняли:

  • ширина внутренней панели будет равна шириневнешняя панель была выровнена, но она также должна оставить достаточно места для вертикальной полосы прокрутки, если это необходимо.
  • высота внутренней панели должна соответствовать высоте самого нижнего компонента, который она отображает.В моем случае я использую ярлык, предполагая, что все компоненты в самом нижнем ряду имеют одинаковую высоту, но это не всегда должно быть правдой.какой из нескольких похожих методов вызывается, когда я всегда вспоминаю о темной магии.
    import java.awt.*;
    import java.awt.event.*;
    import java.util.*;
    import javax.swing.*;
    import javax.swing.event.*;
    
    public class TestFlow extends JPanel {
    
        JPanel panel;
        JScrollPane scroll;
    
        public TestFlow() {
            panel = new JPanel(new FlowLayout());
            scroll = new JScrollPane(panel, 
                ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, 
                ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
            setLayout(new BorderLayout());
            add(scroll, BorderLayout.CENTER);
            addComponentListener(new ComponentAdapter(){
                @Override
                public void componentResized(ComponentEvent e){
                    Rectangle lastChildBounds = panel
                        .getComponent(panel.getComponentCount() - 1).getBounds();
                    Dimension preferred = new Dimension(
                        scroll.getWidth(), 
                        lastChildBounds.y + lastChildBounds.height);
                    if (scroll.getVerticalScrollBar().isVisible()) {
                        preferred.width -= scroll.getVerticalScrollBar().getWidth();
                    }
                    // System.err.println("setting inner panel size to " + preferred);
                    panel.setPreferredSize(preferred);
                    panel.repaint();
                }
            });
        }
    
        public JPanel getInnerPanel() { return panel; }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(() -> {
                // get a TestFlow instance and fill it with random labels
                TestFlow tf = new TestFlow();
                Random r = new Random();
                for (String s : "A B C D E F G H I J K L M N O".split(" ")) {
                    String labelText = String.join("", Collections.nCopies(4+r.nextInt(3)*4, s));
                    JLabel label = new JLabel(labelText);
                    label.setOpaque(true);
                    label.setBackground(Color.YELLOW);
                    tf.getInnerPanel().add(label);
                }
    
                // display it in a window
                JFrame jf = new JFrame("test");
                jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);            
                jf.add(tf, BorderLayout.CENTER);
                jf.setSize(500, 200);
                jf.setLocationRelativeTo(null);
                jf.setVisible(true);
            });
        }
    }
    
...