javalistswingjscrollpanejcheckbox

Create a list of JCheckBoxes and put them into a JScollPane (UR Robot - URCap)


It has been a while since I programmed with Java Swing, so I'm not an expert with all it's methods. Just to contextualize what I'm doing, I'm building an URCap UI that I'm using in a UR Cobot (That's basically a UI that allows me to control something installed to the robot like a gripper, for example) and for this UI I have to create around 64 CheckBoxes to be selected by the user and he can check one or more at the same time. So, following tutorial and other questions from StackOverflow I began by creating a list of CheckBoxes private List<JCheckBox> checkBoxList = new ArrayList<JCheckBox>(); And I added each element of this list to a JPanel, then I added the panel to a JScrollPane, like this:

for (JCheckBox checkBox: checkBoxList) {
            panel.add(checkBox);
        }
        
        scrollPane.add(panel);

This is the further that I could get after tirelessly search for help and testing codes that I found, and this logic should (I think) normally work, according to the people that provide them, but in my case, when I build it, the ScrollPane is showed in the GUI, but there's no checkbox, see the below image:

UI using List

I'm not sure what is causing this problem, I don't know if I should use a list of CheckBoxes like this, just defining it as a List, but I tried a lot of different ways, using a JList, an Array JCheckBox[] and others, but they give me the same problem.

I know that besides this error, my code is working, because I tested the scrollPane using a single JCheckBox (see function createJobsPanel1()) and it's successfully rendering the checkbox:

UI using a single JCheckBox

This is my full code and to explain it breafly to you, you only have to focus on the functions createJobsPanelx(), setIOCheckBoxItems() and the global variables where I define my JCheckBox list. The funciont BuildUI() only calls the others to build the UI. Also, the setIOCheckBoxItems() is called by another function that is placed in another class, but just to explain what it does, it sets the JCheckBox list elements to "1, 2, 3, 4, 5 ..." and so on.

package com.sigmaclermont.ploc2dMultObjDetect.impl;

import java.awt.Component;

import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JCheckBox;
import javax.swing.JScrollPane;
import javax.swing.plaf.metal.MetalCheckBoxIcon;

import com.ur.urcap.api.contribution.ContributionProvider;
import com.ur.urcap.api.contribution.ViewAPIProvider;
import com.ur.urcap.api.contribution.program.swing.SwingProgramNodeView;


public class Ploc2dProgramNodeView implements SwingProgramNodeView<Ploc2dProgramNodeContribution>{
    
    private final ViewAPIProvider apiProvider;
    
    public Ploc2dProgramNodeView(ViewAPIProvider apiProvider) {
        this.apiProvider = apiProvider;
    }
    
    
    //private JCheckBox[] checkBoxList = new JCheckBox[64];
    //private JCheckBox checkBoxOrig = new JCheckBox();
    private List<JCheckBox> checkBoxList = new ArrayList<JCheckBox>();

    @Override
    public void buildUI(JPanel panel, ContributionProvider<Ploc2dProgramNodeContribution> provider) {
        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
        panel.add(createDescription("Select the active Jobs:"));
        panel.add(createJobsPanel2(checkBoxList));
    }
    
    /*public void setIOCheckBoxItem1(Integer item) {

        checkBoxOrig.setText(Integer.toString(item));
        checkBoxOrig.setIcon(new MetalCheckBoxIcon() {
            protected int getControlSize() {return 30;}
        });
        checkBoxOrig.setFont(new java.awt.Font("Arial", Font.LAYOUT_LEFT_TO_RIGHT, 20));
        
    }*/
    
    public void setIOCheckBoxItem2(Integer[] items) {
        
        for (Integer item: items) {
            checkBoxList.add(new JCheckBox(Integer.toString(item+1)));
        }
    }
    
    private Box createDescription(String desc) {
        Box box = Box.createHorizontalBox();
        box.setAlignmentX(Component.LEFT_ALIGNMENT);
        JLabel label = new JLabel(desc);
        box.add(label);
        
        return box;
    }
    
    /*private Box createJobsPanel1(final JCheckBox checkBox) {
        Box box = Box.createHorizontalBox();
        box.setAlignmentX(Component.LEFT_ALIGNMENT);
        
        JPanel panel = new JPanel();
        panel.add(checkBox);
        
        JScrollPane scrollPane = new JScrollPane(panel);
        scrollPane.setPreferredSize(new Dimension(100,100));
        scrollPane.setMaximumSize(scrollPane.getPreferredSize());
        scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        
        checkBox.addItemListener(new ItemListener() {
            
            @Override
            public void itemStateChanged(ItemEvent arg0) {
                // TODO Auto-generated method stub
                
            }
        });
        
        box.add(scrollPane);
        
        return box;
    }*/
    
    private Box createJobsPanel2(final List<JCheckBox> checkBoxList) {
        Box box = Box.createHorizontalBox();
        box.setAlignmentX(Component.LEFT_ALIGNMENT);
        
        JScrollPane scrollPane = new JScrollPane();
        scrollPane.setPreferredSize(new Dimension(300,300));
        scrollPane.setMaximumSize(scrollPane.getPreferredSize());
        scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        
        JPanel panel = new JPanel();
        panel.setPreferredSize(new Dimension(250,250));
        panel.setMaximumSize(panel.getPreferredSize());
        panel.setLayout(new GridLayout(8,1));
        
        for (JCheckBox checkBox: checkBoxList) {
            panel.add(checkBox);
        }
        
        scrollPane.add(panel);
        
        for (JCheckBox checkBox: checkBoxList) {
            checkBox.addItemListener(new ItemListener() {
                
                @Override
                public void itemStateChanged(ItemEvent arg0) {
                    // TODO Auto-generated method stub
                    
                }
            });
        }
        
        box.add(scrollPane);
        
        return box;
    }

}


I tried all types of solution, but none worked. I expect a ScrollPane containing 64 checkboxes with text from "1" to "64" where the user can select one or more.

EDIT: Here is the backend code that contains the openView() method, it calls the setIOCheckBoxItems() from the other code and passes the integer array from 1 to 64

package com.sigmaclermont.ploc2dMultObjDetect.impl;

import com.ur.urcap.api.contribution.ProgramNodeContribution;
import com.ur.urcap.api.contribution.program.ProgramAPIProvider;
import com.ur.urcap.api.domain.data.DataModel;
import com.ur.urcap.api.domain.script.ScriptWriter;
import com.ur.urcap.api.domain.undoredo.UndoRedoManager;
import com.ur.urcap.api.domain.undoredo.UndoableChanges;

public class Ploc2dProgramNodeContribution implements ProgramNodeContribution{
    
    private final ProgramAPIProvider apiProvider;
    private final Ploc2dProgramNodeView view;
    private final DataModel model;
    private final UndoRedoManager undoRedoManager;
    
    public Ploc2dProgramNodeContribution(ProgramAPIProvider apiProvider, Ploc2dProgramNodeView view,
            DataModel model) {
        this.apiProvider = apiProvider;
        this.view = view;
        this.model = model;
        this.undoRedoManager = this.apiProvider.getProgramAPI().getUndoRedoManager();
    }

    private Integer[] getOutputItems() {
        Integer[] items = new Integer[64];
        for(int i = 0; i<64; i++) {
            items[i] = i;
        }
        return items;
    }
    
    @Override
    public void openView() {
        view.setIOCheckBoxItems(getOutputItems());
    }

    @Override
    public void closeView() {
        // TODO Auto-generated method stub
        
    }

    @Override
    public String getTitle() {
        return "Ploc2D MultiObj";
    }

    @Override
    public boolean isDefined() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public void generateScript(ScriptWriter writer) {
        // TODO Auto-generated method stub
        
    }

}

EDIT 2: I tried the answer given by Caps and it didn't solve my problem, but it did change something, now, instead of a grey area inside the scrollpane, the area is white, so the panel probably is inside the scrollpane. Here's the screenshot:

New GUI after modifying the code


Solution

  • Here to say that I finally found the solution and I had to do 2 changes in my code for it to work, the first one was to implement @Caps' suggestion in his answer, initializing the Scrollpane with the panel instead of adding it afterwards. So if anyone encounter the same problem, just do this changement.

    JScrollPane scrollPane = new JScrollPane(panel);
    

    The second thing that I discovered is that my JCheckBox list was empty because it wasn't initializing the JCheckBoxes inside the setIOCheckBoxItem2() function

    public void setIOCheckBoxItem2(Integer[] items) {
        
        for (Integer item: items) {
            checkBoxList.add(new JCheckBox(Integer.toString(item+1)));
        }
    }
    

    So when createJobsPanel2(checkBoxList) is called, checkBoxList is empty, tho the panel will be empty as well, setIOCheckBoxItem2() is being called after this function, meaning that my list was being filled after creating the scrollPane, not before, as I thought. The solution was then initialize the checkBoxList in the constructor of the class, where I know for sure that is the first method called in my code before createJobsPanel(), so the checkBoxList will not be empty. After that, in setIOCheckBoxItem2() I just use setText() to change the checkBoxes text and other parameters.

    Thanks you all, here is the working final code:

    package com.sigmaclermont.ploc2dMultObjDetect.impl;
    
    import java.awt.Component;
    
    import java.awt.Dimension;
    import java.awt.Font;
    import java.awt.GridLayout;
    import java.awt.event.ItemEvent;
    import java.awt.event.ItemListener;
    import java.util.ArrayList;
    import java.util.List;
    
    import javax.swing.Box;
    import javax.swing.BoxLayout;
    import javax.swing.DefaultListModel;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.JCheckBox;
    import javax.swing.JComboBox;
    import javax.swing.JScrollPane;
    import javax.swing.JSlider;
    import javax.swing.ListCellRenderer;
    import javax.swing.event.ChangeEvent;
    import javax.swing.event.ChangeListener;
    import javax.swing.plaf.metal.MetalCheckBoxIcon;
    import javax.swing.JList;
    
    import com.ur.urcap.api.contribution.ContributionProvider;
    import com.ur.urcap.api.contribution.ViewAPIProvider;
    import com.ur.urcap.api.contribution.program.swing.SwingProgramNodeView;
    
    
    public class Ploc2dProgramNodeView implements SwingProgramNodeView<Ploc2dProgramNodeContribution>{
        
        private final ViewAPIProvider apiProvider;
        private List<JCheckBox> checkBoxList = new ArrayList<JCheckBox>();
        
        public Ploc2dProgramNodeView(ViewAPIProvider apiProvider) {
            // Here the checkBoxList is initialized
            for (int i = 0; i < 64; i++) {
                checkBoxList.add(new JCheckBox());
            }
            
            this.apiProvider = apiProvider;
        }
        
    
        @Override
        public void buildUI(JPanel panel, ContributionProvider<Ploc2dProgramNodeContribution> provider) {
            panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
            panel.add(createDescription("Select the active Jobs:"));
            panel.add(createJobsPanel(checkBoxList));
        }
        
        public void setIOCheckBoxItems(Integer[] items) {
            // Here I set the text and other parameter of the checkboxes
            for (Integer item: items) {
                checkBoxList.get(item).setText(Integer.toString(item+1));
                checkBoxList.get(item).setIcon(new MetalCheckBoxIcon() {
                    protected int getControlSize() {return 40;}
                });
                checkBoxList.get(item).setFont(new java.awt.Font("Arial", Font.LAYOUT_LEFT_TO_RIGHT, 25));
            }
        }
        
        private Box createDescription(String desc) {
            Box box = Box.createHorizontalBox();
            box.setAlignmentX(Component.LEFT_ALIGNMENT);
            JLabel label = new JLabel(desc);
            box.add(label);
            
            return box;
        }
        
        Box createJobsPanel(final List<JCheckBox> checkBoxList) {
            Box box = Box.createHorizontalBox();
            box.setAlignmentX(Component.LEFT_ALIGNMENT);
            
            JPanel panel = new JPanel();
            panel.setLayout(new BoxLayout(panel,BoxLayout.Y_AXIS));
            
            for (JCheckBox checkBox: checkBoxList) {
                panel.add(checkBox);
            }
            
            JScrollPane scrollPane = new JScrollPane(panel);
            scrollPane.setPreferredSize(new Dimension(300,300));
            scrollPane.setMaximumSize(scrollPane.getPreferredSize());
            scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
            
            
            for (JCheckBox checkBox: checkBoxList) {
                checkBox.addItemListener(new ItemListener() {
                    
                    @Override
                    public void itemStateChanged(ItemEvent arg0) {
                        // TODO Auto-generated method stub
                        
                    }
                });
            }
            
            box.add(scrollPane);
            
            return box;
        }
    
    }
    

    And the resulting UI: Working UI