JList not behaving as I expected



I’m practicing with a JList which gets populated with random numbers as the “Add Numbers” button is being clicked, and I have run into a few issues I have not been able to fix.

In the code below, I’ve placed a button to toggle the visibility state of the list but it turns out it does not work unless the list is visible initially.

Then I’ve set the list to show 8 rows but only 7 are displayed.

Finally, why is the horizontal scroll pane showing up at all if only 3 decimal places are used so there’s enough room at the right?

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DecimalFormat;

import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

public class JListTest {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        listFrame myFrame=new listFrame();
        myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
        myFrame.setVisible(true);
    }
}

class listFrame extends JFrame {
    private static DecimalFormat df3 = new DecimalFormat("#.###");
    public listFrame() {
        setTitle("List demo");
        setBounds(400,300,400,300);
        listModel = new DefaultListModel();
        randomNumberList=new JList(listModel);
        //**********************************************************************
        // the number of elements visible in the list turns out to be
        // one less than the specified below
        randomNumberList.setVisibleRowCount(8);
        //**********************************************************************
        randomNumberList.setFixedCellWidth(100);
        JScrollPane scrollPanel=new JScrollPane(randomNumberList);
        //**********************************************************************
        // if this line is uncommented the button fails to make the list visible
        // scrollPanel.setVisible(false);
        //**********************************************************************
        JPanel listPanel=new JPanel();
        listPanel.add(scrollPanel);
        JPanel textPanel=new JPanel();      
        toggle=new JButton("Toggle Visible");
        toggle.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                scrollPanel.setVisible(!scrollPanel.isVisible());
            }
        });
        textPanel.add(toggle);
        addNumber=new JButton("Add Numbers");
        addNumber.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                double min=0;
                double max=1000;
                double x= Math.random()*(max-min+1)+min;
                listModel.addElement(df3.format(x));
                randomNumberList.ensureIndexIsVisible(listModel.getSize()-1);
            }
        });
        textPanel.add(addNumber);
        add(listPanel,BorderLayout.NORTH);
        add(textPanel,BorderLayout.SOUTH);
    }
    private JButton toggle,addNumber;
    private JList randomNumberList;
    private DefaultListModel listModel;
}

UPDATE: here’s some code I’ve mentioned in a comment below where the setVisibleRowCount does work:

import java.awt.BorderLayout;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

public class JList_ShowMonths {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        JFrame myLFrame=new MonthFrame();
        myLFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);    
        myLFrame.setVisible(true);
    }
}

class MonthFrame extends JFrame {
    private JList<String> monthList;
    private JPanel listPanel,txtPanel;
    private JLabel selectionLabel;
    public MonthFrame() {
        setTitle("Display the month names");
        setBounds(400,300,400,300);
        String [] months= {"January","February","March","April","May","June",
                "July","August","September","October","November","December"};
        monthList=new JList<String>(months);
        monthList.setVisibleRowCount(4);
        JScrollPane monthScroll=new JScrollPane(monthList);
        listPanel=new JPanel();
        listPanel.add(monthScroll);
        monthList.addListSelectionListener(new ListSelectionListener(){
            public void valueChanged(ListSelectionEvent e) {
                List<String> values=monthList.getSelectedValuesList();
                StringBuilder text=new StringBuilder("Selected month: ");
                for (String element:values) {
                    String word=element;
                    text.append(word);
                    text.append(" ");
                }
                selectionLabel.setText(text.toString());
            }
        });
        txtPanel=new JPanel();
        selectionLabel=new JLabel("Mes seleccionado:");
        txtPanel.add(selectionLabel);
        add(listPanel,BorderLayout.NORTH);
        add(txtPanel,BorderLayout.SOUTH);
    }
}

Answer

First the code. Explanations after the code.

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;

import java.text.DecimalFormat;

import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

public class LstFrame extends JFrame {
    private static DecimalFormat df3 = new DecimalFormat("#.###");

    private JButton toggle, addNumber;
    private JList<Object> randomNumberList;
    private DefaultListModel<Object> listModel;

    public LstFrame() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setTitle("List demo");
        listModel = new DefaultListModel<Object>();
        randomNumberList = new JList<>(listModel);
        randomNumberList.setVisibleRowCount(8);
        randomNumberList.setFixedCellWidth(100);
        JScrollPane scrollPanel = new JScrollPane(randomNumberList);
        scrollPanel.setPreferredSize(new Dimension(110, 100));
        scrollPanel.setVisible(false);
        JPanel textPanel = new JPanel();
        toggle = new JButton("Toggle Visible");
        toggle.addActionListener(e -> {
            scrollPanel.setVisible(!scrollPanel.isVisible());
            revalidate();
        });
        textPanel.add(toggle);
        addNumber = new JButton("Add Numbers");
        addNumber.addActionListener(e -> {
            double min = 0;
            double max = 1000;
            double x = Math.random() * (max - min + 1) + min;
            listModel.addElement(df3.format(x));
            randomNumberList.ensureIndexIsVisible(listModel.getSize() - 1);
        });
        textPanel.add(addNumber);
        add(scrollPanel, BorderLayout.CENTER);
        add(textPanel, BorderLayout.SOUTH);
        setSize(260, 240);
        setLocationByPlatform(true);
        setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(() -> new LstFrame());
    }
}

If a component is initially invisible, it is not painted, nor are the rest of the components painted in such a way as to leave space for the invisible component. Hence, in the above code, I set the size of the JFrame. I simply played around with the numbers until I found suitable values.

Yes, the default layout manager for [the content pane of] JFrame is BorderLayout. Only the center component, in BorderLayout, is displayed at its preferred size. Hence you should add the JScrollPane (which contains the JList) in the center and not at NORTH. And you should also explicitly set the preferred size of the JScrollPane. Also note that the CENTER component takes up all the space left after the determining the space required for the NORTH, EAST, WEST and SOUTH components. Hence if you resize the JFrame, the size of the JScrollPane will also change.

The JPanel containing the JButtons can safely be added at SOUTH.

When you change the components displayed in a container – by adding or removing components or changing visibility of components – you need to repaint the GUI. Method revalidate is one way to do this. Refer to the ActionListener for toggle.

I don’t know how to create an animated GIF so I can’t add one that demonstrates the above code in action. I can, however, post separate images showing the different states.

After launching the application.

initially

After pressing Toggle Visibe

list visible

After adding ten [random] numbers – since the preferred height of the JScrollPane is enough to display nine numbers.

scrollbar



Source: stackoverflow