如何动态添加元素到 JScrollPane?

How To Dynamically Add Elements To a JScrollPanel?

我正在尝试使用 Java 中的 Swing 创建一个 GUI,它执行以下操作:

按下 "Add" 按钮时,将创建一个 JPanel 并填满 GUI 的整个宽度。在 JPanel 内部将使用 FlowLayout 排列 JLabel,以告知具体细节(即名称、用户 ID 等)。每当随后按下“添加”按钮时,我都希望在前一个 JPanel 的正下方添加一个新的 JPanel,以便 JScrollPane 将在必要时激活其垂直滚动条。基本上,我希望 JScrollPane 能够在其中动态添加新的 JPanel。

但是,我在尝试执行此操作时遇到了几个问题。我无法确定哪个 LayoutManager 用于 JScrollPane 中的面板。我尝试过使用 GridlLayout,但这似乎不起作用,因为当有足够的 JPanel 来覆盖整个 JScrollPane 时,GridLayout 只是创建一个新列来压缩新的 JPanel,而不是将其添加到先前的 JPanel 下方。我应该怎么做才能实现这一目标?这是我所拥有的一些图片以及我想要发生的事情的示例。

这是我想要发生的事情:

任何帮助将不胜感激,提前致谢! :)

编辑:这是我尝试使用 GridLayout 的代码示例。

public Window() {


    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setBounds(100, 100, 701, 400);
    contentPane = new JPanel();
    contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
    contentPane.setLayout(new BorderLayout(0, 0));
    setContentPane(contentPane);

    JPanel buttonPanel = new JPanel();
    contentPane.add(buttonPanel, BorderLayout.SOUTH);

    JButton btnAdd = new JButton("Add");
    buttonPanel.add(btnAdd);

    JScrollPane scrollPane = new JScrollPane();
    contentPane.add(scrollPane, BorderLayout.CENTER);

    JPanel systemsPanel = new JPanel();
    scrollPane.setViewportView(systemsPanel);
    systemsPanel.setLayout(new GridLayout(8, 1, 0, 0)); //8 rows for now



    for (int i = 1; i < 50; i++) {
        systemsPanel.add(new JButton("test"), "cell 0 " + i);
    }
    Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
    this.setLocation(dim.width / 2 - this.getSize().width / 2, dim.height / 2 - this.getSize().height / 2); //Center the Frame 

}

如果您想将多行 JPanel 添加到 GUI 中,我会将它们放置在使用 GridLayout(0, 1) 或 (0, 1, x, y) 的 JPanel 中。 0 表示行数可变,1 表示 1 列,x 和 y 是垂直和水平间隙。然后,我将使用 JPanel 的 BorderLayout 将其放置在 NORTH 位置,这样行就不会像它们本来想做的那样扩展以填充滚动窗格的视口。我会将此第二个包装器 JPanel 放入 JScrollPane 的视口中。

添加任何组件后,我会在容器上调用 revalidate 和 repaint 以便其布局管理器将激活并正确放置新组件,并重新绘制以去除可能挂起的任何脏像素(更重要的是删除组件时)。

比如我的MCVE:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;

import javax.swing.*;

public class AddRowA extends JPanel {
    private static final int PREF_W = 650;
    private static final int PREF_H = 400;

    // JPanel to hold all rows. uses gridlayout that has 1 column and variable number
    // of rows
    private JPanel rowHolderPanel = new JPanel(new GridLayout(0, 1, 1, 1));

    public AddRowA() {
        // outerPanel is a wrapper or container JPanel that is 
        // held by JScrollPane's viewport that holds the rowHolderPanel in 
        // a BorderLayout.PAGE_START location, so the rows don't expand unnecessarily
        JPanel outerPanel = new JPanel(new BorderLayout());
        outerPanel.add(rowHolderPanel, BorderLayout.PAGE_START);
        JScrollPane scrollPane = new JScrollPane(outerPanel);
        scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

        JPanel addPanel = new JPanel();
        addPanel.add(new JButton(new AddAction("Add")));

        setLayout(new BorderLayout());
        add(scrollPane, BorderLayout.CENTER);
        add(addPanel, BorderLayout.PAGE_END);
    }

    @Override
    public Dimension getPreferredSize() {
        if (isPreferredSizeSet()) {
            return super.getPreferredSize();
        }
        return new Dimension(PREF_W, PREF_H);
    }

    private class AddAction extends AbstractAction {
        public AddAction(String name) {
            super(name);
            int mnemonic = (int) name.charAt(0);
            putValue(MNEMONIC_KEY, mnemonic);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            JPanel panel = new JPanel();
            panel.add(new JLabel("Foo"));
            panel.add(Box.createHorizontalStrut(25));
            panel.add(new JButton("Bar"));
            panel.setBorder(BorderFactory.createLineBorder(Color.BLACK, 3));

            rowHolderPanel.add(panel);
            rowHolderPanel.revalidate();
            rowHolderPanel.repaint();
        }
    }

    private static void createAndShowGui() {
        JFrame frame = new JFrame("Add Row A");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(new AddRowA());
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}

但是,您最好还是使用 JTable...例如:

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;

public class AddRowB extends JPanel {
    private MyTableModel tableModel = new MyTableModel();
    private JTable table = new JTable(tableModel);

    public AddRowB() {
        JPanel addPanel = new JPanel();
        addPanel.add(new JButton(new AddAction("Add")));

        setLayout(new BorderLayout());
        add(new JScrollPane(table));
        add(addPanel, BorderLayout.PAGE_END);
    }

    private class AddAction extends AbstractAction {
        public AddAction(String name) {
            super(name);
            int mnemonic = (int) name.charAt(0);
            putValue(MNEMONIC_KEY, mnemonic);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            RowData row = new RowData("Type", "Address", "User ID");
            tableModel.addRow(row);
        }
    }

    private static void createAndShowGui() {
        JFrame frame = new JFrame("Add Row B");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(new AddRowB());
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}

class MyTableModel extends DefaultTableModel {
    private static final String[] COLUMNS = {"Type", "Address", "User ID"};

    public MyTableModel() {
        super(COLUMNS, 0);
    }

    public void addRow(RowData rowData) {
        Object[] row = {
                rowData.getType(),
                rowData.getAddress(),
                rowData.getUserID()
        };
        addRow(row);
    }
}

class RowData {
    String type;
    String address;
    String userID;

    public RowData(String type, String address, String userID) {
        this.type = type;
        this.address = address;
        this.userID = userID;
    }

    public String getType() {
        return type;
    }

    public String getAddress() {
        return address;
    }

    public String getUserID() {
        return userID;
    }

}