Chapter 31. The JList Class

The JList class is a more powerful replacement for the AWT's List class. A JList can be instantiated using a Vector or an array to represent its contents. It does not support scrolling and thus must be added to a JScrollPane to allow scrolling to take place.

In the simplest program that you can write using a JList, you

  1. add a JScrollPane to the Frame,

  2. create a Vector of data,

  3. create a JList using the Vector, and

  4. add the JList to the JScrollPane's viewport.

This program is shown next.

    JPanel jp = new JPanel();        //a panel in Frame
    getContentPane().add(jp); 

    //create a scroll pane
    JScrollPane sp = new JScrollPane();
    jp.add(sp);                       //add the pane to the layout
    Vector dlist = new Vector();      //create a Vector
    dlist.addElement("Anchovies");    //and add data
    dlist.addElement("Bananas");
    dlist.addElement("Cilantro");
    dlist.addElement("Doughnuts");
    dlist.addElement("Escarole");
    JList list = new JList(dlist);    //create a list with data
    sp.getViewport().add(list);       //add the list to the
                                        Jscrollpane

This produces the display shown in Figure 31.1.

A simple JList.

Figure 31.1. A simple JList.

You could just as easily use an array instead of a Vector and get the same result.

List Selections and Events

You can set the JList to allow users to select a single line, multiple contiguous lines, or separated multiple lines by using the setSelectionModemethod, where the arguments can be

SINGLE_SELECTION
SINGLE_INTERVAL_SELECTION
MULTIPLE_INTERVAL_SELECTION

You can then receive these events by using the addListSelectionListener method. Your program must implement the following method from the ListSelectionListener interface:

public void valueChanged(ListSelectionEvent e)

In the following example, we display the selected list item in a text field:

public void valueChanged(ListSelectionEvent e)    {
    text.setText((String)list.getSelectedValue());

This is shown in Figure 31.2.

A JList with the selected item displayed in the JTextField.

Figure 31.2. A JList with the selected item displayed in the JTextField.

Changing a List Display Dynamically

For a list to change dynamically while your program is running, the problem is somewhat more involved because the JList displays the data that it is initially loaded with and does not update the display unless you tell it to. One simple way to accomplish this is to use the setListData method of JList to keep passing it a new version of the updated Vector after you change it. In the JListDemo.java program, the contents of the top text field are added to the list each time that the Add button is clicked. All of this place in the action routine for that button.

public void actionPerformed(ActionEvent e)      {
    dlist.addElement(text.getText());    //add text from the field
    list.setListData(dlist);        //send a new Vector to the list
    list.repaint();                 //and tell it to redraw
}

One drawback to this solution is that you are passing the entire Vector to the list each time and it must update its entire contents each time rather than updating only the portion that changed. This brings us to the underlying ListModel that contains the data that the JList displays.

When you create a JList using an array or a Vector, the JList automatically creates a ListModel object that contains those data. Each ListModel object is a concrete subclass of the AbstractListModel class that defines the following methods:

void fireContentsChanged(Object source, int index0, int index1);
void fireIntervalAdded(Object source, int index0, int index1);
void fireIntervalRemoved(Object source, int index0, int index1);

The AbstractListModel class is declared as abstract, so you must subclass it and create your own class, but there are no abstract methods that you must implement. All of them have default implementations. You need only call those fire methods that your program will be using, for example, the fireIntervalAdded method in the following. The ListModel is an object that contains the data (in a Vector or other suitable structure) and notifies the JList whenever it changes. Here, the ListModel is the following:

class JListData extends AbstractListModel {
    private Vector dlist;                 //the color name list  

    public JListData()     {
        dlist = new Vector();
        makeData();
    }
    public int getSize()     {
        return dlist.size();
    }
    private Vector makeData()
    {
        dlist = new Vector();             //create the Vector
        dlist = addElement("Anchovies")   //and add data
         //then add the rest of the names as before
        return dlist; 

    }
    public Object getElementAt(int index)     {
        return dlist.elementAt(index);
    }
    //add a string to the list and tell the list about it
    public void addElement(String s)     {
        dlist.addElement(s);
        fireIntervalAdded(this, dlist.size()-1, dlist.size());
    }
}

The ListModel approach is really an implementation of the Observer pattern discussed in Chapter 15. The data are in one class, the rendering or display methods in another, and the communication between them triggers new display activity.

A Sorted JList with a ListModel

To display a list that is always sorted, you can use the DefaultListModel class to contain the data. This class implements the same methods as the Vector class and notifies the JList whenever the data change. So a complete program for a nonsorted list display can be as follows:

//creates a JList based on an unsorted DefaultListModel
public class ShowList extends JxFrame implements ActionListener {
    String names[]= ("Dave", "Charlie", "Adam", "Edward", "Barry");
    JButton             Next;
    DefaultListModel    ldata;
    int                 index; 

    public ShowList() {
        super("List of names");
        JPanel jp = new JPanel();
        getContentPane().add(jp); 
        jp.setLayout(new BorderLayout());

        //create the ListModel
        ldata = new DefaultListModel();
        //Create the list
        JList nList = new JList(ldata);
        //add it to the scrollpane
        JScrollPane sc = new JScrollPane();
        sc.getViewport().setView(nList);
        jp.add ("Center", sc); 
        JButton Next = new JButton ("Next");

        //add an element when the button is clicked
        JPanel bot = new JPanel();
        jp.add("South", bot);
        bot.add(Next); 
        Next.addActionListener (this); 
        
        setSize(new Dimension(150, 150));
        setVisible(true); 
        index = 0; 
    }
    public void actionPerformed(ActionEvent evt) {
        if(index < names.length)
            1data.addElement (names[index++];
    }

In this program, we add along the bottom a Next button that adds a new name each time that it is clicked. The data are not sorted here, but obviously if we subclass the DefaultListModel, we can have our sorted list and have the elements always sorted, even when new names are added.

So, if we create a class based on DefaultListModel that extends the addElement method and re-sorts the data each time, we'll have our sorted list.

//this list model re-sorts the data every time
public class SortedModel extends DefaultListModel {
    private String[] dataList; 
public void addElement(Object obj) {
     //add to the internal Vector
     super.addElement(obj);
     //copy into the array
     dataList = new String[size()];
     for(int i = 0; i < size(); i++) {
             dataList[i] = (String)elementAt(i);
        }
     //sort the data and copy it back 
     Arrays.sort (dataList); //sort the data
     clear();     //clear out the Vector

     //reload the sorted data
     for(int i = 0; i < dataList.length; i++)
         super.addElement(dataList[i]); 

     //tell JList to repaint
     fireContentsChanged(this, 0, size());
    }
}

Figure 31.3 shows this list. The names are added one at a time in nonalphabetical order each time the Next button is clicked, but it is sorted before being displayed.

Sorted data using SortedListModel.

Figure 31.3. Sorted data using SortedListModel.

Sorting More-Complicated Objects

Suppose now that we want to display both first and last names and want to sort by the last names. In order to do this, we must create an object that holds first and last names but which can be sorted by last name. And how do we do this sort? We could do it by brute force, but in Java any class that implements the Comparable interface can be sorted by the Arrays.sort method. And the Comparable interface consists of only one method:

public int compareTo(Object obj)

Here, the class returns a negative value, zero, or a positive value, depending on whether the existing object is less than, equal to, or greater than the argument object. Thus we can create a Person class with this interface as follows:

public class Person implements Comparable {

    private String frname, lname; 

    public Person(String name) {
         //split the name apart
             int i = name.indexOf (" ");
             frname = name.substring (0, i).trim();
             lname = name.substring (i).trim();
    }
    public int compareTo(Object obj) {
        Person to = (Person)obj;
        return lname.compareTo (to.getLname ());
    }
    public String getLname() {
        return lname;
    }
    public String getFrname() {
        return frname;
    }
    public String getName() {
        return getFrname()+" "+getLname();
    }
}

Note that the compareTo method simply invokes the compareTo method of the last name String objects.

The other change to make is to have the data model return both names. So we extend the getElementAt method.

//data model that uses and sorts Person objects
public class SortedModel extends DefaultListModel {
    private Person[] dataList; 

    public void addElement(Object obj) {
        Person per = new Person((String) obj);
        super.addElement(per);
        dataList = new Person[size()];
    //copy the Person into an array
    for(int i = 0; i < size(); i++) {
        dataList[i] = (Person)elementAt(i);
    }

    //sort them
    Arrays.sort (dataList); 
    //and put them back
      clear();
      for(int i = 0; i < dataList.length; i++)
          super.addElement(dataList[i]);
      fireContentsChanged(this, 0, size());
    }
    public Object getElementAt(int index) {
        //returns both names as a string
        Person p = dataList[index];
        return p.getName();
    }
    public Object get(int index) {
        return getElementAt(index);
    }
}

Figure 31.4 shows the resulting sorted names.

Sorted list using the Comparable interface to sort on last names.

Figure 31.4. Sorted list using the Comparable interface to sort on last names.

Getting Database Keys

One disadvantage of a sorted list is that clicking on the nth element does not correspond to selecting the nth element, since the sorted elements can be in an order that differs from the order in which they were added to the list. Thus if you want to get a database key corresponding to a particular list element (here, a person) in order to display detailed information about that person, you must keep the database key inside the Person object. This is analogous to, but considerably more flexible than, the Visual Basic approach whereby you can keep only one key value for each list element. Here, you could keep several items in the Person object if that is desirable. In Figure 31.5, we double-click on one person's name and pop up a window containing his or her phone number.

A pop-up list showing details appears when a name is double-clicked.

Figure 31.5. A pop-up list showing details appears when a name is double-clicked.

When you double-click on a JList, you must add a mouse listener to the JList object in order to pop up a window. In our code, we create a class called ListMouseListener, which carries out the listening and produces the pop-up. First we add the ListMouseListener by

nList.addMouseListener(new ListMouseListener(nList, ldata, db, this));

where db represents our database and ldata the list data model. Following is the complete mouseListener class:

public class ListMouseListener extends MouseAdapter {
    private JList              nList;
    private DataBase           db;
    private SortedModel        lData;
    private JFrame             jxf; 

    public ListMouseListener(JList list, SortedModel 1data,
                        DataBase dbase, JFrame jf) {
        nList = list;
        db    = dbase;
        jxf   = jf;
        lData = ldata;
    }
    //
    public void mouseClicked(MouseEvent e) {
        if (e.getClickCount () == 2) {
        //a mouse double-click
        //gets the database key for this person
            int index = nList.locationToIndex (e.getPoint());
            int key   = lData.getKey (index);
        //display pop up dialog box
            Details details = new Details(jxf,
                db.getName (key), db.getPhone (key));
            details.setVisible(true);
        }
    }
}

Because the JList control has no specific methods for detecting a mouse double-click, we check the mouseClicked event method to determine whether the click count is 2. If it is, we get the database key for that line and query the database for the name and telephone number of the person with that key. In the example code on the CD-ROM, we simulate the database with a simple text file so that the code example does not become unwieldy.

Adding Pictures in List Boxes

The JList is flexible in yet another way. You can write your own cell rendering code to display anything you want in a line of a list box. So you can include images, graphs, or dancing babies if you want. All you have to do is create a cell rendering class that implements the ListCellRenderer interface. This interface has only one method—getListCellRendererComponent—and is easy to write. By extending the JLabel class, which itself allows for images as well as text, we can display names and images alongside each name. We assume that each Person object now contains the image to display.

public class cellRenderer extends JLabel implements
ListCellRenderer {

    public Component getListCellRendererComponent(JList list, 
            Object value, int index, boolean isSelected,
            boolean hasFocus) {
        Person p = (Person)value;    //get the person
        setText(p.getName ());       //display name
        setIcon(p.getIcon ());       //and image
        if(isSelected)
            setBackground(Color.lightGray );
        else
            setBackground(Color.white);
        return this;
    }
}

We also connect this cell renderer class to the JList with the following method call:

nList.setCellRenderer (new cellRenderer());

The resulting display is shown in Figure 31.6.

A sorted list with pictures added via a custom cell renderer.

Figure 31.6. A sorted list with pictures added via a custom cell renderer.

Programs on the CD-ROM

Program Description  

SwingJListJlistDemo.java

Simple demonstration of JList. 

SwingJListJlistDemo.java

JList, where clicking on an entry copies it to the text field. 

SwingJListJListADemo.java

JList, where entering text in a text field and clicking on the Add button adds data to a Vector and then causes a repaint. 

SwingJListJListMDemo.java

JList, where entering text in a text field and clicking on the Add button adds data to a JListModel. 

SwingJListBaseSortedShowList.java

A simple sorted list. 

SwingJListTwoNamesShowlist.java

A list sorted by two names based on last names. 

SwingJListDatabaseShowList.java

A list sorted with the database key. 

SwingJListImageListShowList.java

A sorted list with images. 
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset