Search code examples
javaformsswingjlistlistcellrenderer

JList highlight row issue


I'm trying to highlight all rows in a JList which have been "matched" with data elsewhere, and disable that row. When I debug, I can see the correct data is set within cbNameIsMatched. However, what happens instead is that after I create the first match, each row I select in the JList is highlighted instead of the one with the "matched" index. The setEnabled is also setting for all items from the end of the list up to where I click in the list.

class MyListCellRenderer extends JLabel implements ListCellRenderer
        {
            public MyCopybookListCellRenderer()
            {
                setOpaque(false);
            }

            @Override
            public Component getListCellRendererComponent(JList paramList, Object value,
                    int index, boolean isSelected, boolean cellHasFocus) 
            {
                setText(value.toString());

                if(isSelected)
                {
                    setOpaque(true);
                }
                else
                {
                    setOpaque(false);
                }

                if(cbNameIsMatched[index]==2)
                {
                    setBackground(Color.YELLOW);
                    setEnabled(false);                      
                }

                myList.repaint();

                return this;
            }

Solution

  • You would do better extending DefaultListCellRenderer over JLabel as the former already takes care of everything and all you have to do is change the specific things you need. It gives you a "safety net" for the cases you didn't touch.

    public class GetterText extends JFrame {
    
        GetterText() {
    
            JList<String> list = new JList<>(new String[]{"AAAA", "BBBB", "CCCC", "DDDD"});
            list.setCellRenderer(new MyListCellRenderer());
    
            getContentPane().add(list);
            setLocationRelativeTo(null);
            setDefaultCloseOperation(EXIT_ON_CLOSE);
            pack();
            setVisible(true);
        }
    
        private class MyListCellRenderer extends DefaultListCellRenderer {
    
            @Override
            public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
    
                JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
                label.setOpaque(isSelected); // Highlight only when selected
                label.setOpaque(true); // Highlight always
                if(index == 2) { // I faked a match for the second index, put you matching condition here.
                    label.setBackground(Color.YELLOW);
                    label.setEnabled(false);     
                }
                return label;
            }
        }
    
        public static void main(String[] args) {
    
            new GetterText();
        }
    }
    

    Edit: elaboration on the use of super

    super gives a reference to the superclass which you can use to call its methods. When overriding a method of the superclass, calling that superclass's method means "do what you did before", or, "retain the implementation". This is good because you start from a point where everything works as the default and what you have left to do is tweak specific behaviors without the need to take care for all the others.

    In this case, if you return label after

    JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
    

    you would get the default behavior because it is the one being used by the extending DefaultListCellRenderer. The arguments are the same in order to get the same result as the superclass would give. Then I go on to change this default JLabel. What you do is create a new JLabel with no default behavior. Note that I "cheat" here by knowing that the Component returned by DefaultListCellRenderer.getListCellRendererComponent is a JLabel.