Java 如何向JScrollPanel动态添加元素?

Java 如何向JScrollPanel动态添加元素?,java,swing,user-interface,jpanel,jscrollpane,Java,Swing,User Interface,Jpanel,Jscrollpane,我正在尝试使用Java中的Swing制作一个GUI,它可以执行以下操作: 当按下“添加”按钮时,将创建一个JPanel并填充整个GUI宽度。在JPanel内部,将使用告诉特定细节(即名称、用户ID等)的FlowLayout安排JLabel。无论何时按下Add按钮,我都希望在上一个按钮的正下方添加一个新的JPanel,以便JScrollPane在必要时激活其垂直滚动条。基本上,我希望JScrollPane能够在其中动态添加新的JPanel 然而,我在尝试这样做时遇到了一些问题。在JScrollPa

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

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

然而,我在尝试这样做时遇到了一些问题。在JScrollPane中,我无法确定要为面板使用哪个LayoutManager。我尝试过使用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 

}
公共窗口(){
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
立根(100100701400);
contentPane=newjpanel();
setboorder(新的EmptyBorder(5,5,5,5));
setLayout(新的BorderLayout(0,0));
setContentPane(contentPane);
JPanel buttonPanel=新的JPanel();
添加(buttonPanel,BorderLayout.SOUTH);
JButton btnAdd=新JButton(“添加”);
按钮面板添加(btnAdd);
JScrollPane scrollPane=新的JScrollPane();
添加(滚动窗格,BorderLayout.CENTER);
JPanel systemsPanel=新的JPanel();
scrollPane.setViewportView(systemsPanel);
setLayout(新的GridLayout(8,1,0,0));//现在有8行
对于(int i=1;i<50;i++){
systemsPanel.add(新JButton(“测试”),“单元格0”+i);
}
维度dim=Toolkit.getDefaultToolkit().getScreenSize();
this.setLocation(dim.width/2-this.getSize().width/2,dim.height/2-this.getSize().height/2);//使框架居中
}

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

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

例如,我的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;
    }

}

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

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

例如,我的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;
    }

}

请创建并发布一个有效的链接,以便快速获得最佳帮助。这段代码将是小的,自立的,并张贴在这里作为代码格式的文本与您的问题,而不是在一个链接。祝你好运GridLayout应该可以正常工作,但您可能没有正确使用它。再次向我们展示如何在代码中使用它。另外,您确定不想使用JTable吗?如前所述,您应该使用
JTable
。您将无法使用FlowLayout在面板中定位每个组件。也就是说,您的组件不会在列中对齐,因为每个组件的大小可能不同。有关更多信息,请参阅。但查看您的GUI,您似乎真的很想使用JTable,它的列对应于类型、地址、用户id和第二种类型。
我仍然需要其他元素,如JButtons
-请参阅。请创建并发布一个有效的JTable,以便快速获得最佳帮助。这段代码将是小的,自立的,并张贴在这里作为代码格式的文本与您的问题,而不是在一个链接。祝你好运GridLayout应该可以正常工作,但您可能没有正确使用它。再次向我们展示如何在代码中使用它。另外,您确定不想使用JTable吗?如前所述,您应该使用
JTable
。您将无法使用FlowLayout在面板中定位每个组件。也就是说,您的组件不会在列中对齐,因为每个组件的大小可能不同。有关更多信息,请参阅。但查看GUI,您似乎真的很想使用JTable,它的列对应于类型、地址、用户id和第二种类型。
我仍然需要其他元素,如JButtons
-请参阅。