0

First of all I am new to JAVA, started yesterday...so be gentle haha.

In short I am adding a list of JPanels to a JPanel that is set as a viewport of a JScrollPane.

The issue is that when I add an item it divides the remaning space in the JPanel evenly between my rows... Here is how it looks with 2 items added:

enter image description here

And here is how it looks with 3 items added:

enter image description here

After reachnig 10 -ish items the list starts to expand and becomes scrollable. Been searching the internet for a couple of hours now with no luck... any suggestions are welcome!

Here is the source code if it helps:

public class StudentListView extends JScrollPane {

    private  JPanel myList;
    private  EventManager events;
    private  List<StudentModel> students;
    private  StudentRow listRow;

    public StudentListView(List<StudentModel> students, EventManager events) {
        this.students = students;
        this.events = events;

        myList = new JPanel();
        myList.setLayout(new BoxLayout(myList, BoxLayout.Y_AXIS));

        for (int i = 0; i < this.students.size(); i++) {     
            listRow = new StudentRow(students.get(i), events, i);
            listRow.setAlignmentY(Component.TOP_ALIGNMENT);
            myList.add(listRow);
        }

        setViewportView(myList);
    }

    public void updateView(List<StudentModel> students) {
        myList.removeAll();

        this.students = students;
        for (int i = 0; i < this.students.size(); i++) { 
            listRow = new StudentRow(students.get(i), events, i);
            myList.add(listRow);
        }

        myList.revalidate();
        setViewportView(myList);
    }
}

And the ROWS:

public class StudentRow extends JPanel {
    
    public int index;
    private JLabel nameLabel;
    private JLabel ageLabel;
    private JButton editButton;
    private JButton deleteButton;

    public StudentRow(StudentModel student, EventManager events, int index) {
        this.index = index;

        setPreferredSize(new Dimension(400, 30));

        nameLabel = new JLabel();
        nameLabel.setText(student.getName());
        ageLabel = new JLabel();
        ageLabel.setText(Integer.toString(student.getAge()));
        editButton = new JButton("Edit");
        deleteButton = new JButton("Delete");
        
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.fill = GridBagConstraints.WEST;
        add(nameLabel, gbc);
        
        gbc.gridx = 1;
        gbc.gridy = 0;
        gbc.fill = GridBagConstraints.WEST;
        add(ageLabel, gbc);
        
        gbc.gridx = 2;
        gbc.gridy = 0;
        gbc.fill = GridBagConstraints.EAST;
        add(editButton, gbc);
        
        gbc.gridx = 3;
        gbc.gridy = 0;
        gbc.fill = GridBagConstraints.EAST;
        add(deleteButton, gbc);

        // Create component action listeners
        deleteButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                events.notify("removeStudent", student);
            }
        });
    }
}

SOLUTION:

public class StudentListView extends JScrollPane {

    private  JPanel myList;
    private  EventManager events;
    private  List<StudentModel> students;
    private  StudentRow listRow;
    private  JPanel BorderLayoutPanel;

    public StudentListView(List<StudentModel> students, EventManager events) {
        this.students = students;
        this.events = events;

        myList = new JPanel();
        myList.setLayout(new GridLayout(0,1));

        BorderLayoutPanel = new JPanel(new BorderLayout());
        BorderLayoutPanel.add(myList, BorderLayout.PAGE_START);

        for (int i = 0; i < this.students.size(); i++) {  
            listRow = new StudentRow(students.get(i), events, i);
            myList.add(listRow);
        }

        setViewportView(BorderLayoutPanel);
    }

    public void updateView(List<StudentModel> students) {
        myList.removeAll();

        this.students = students;
        for (int i = 0; i < this.students.size(); i++) { 
            listRow = new StudentRow(students.get(i), events, i);
            myList.add(listRow);
        }

        myList.revalidate();
        setViewportView(BorderLayoutPanel);
    }
}
Nex
  • 92
  • 9
  • *B: Tried but did not work * - that is the approach I use. If it didn't work then you didn't implement it correctly. Post a proper [mre] demonstrating the problem. For the MRE all you need is a frame with a scroll pane and your panels. Create a button to add a new row of data. Once the MRE works and you understand the concept, then you change your real code. If you can't get the MRE to work, then you have simple code to post. – camickr Apr 28 '23 at 20:08
  • Maybe this will help: https://stackoverflow.com/a/75674828/131872 – camickr Apr 28 '23 at 20:12

1 Answers1

4

Possible solutions:

  • Use a JTable for tabular data and display

Or

  • Place the JPanel rows into a JPanel that uses a GridLayout
  • And place that GridLayout-using JPanel into the BorderLayout.PAGE_START (also known as BorderLayout.NORTH) position of a BorderLayout-using JPanel
  • Then place that JPanel into the JScrollPane. The rows will all be sized their preferred heights.

Or

  • Use a GridBagLayout, but use a GridBagConstraints.weighty of 0 so that it does not expand.

For example:

enter image description here

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

public class GridPanelExample extends JPanel {
    private static final int PREF_W = 500;
    private static final int PREF_H = 300;
    private JPanel gridPanel = new JPanel(new GridLayout(0, 1)); // 1 column, variable number of rows
    private JPanel BorderLayoutPanel = new JPanel(new BorderLayout());
    private JScrollPane scrollPane = new JScrollPane(BorderLayoutPanel);
    private int index = 0;

    public GridPanelExample() {
        BorderLayoutPanel.add(gridPanel, BorderLayout.PAGE_START);
        setLayout(new BorderLayout());

        for (index = 0; index < 3; index++) {
            StudentRow row = new StudentRow(index);
            gridPanel.add(row);
        }

        JButton addStudentButton = new JButton("Add Student");
        addStudentButton.addActionListener(e -> {
            StudentRow row = new StudentRow(index++);
            gridPanel.add(row);
            gridPanel.revalidate();
            gridPanel.repaint();
        });
        JPanel buttonPanel = new JPanel();
        buttonPanel.add(addStudentButton);

        scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        add(scrollPane, BorderLayout.CENTER);
        add(buttonPanel, BorderLayout.PAGE_END);
    }

    @Override
    public Dimension getPreferredSize() {
        Dimension superSize = super.getPreferredSize();
        int width = Math.max(superSize.width, PREF_W);
        return new Dimension(width, PREF_H);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            GridPanelExample mainPanel = new GridPanelExample();
            JFrame frame = new JFrame("GridPanelExample");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.getContentPane().add(mainPanel);
            frame.pack();
            frame.setLocationByPlatform(true);
            frame.setVisible(true);
        });
    }
}

class StudentRow extends JPanel {

    public int index;
    private JLabel nameLabel;
    private JLabel ageLabel;
    private JButton editButton;
    private JButton deleteButton;

    public StudentRow(int index) {
        this.index = index;
        setLayout(new GridBagLayout());

        String nameText = String.format("name: %03d", index);
        nameLabel = new JLabel(nameText);
        ageLabel = new JLabel();
        ageLabel.setText(Integer.toString(20));
        editButton = new JButton("Edit");
        deleteButton = new JButton("Delete");
        deleteButton.addActionListener(e -> {
            Container parent = getParent();
            parent.remove(this);
            parent.revalidate();
            parent.repaint();
        });

        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.weightx = 1;
        gbc.weighty = 0;
        add(nameLabel, gbc);

        gbc.gridx = 1;
        gbc.gridy = 0;
        gbc.fill = GridBagConstraints.WEST;
        add(ageLabel, gbc);

        gbc.gridx = 2;
        gbc.gridy = 0;
        gbc.fill = GridBagConstraints.EAST;
        add(editButton, gbc);

        gbc.gridx = 3;
        gbc.gridy = 0;
        gbc.fill = GridBagConstraints.EAST;
        add(deleteButton, gbc);

        int gap = 3;
        setBorder(BorderFactory.createEmptyBorder(gap, gap, gap, gap));
    }
}
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • A: I dont want to use JTable as the only allow sectionListener that would not work here, just like JList B: Tried but did not work C: I just cannot get it display the items vertically Do you have any suggestions how to make it vertical? – Nex Apr 28 '23 at 19:46
  • @KrisztianNagy: for example... – Hovercraft Full Of Eels Apr 28 '23 at 20:47
  • 1
    @KrisztianNagy *"JTable as the only allow sectionListener that would not work here"* - in what ways would it not work - looking at the UI, it would seem like a perfect fit (see [this](https://stackoverflow.com/questions/25070511/add-jbutton-to-each-row-of-a-jtable/25071138#25071138) for reasons why I'd prefer it (as a user) over a wall of buttons) – MadProgrammer Apr 28 '23 at 22:20
  • Scroll -> Border -> Grid layout worked from your suggestion @HovercraftFullOfEels . Although I have no idea why.... Updated the question above so you can see the result. – Nex Apr 28 '23 at 23:00