在列表中组织JPanels的最佳方法是什么?

时间:2013-12-10 06:05:28

标签: java swing

我发现自己最近写了很多程序,都需要显示一些数据集。到目前为止,我所想到的最好的方法是制作包含集合中每个项目数据的小型JPanel,并将它们全部放在一个大的JPanel中,然后放入JScrollPane。它的工作和外观与预期一样,但存在一个问题:我似乎无法让较小的JPanel从较大的JPanel的顶部开始。

Example

只有当我在较大的JPanel(红色)中添加少量小型JPanel(绿色)时,问题才会明显。

下面介绍的是我用来制作上述内容的方法,我想知道是否有更好的方法可以做到这一点(列表从顶部开始就像它应该的那样):

  • 我创建了一个扩展JPanel的类,并在其中添加我想要显示的所有数据。我们称之为" SmallPanel.java"。我没有设定它的大小(稍后会出现)。

  • 在我的主窗口的类中(扩展了JFrame):

    private JScrollPane scrollPane;
    private JPanel panel;
    ...
    scrollPane = new JScrollPane();
    getContentPane().add(scrollPane);
    
    panel = new JPanel();
    panel.setLayout(new GridBagLayout());
    scrollPane.setViewportView(panel);
    ...
    private void addPanel()
    {
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = panel.getComponentCount(); //The new JPanel's place in the list
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.anchor = GridBagConstraints.PAGE_START; //I thought this would do it
        gbc.ipady = 130; //Set the panel's height, the width will get set to that of the container JPanel (which is what I want since I'd like my JFrames to be resizable)
        gbc.insets = new Insets(2, 0, 2, 0); //Separation between JPanels in the list
        gbc.weightx = 1.0;
        SmallPanel smallPanel = new SmallPanel();
        panel.add(smallPanel, gbc);
        panel.revalidate();
        panel.invalidate();
        panel.repaint(); //Better safe than peeved
    }
    
  • 每次要添加面板时都调用addPanel()方法。

修改

最终解决方案(基于MadProgrammer'以下答案):

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.border.BevelBorder;

public class ListPanel extends JPanel
{
    private static final long serialVersionUID = 1L;
    private JPanel fillerPanel;
    private ArrayList<JPanel> panels;

    public ListPanel(List<JPanel> panels, int height)
    {
        this(panels, height, new Insets(2, 0, 2, 0));
    }

    public ListPanel(List<JPanel> panels, int height, Insets insets)
    {
        this();
        for (JPanel panel : panels)
            addPanel(panel, height, insets);
    }

    public ListPanel()
    {
        super();
        this.fillerPanel = new JPanel();
        this.fillerPanel.setMinimumSize(new Dimension(0, 0));
        this.panels = new ArrayList<JPanel>();
        setLayout(new GridBagLayout());
    }

    public void addPanel(JPanel p, int height)
    {
        addPanel(p, height, new Insets(2, 0, 2, 0));
    }

    public void addPanel(JPanel p, int height, Insets insets)
    {
        super.remove(fillerPanel);
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = getComponentCount();
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.anchor = GridBagConstraints.PAGE_START;
        gbc.ipady = height;
        gbc.insets = insets;
        gbc.weightx = 1.0;
        panels.add(p);
        add(p, gbc);
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = getComponentCount();
        gbc.fill = GridBagConstraints.VERTICAL;
        gbc.weighty = 1.0;
        add(fillerPanel, gbc);
        revalidate();
        invalidate();
        repaint();
    }

    public void removePanel(JPanel p)
    {
        removePanel(panels.indexOf(p));
    }

    public void removePanel(int i)
    {
        super.remove(i);
        panels.remove(i);
        revalidate();
        invalidate();
        repaint();
    }

    public ArrayList<JPanel> getPanels()
    {
        return this.panels;
    }

    public static void main(String[] args)
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setMinimumSize(new Dimension(500, 500));
        f.setLocationRelativeTo(null);
        f.getContentPane().setLayout(new BorderLayout());
        final ListPanel listPanel = new ListPanel();
        for (int i = 1; i <= 10; i++)
            listPanel.addPanel(getRandomJPanel(), new Random().nextInt(50) + 50);
        JButton btnAdd = new JButton("Add");
        btnAdd.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent paramActionEvent)
            {
                listPanel.addPanel(getRandomJPanel(), new Random().nextInt(50) + 50);
            }
        });
        JButton btnRemove = new JButton("Remove");
        btnRemove.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent paramActionEvent)
            {
                listPanel.removePanel(0);
            }
        });
        f.getContentPane().add(btnRemove, BorderLayout.NORTH);
        f.getContentPane().add(btnAdd, BorderLayout.SOUTH);
        JScrollPane scrollPane = new JScrollPane();
        scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
        scrollPane.setViewportView(listPanel);
        f.getContentPane().add(scrollPane, BorderLayout.CENTER);
        f.setVisible(true);
    }

    public static JPanel getRandomJPanel()
    {
        JPanel panel = new JPanel();
        panel.setBorder(new BevelBorder(BevelBorder.LOWERED, null, null, null, null));
        panel.add(new JLabel("This is a randomly sized JPanel"));
        panel.setBackground(new Color(new Random().nextFloat(), new Random().nextFloat(), new Random().nextFloat()));
        return panel;
    }
}

2 个答案:

答案 0 :(得分:2)

我发现的最佳解决方案是使用SwingLabs SwingX中的VerticalLayout(可以从here下载)。

您“可以”使用GridBagLayout,其中隐藏的组件位于末尾,其weighty属性设置为1,但这需要管理更多其他工作,因为你需要不断更新所有组件的x / y位置以使其保持原位......

已更新为GridBagLayout示例

enter image description here

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class VerticalLayoutExample {

    public static void main(String[] args) {
        new VerticalLayoutExample();
    }

    public VerticalLayoutExample() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                final TestPane pane = new TestPane();
                JButton add = new JButton("Add");
                add.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        pane.addAnotherPane();
                    }
                });

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new JScrollPane(pane));
                frame.add(add, BorderLayout.SOUTH);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private JPanel filler;
        private int y = 0;

        public TestPane() {

            setBackground(Color.RED);
            setLayout(new GridBagLayout());

            filler = new JPanel();
            filler.setOpaque(false);
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.weighty = 1;
            gbc.gridy = 0;

            add(filler, gbc);

        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 400);
        }

        public void addAnotherPane() {

            JPanel panel = new JPanel(new GridBagLayout());
            panel.add(new JLabel("Hello"));

            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridy = y++;
            gbc.weightx = 1;
            gbc.fill = GridBagConstraints.HORIZONTAL;
            gbc.gridwidth = GridBagConstraints.REMAINDER;
            gbc.insets = new Insets(4, 4, 4, 4);

            add(panel, gbc);

            GridBagLayout gbl = ((GridBagLayout)getLayout());
            gbc = gbl.getConstraints(filler);
            gbc.gridy = y++;
            gbl.setConstraints(filler, gbc);

            revalidate();
            repaint();

        }           
    }        
}

这只是一个概念。正如camickr指出的那样,只要您知道最后一个组件,就可以调整组件的GridBagConstraints,以便列表中的最后一个组件具有weighty 1代替...

尽可能地覆盖GridBagLayout所做的一些事情,例如,我没有使用面板的首选尺寸,而是让GridBagLayout让它填满HORIZONTAL 1}}父容器的宽度......

答案 1 :(得分:2)

您可以使用垂直BoxLayout

确保面板的最大尺寸等于首选尺寸,以便面板不会增长。

编辑:

由于您的类已经有一个自定义面板,您只需覆盖getMaximumSize()方法以返回适当的值。类似的东西:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;

public class VerticalLayoutExample2 {

    public static void main(String[] args) {
        new VerticalLayoutExample2();
    }

    public VerticalLayoutExample2() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                final TestPane pane = new TestPane();
                JButton add = new JButton("Add");
                add.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        pane.addAnotherPane();
                    }
                });

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new JScrollPane(pane));
                frame.add(add, BorderLayout.SOUTH);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private JPanel filler;
        private int y = 0;

        public TestPane() {

            setBackground(Color.RED);
            setLayout(new GridBagLayout());
            setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
            setBorder( new EmptyBorder(4, 4, 4, 4) );
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 400);
        }

        public void addAnotherPane() {
            SmallPanel panel = new SmallPanel();
            panel.setLayout( new GridBagLayout() );
            panel.add(new JLabel("Hello"));
            add(panel);
            add(Box.createVerticalStrut(4));

            revalidate();
            repaint();
        }
    }

    static class SmallPanel extends JPanel
    {
        @Override
        public Dimension getMaximumSize()
        {
            Dimension preferred = super.getPreferredSize();
            Dimension maximum = super.getMaximumSize();
            maximum.height = preferred.height;
            return maximum;
        }
    }
}

我知道您提到过您不想使用lib,但您也可以查看Relative Layout。它只是一个单一的课程。它可以很容易地模仿BoxLayout但更容易使用,因为您不需要覆盖getMaximumSize()方法或将Box组件添加到面板以提供垂直间距。

您可以将其设置为面板的布局,如下所示:

RelativeLayout rl = new RelativeLayout(RelativeLayout.Y_AXIS);
rl.setFill( true ); // fills components horizontally
rl.setGap(4); // vertical gap between panels
yourPanel.setLayout(rl);
yourPanel.add( new SmallPanel(...) );
yourPanel.add( new SmallPanel(...) );