简体   繁体   中英

Swing: How to create a scrollable list of JPanels that use no Layout

I am trying to find a way to create a list of JPanels which have no Layout manager.

I found several examples on SO providing solutions to the 'scrollable JPanel list' problem. More specifically, I started from this thread and worked on it.

My current problem is that I am having issues when I remove the layout from the JPanels in the list.

They get added correctly but the scrollbar does not appear when needed, and the panels start overlapping.

Note: I know that layouts are heavily preferred but I find it more straightforward to work with pure X, Y coordinates and a mockup program. Please do not bash me for this...

Here is a small working example (taken and modified from this comment ):

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.MatteBorder;

public class JScrollPanels {
    private int i;
    private JPanel listContainer;

    private void initUI() {
        final JFrame frame = new JFrame(JScrollPanels.class.getSimpleName());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        listContainer = new JPanel();
        listContainer.setLayout(new BoxLayout(listContainer, BoxLayout.Y_AXIS));

        frame.add(new JScrollPane(listContainer), BorderLayout.CENTER);
        JButton button = new JButton("Add");
        button.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                final JPanel newPanel = new JPanel();
                newPanel.setLayout(null);

                newPanel.setSize(272, 110);

                JLabel name = new JLabel("Name " + i++);
                name.setBounds(18, 13, 84, 21);
                newPanel.setBorder(new MatteBorder(0, 0, 1, 0, Color.GRAY));

                JLabel date = new JLabel("12/11/2014");

                date.setBounds(10, 44, 123, 21);

                JButton btn= new JButton(">");
                btn.addActionListener( new NameListener(name, date) );

                btn.setBounds(205, 44, 48, 30);

                newPanel.add(name);
                newPanel.add(date);
                newPanel.add(btn);

                listContainer.add(newPanel);
                listContainer.revalidate();

                // Scroll down to last added panel
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        newPanel.scrollRectToVisible(newPanel.getBounds());
                    }
                });
            }
        });
        frame.add(button, BorderLayout.PAGE_END);

        frame.setSize(272, 300);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (UnsupportedLookAndFeelException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new JScrollPanels().initUI();
            }
        });
    }

    public class NameListener implements ActionListener {

        private JLabel name;
        private JLabel date;

        public NameListener(JLabel name, JLabel date) {
            this.name = name;
            this.date = date;
        }

        public void actionPerformed(ActionEvent e) {
            System.out.println("Clicked " + name.getText() + " on " + date.getText());
        }

    }
}

EDIT:

Fixed by using this:

final JPanel newPanel = new JPanel(){
                    @Override
                    public Dimension getPreferredSize() {
                        return new Dimension(272, 110);
                    }
                };

Thanks, mKorbel!

Again, I still persist in my claim that in the long run, a layout manager will work best. For instance, your GUI could use something perhaps like this:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.swing.*;
import javax.swing.border.Border;

@SuppressWarnings("serial")
public class JScrollPanels2 extends JPanel {
   private static final int PREF_W = 300;
   private static final int PREF_H = 300;
   private int i;
   private JPanel listContainer;

   public JScrollPanels2() {
      JPanel outerWrapperPanel = new JPanel(new BorderLayout());
      listContainer = new JPanel();
      listContainer.setLayout(new BoxLayout(listContainer, BoxLayout.Y_AXIS));
      outerWrapperPanel.add(listContainer, BorderLayout.PAGE_START);

      JPanel buttonPanel = new JPanel(new GridLayout(1, 0, 5, 0));
      buttonPanel.add(new JButton(new AddAction("Add")));

      setLayout(new BorderLayout());
      add(new JScrollPane(outerWrapperPanel), BorderLayout.CENTER);
      add(buttonPanel, 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 evt) {
         listContainer.add(new CellPanel(i));
         listContainer.revalidate();
         listContainer.repaint();
         i++;
      }
   }

   private class CellPanel extends JPanel {

      private final int gap = 4;
      private int index;
      private String name;
      private Date date;
      private final String format = "MM/dd/yyyy";
      private SimpleDateFormat sdf = new SimpleDateFormat(format);

      public CellPanel(int i) {
         this.index = i;
         this.name = "Name " + index;
         setLayout(new GridBagLayout()); 
         date = new Date();
         Border emptyBorder = BorderFactory.createEmptyBorder(gap, gap, gap, gap);
         Border lineBorder = BorderFactory.createLineBorder(Color.black);
         setBorder(BorderFactory.createCompoundBorder(lineBorder, emptyBorder));

         add(new JLabel(name), createGbc(0, 0));
         add(new JLabel(""), createGbc(1, 0));
         add(new JLabel(sdf.format(date)), createGbc(0, 1));
         add(new JButton(new MyBtnAction(">")), createGbc(1, 1));
      }

      private GridBagConstraints createGbc(int x, int y) {
         GridBagConstraints gbc = new GridBagConstraints();
         gbc.gridx = x;
         gbc.gridy = y;
         gbc.gridwidth = 1;
         gbc.gridheight = 1;
         gbc.weightx = 1.0;
         gbc.weighty = 1.0;

         if (x % 2 == 0) {
            gbc.anchor = GridBagConstraints.WEST;
         } else {
            gbc.anchor = GridBagConstraints.EAST;
         }
         return gbc;
      }

      private class MyBtnAction extends AbstractAction {

         public MyBtnAction(String name) {
            super(name);
            // int mnemonic = (int) name.charAt(0);
            // putValue(MNEMONIC_KEY, mnemonic);
         }

         @Override
         public void actionPerformed(ActionEvent evt) {
            System.out.println("Button pressed for " + name);
         }
      }

   }

   private static void createAndShowGui() {
      JScrollPanels2 mainPanel = new JScrollPanels2();

      JFrame frame = new JFrame("JScrollPanels2");
      frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

One of the beauties of use of layout managers is their flexibility. If you wanted to add new components to the inner JPanel, you can easily do so without having to manually recalculate every dang other component's position, if they need to be shifted. Instead, let the layout managers do the heavy lifting for you. Also, while your null-layout using GUI might look good on one platform it will usually look terrible on most other platforms or screen resolutions.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM