Dynamic width and fixed height of rows in JPanel

1.8k views Asked by At

I am trying to use Java layout managers (I have no external libraries, and am using Java 7) to create a layout where data is provided in rows, where the rows have a fixed height, but the width fills to fit the window. An example is provided below:

Example of layout

The idea is that I will be adding rows throughout the run of the application, and each row will simply be added to the bottom list. If too many rows are added to the window, then a scroll bar will appear (that bit I have sorted).

My issue so far is that if I use a GridLayout for the main JPanel, it will make the rows fill to the entire window, and just make each row smaller as more rows are added. I want it so that regardless of how many rows are added, the rows height will always remain the same.

Each row consists of two columns that should be the same width. So for this I believe a GridLayout would work nicely, but I'm thinking that I need to add the GridLayout to another panel which essentially represents the entire row.

It is preferable that I use Swing layout managers to achieve the desired layout, but if other suggestions are posed, then I will definitely look into them. As numerous people seem to have found in other questions I've seen, Java's layout managers seem to be a bit lacking in some areas, unfortunately I am unable to tell when it is my knowledge that is lacking, rather than the language itself.

1

There are 1 answers

1
MadProgrammer On BEST ANSWER

My first recommendation would be to use a JTable, while it might "seem" complicated, the API has been optimised to be very efficient and handle thousands of rows

If that doesn't meet your needs, I'd consider using GridBagLayout if you don't want to use any 3rd party libraries, the trick is knowing how you might maniplate them to achieve the results you want, for example, the following uses three panels, the main "outter" panel which acts as the primary view and two inner panels. One to hold the content and another to act as a filler so the content is always pushed to the top of the view.

Layout

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Rectangle;
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.JViewport;
import javax.swing.Scrollable;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

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

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

                TestPane tp = new TestPane();
                JButton add = new JButton("Add Row");
                add.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        tp.addRow();
                    }
                });

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

    public class TestPane extends JPanel implements Scrollable {

        private JPanel content;
        private JPanel filler;

        private GridBagConstraints masterConstraints;

        public TestPane() {

            content = new JPanel(new GridBagLayout());
            filler = new JPanel();

            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridwidth = GridBagConstraints.REMAINDER;
            gbc.weightx = 1;
            gbc.fill = GridBagConstraints.HORIZONTAL;

            add(content, gbc);
            gbc.weighty = 1;
            add(filler, gbc);

            masterConstraints = new GridBagConstraints();
            masterConstraints.gridwidth = GridBagConstraints.REMAINDER;
            masterConstraints.weightx = 1;
            masterConstraints.fill = GridBagConstraints.HORIZONTAL;
        }

        public void addRow() {
            JLabel label = new JLabel("This is just a label, but technically, you can anythig you want");
            content.add(label, masterConstraints);
            revalidate();
            repaint();
        }

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

        @Override
        public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
            return 64;
        }

        @Override
        public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
            return 64;
        }

        @Override
        public boolean getScrollableTracksViewportWidth() {
            Container parent = getParent();
            boolean trackWidth = false;
            if (parent instanceof JViewport) {
                trackWidth = parent.getHeight() > getPreferredSize().width;
            }
            return trackWidth;
        }

        @Override
        public boolean getScrollableTracksViewportHeight() {
            Container parent = getParent();
            boolean trackHeight = false;
            if (parent instanceof JViewport) {
                trackHeight = parent.getHeight() > getPreferredSize().height;
            }
            return trackHeight;
        }

    }

}

Have a look at How to Use GridBagLayout for more details

"But, isn't there a easier way?" I hear you asking! Well, you could use the VerticalLayout from SwingLabs SwingX library (or a JTable)