Sun Java Solaris Communities My SDN Account Join SDN
 
Article

Your Backstage at the JDC: Customizing JList Rendering

 
 

Articles Index


How do you perk up a plain-looking list of items in a display so that each category of item stands out? One way to do it is to color code each category. Then the items in each category will show in their respective color. That's what an engineer on the Java Developer Connection (JDC) team recently did to enhance the status panel of an application used to send JDC email. And he did it by using JList, a Java Foundation Class (JFC) Project Swing component, with a customized cell renderer. This article explains how.

JLists and Cell Renderers

JList is one of the commonly used JFC Project Swing components. It's used to create a list, that is, a group of items in a column. Typically the list is designed to present choices that a user can select from. A list uses a cell renderer to present its list items. By default, the list uses a cell renderer that can present strings and icons, in other words text and fixed-size images. The default cell renderer uses the JLabel component.

Here, for example, is a program that uses JList to create a list of three items. The JList object in the program implicitely uses a default cell renderer.

 import javax.swing.JList;
 import javax.swing.JPanel;
 import javax.swing.JFrame;
 import javax.swing.DefaultListModel;
 import java.awt.event.WindowAdapter;
 import java.awt.event.WindowEvent;


 public class PlainList {
 // Create a window      
     public static void main(
       String args[]) {
         JFrame frame = new JFrame(
           "Plain List Demo");
          frame.addWindowListener(
            new WindowAdapter() {
              public void windowClosing(
                WindowEvent e) {
                  System.exit(0);
                    }
          });
                
          // Use a list model that 
          //allows for updates
          DefaultListModel model = 
            new DefaultListModel();
          JList statusList = new JList(model);
                
          // Create some dummy list items
          model.addElement("test line one");
          model.addElement("foo foo foo");
          model.addElement("quick brown fox");

          // Display the list 
          JPanel panel = new JPanel();
          panel.add(statusList);
          frame.getContentPane().add(
            "Center", panel);
          frame.pack();
          frame.setVisible(true);
      }
  }

Notice the use of DefaultListModel in the example program. This is a model class that allows for the items in a list to be changed. You have various choices when you initialize a list. You can initialize it from a vector or array of objects-these implicitely create a ListModel model for the list. Or you can initialize the list from an object that conforms to the ListModel interface. ListModel doesn't allow for changing the contents of the list; you can't add, remove, or update items in the list. If you want to allow for list item changes, you need to use DefaultListModel. If you run the program, here's what you'll see.

Because the program uses the default cell renderer, the list items can only include plain text and icons. In this example, the list is plain text only. Nothing fancy here.

JLists and Customized Cell Renderers

If you want to to do something fancier with a list, like display background color in different cells, you need to do the following:

  • Create a class that models the items in the list. Here's where you specify the attributes of the class such as background color.
  • Create and use a custom cell renderer. This involves two steps:
    1. Create a class that implements the ListCellRenderer interface.
    2. Call the JList method setCellRenderer and specify as an argument to the method an instance of the class.

Create a class that models the items in the list

Here's an example of a class that models the items in a list. The class encapsulates the background color and text.

class ListItem {
            private Color color;
            private String value;

            public ListItem(
               Color c, String s) {
                color = c;
                value = s;
            }
            public Color getColor() {
                return color;
            }
            public String getValue() {
                return value;
            }
        }

Create a class that implements the ListCellRenderer interface

Here's an example of a class that implements the ListCellRenderer interface.

class MyCellRenderer extends JLabel 
  implements ListCellRenderer {

   public MyCellRenderer () {
       // Don't paint behind the component
       setOpaque(true);
   }

   // Set the attributes of the 
   //class and return a reference
   public Component 
     getListCellRendererComponent(JList list,
         Object value, // value to display
         int index,    // cell index
         boolean iss,  // is selected
         boolean chf)  // cell has focus?
   {
   


        // Set the text and 
        //background color for rendering
        setText(((ListItem)value).getValue());
        setBackground(((
          ListItem)value).getColor());

         // Set a border if the 
         //list item is selected
        if (iss) {
            setBorder(BorderFactory.createLineBorder(
              Color.blue, 2));
        } else {
            setBorder(BorderFactory.createLineBorder(
             list.getBackground(), 2));
        }

        return this;
    }
 }

Notice a few things about MyCellRenderer:

  • It extends JLabel, another JFC Project Swing component. JLabel is used to display text or images. Extending JLabel is a convenient way to display selectable text or images.
  • It sets opaque to true in the constructor. This instructs the paint system to use the background of that particular component.
  • It implements getListCellRendererComponent, the only method in the ListCellRenderer interface . This method sets the attributes of the class and then returns a reference to the class.
  • The setText method uses the string value stored in the ListItem object to set the JLabel text, and the setBackground method uses the color value stored in the ListItem object to set the background color of the JLabel.
  • If the list item is selected, that is the iss parameter of getListCellRendererComponent is true, MyCellRenderer uses the setBorder method to set a two-pixel blue border around the list item. The border around the item signals that the item is selected.

Create a class that calls the setCellRender method

Here's a class that creates a JList object using the cell renderer that was created in the previous example. It's a class that calls the setCellRenderer method and specifies as an argument an instance of the MyCellRenderer class.

  public class CustomList {
      // Create a window
      public static void main(
       String args[]) {
          JFrame frame = new JFrame(
           "Custom List Demo");
          frame.addWindowListener(
           new WindowAdapter() {
              public void windowClosing(
              WindowEvent e) {
                  System.exit(0);
              }
          });

          // Use a list model that 
          //allows for updates 
          DefaultListModel model = 
            new DefaultListModel();
          JList statusList = 
            new JList(model);
          statusList.setCellRenderer(
            new MyCellRenderer());
           // Create some dummy list items.
          ListItem li = new ListItem(
           Color.cyan, "test line one");
          model.addElement(li);
          li = new ListItem(
           Color.yellow, "foo foo foo");
          model.addElement(li);
          li = new ListItem(
           Color.green, "quick brown fox");
          model.addElement(li);
  
          // Display the list    
          JPanel panel = new JPanel();
          panel.add(statusList);
          frame.getContentPane().add(
           "Center", panel);
          frame.pack();
          frame.setVisible(true);
      }
  }

CustomList creates an instance of the DefaultListModel class. Remember this allows for changes to the items in the list. It then creates the JList object, statusList. Next it uses the custom cell renderer; notice the method call to statusList.setCellRenderer(newMyCellRenderer()).

A Demonstration

Now to put the ListItem, MyCellRenderer, and CustomList classes into a complete program for demonstration.

     import javax.swing.JList;
     import javax.swing.ListCellRenderer;
     import javax.swing.JLabel;
     import javax.swing.JPanel;
     import javax.swing.JFrame;
     import javax.swing.BorderFactory;
     import javax.swing.DefaultListModel;
     import java.awt.Color;
     import java.awt.Component;
     import java.awt.event.WindowAdapter;
     import java.awt.event.WindowEvent;

  class ListItem {
      private Color color;
      private String value;

      public ListItem(Color c, String s) {
          color = c;
          value = s;
      }
      public Color getColor() {
          return color;
      }
      public String getValue() {
          return value;
      }
  }

 class MyCellRenderer extends 
  JLabel implements ListCellRenderer {

      public MyCellRenderer () {
      // Don't paint behind the component
          setOpaque(true);
      }

 // Set the attributes of the 
 //class and return a reference
  public Component 
   getListCellRendererComponent(JList list,
        Object value, // value to display
        int index,    // cell index
        boolean iss,  // is selected
        boolean chf)  // cell has focus?
  {
      // Set the text and color 
      //background for rendering
      setText(((ListItem)value).getValue(
           ));
      setBackground(((
       ListItem)value).getColor());

      // Set a border if the list 
      //item is selected 
            if (iss) {
                setBorder(
                 BorderFactory.createLineBorder(
                  Color.blue, 2));
            } else {
                setBorder(
                 BorderFactory.createLineBorder(
                  list.getBackground(), 2));
            }
             return this;
        }
     }

 public class CustomList {
        // Create a window
        public static void main(String args[]) {
            JFrame frame = new JFrame(
             "Custom List Demo");
            frame.addWindowListener(
             new WindowAdapter() {
                public void windowClosing(
                 WindowEvent e) {
                    System.exit(0);
                }
            });

            // Use a list model that 
            //allows for updates
            DefaultListModel model = 
             new DefaultListModel();
            JList statusList = 
             new JList(model);
            statusList.setCellRenderer(
              new MyCellRenderer());

                // Create some dummy 
                //list items.
            ListItem li = new ListItem(
             Color.cyan, "test line one");
            model.addElement(li);
            li = new ListItem(
             Color.yellow, "foo foo foo");
            model.addElement(li);
            li = new ListItem(
             Color.green, "quick brown fox");
            model.addElement(li);

            // Display the list
            JPanel panel = new JPanel();
            panel.add(statusList);
            frame.getContentPane(
             ).add("Center", panel);
            frame.pack();
             frame.setVisible(true);
        }
    }
    

If you run the program, you'll see the following display:

Exactly what you want, a list whose list items display different color backgrounds.

And what about the "real thing," the status panel of the application used by the JDC staff? Here's what that looks like.

Customization Doesn't End Here

The customization possibilities don't end here. By having the MyCellRenderer class extend a component other than JLabel, you can create a wide variety of visually engaging lists. Imagine using the JSlider class instead of JLabel to render your list elements. The possibilities are endless. To learn more about the JList class and other Swing components, take a look at the Swing Short Courses.

Coffecup Logo

About the Authors

Gregory Layer is a senior engineer in the JDC group. He is the principle architect of the Bug Parade and the JDC bulk mailing system. He invites any feedback you have about the article or the code, such as enhancements or potential bugs. If you have comments, please send them to : Greg Layer.

Edward Ort is a staff member of the Java Developer Connection. He has written extensively about relational database technology and programming languages.