java - 在 JScrollBar 中自动换行的布局

标签 java swing jscrollpane miglayout flowlayout

我正在尝试构建一个组件水平放置的面板,如果没有足够的空间则自动换行,还有一个垂直滚动条。

像这样:

+-----------------+
|[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);

  • 调整窗口大小后,面板不会重新布局。我必须再次手动移动窗口以重新布局内容。
  • 滚动条永远不可见,但它应该是可见的。

该代码有什么建议吗?

[编辑] 我已将原始代码复杂化,并应用了迄今为止提供的建议。

出于设计目的,我想在面板顶部使用 MigLayout。一开始,一切都布置得很好。放大窗口时,它也有效。但不是关于减少窗口。 addComponentListener不会带来任何附加值。

public class TestMigFlow extends JFrame {

    public TestMigFlow() {

        getContentPane().setLayout(new BorderLayout());
        JPanel panel = new JPanel(new MigLayout("debug, fill, flowy", "[fill]"));
        JScrollPane scroll = new JScrollPane(panel, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);

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

        panel.add(new JLabel("A title as spearator "), "growy 0");

        JPanel sub = new JPanel(new WrapLayout());
//        panel.add(sub, "growx 0"); // Works well on shrink but not on grow
        panel.add(sub); // Works well on grow but not on shrink

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

        addComponentListener(new ComponentAdapter() {

            @Override
            public void componentResized(ComponentEvent e) {
                Dimension max = new Dimension(scroll.getWidth(), Short.MAX_VALUE);
                panel.setMaximumSize(max);
                panel.repaint();
            }

        });

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

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new TestMigFlow().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);
        }

    }

最佳答案

下面的代码适合我。

我已将代码拆分为一个组件,该组件执行所有布局和测试代码以验证它是否有效(在 static main 中)方法。

我没有 Hook 以调整 JScrollPane 上的事件大小,而是在整个 TestFlow (TF) 组件上这样做 - 我相信这是等效的,但它似乎更安全。每当调整 TF 大小时,我都会更新面板的首选大小,就像您所做的一样 - 但有两个步骤您没有采取:

  • 内面板的宽度将与外面板指定的宽度相同,但如果需要,还应为垂直滚动条留出足够的空间。
  • 内部面板的高度应该是它显示的最低组件的高度。在我的例子中,我采取了假设最低行中的所有组件高度相同的捷径,但这不一定总是正确的。

深入探讨 Swing 如何处理布局和绘画,以及调用几个看起来相似的方法中的哪些方法时,总是让我想起黑魔法。

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

关于java - 在 JScrollBar 中自动换行的布局,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58114283/

相关文章:

java - NoNodeAvailableException : None of the configured nodes are available

Java Applet JOptionPane 和 setSize 问题

Java Swing JScrollPane 按住 Shift 并使用鼠标滚轮时不滚动

java - 如何从oracle中检索图像并在java框架中显示

java - 将 JScrollPane 添加到 JSplitPane

java - 使用滚动条清屏(Java)

java - JTextArea 不显示在 JTabbedPane 内的 JPanel 上

java - Uri 对所有内容进行编码,包括未保留的字符

尝试填充列表时出现 Java 堆空间异常

java - 有没有可能通过oracle获取警告消息的方法