Richard G Baldwin (512) 223-4758, baldwin@austin.cc.tx.us, http://www2.austin.cc.tx.us/baldwin/

Swing, Simplified Lists in Swing

Java Programming, Lecture Notes # 209, Revised 12/16/98.


Preface

Students in Prof. Baldwin's Advanced Java Programming classes at ACC will be responsible for knowing and understanding all of the material in this lesson beginning with the Spring semester of 1999.

This lesson was originally written on October 4, 1998, using the JDK 1.1.6 download package along with Swing 1.0.3. The lesson was upgraded to JDK 1.2 on 12/16/98. The purpose of this lesson is to illustrate the use of simplified lists in Swing which largely (but not completely) ignore the Model-Delegate paradigm.

Introduction

You may have read that Swing components are designed around a modified version of Model-View-Control (MVC) paradigm in which the view and the control are combined into an object called a delegate.  Delegates represent the model to the user (as a view normally does) and also translate user input into the model (as a control normally does).  As such, the Swing approach is based on a model-delegate paradigm instead of a true MVC paradigm.

You may also have read that Swing gives you the ability to control how widgets look, how they  respond to input, and in some cases, how their data is represented.

All of this is true, and unfortunately, can sometimes lead to confusion.  But, the fact that you can do all of these things doesn't mean that you must do them.  I will discuss the implementation and use of the model-delegate paradigm in detail in subsequent lessons.  The primary purpose of this lesson is to teach you how to largely ignore the model-delegate paradigm and use the Swing JList component in a simple and effective way.

As is my practice, I will use a sample program as a teaching aid.

Sample Program

The name of this program is SwingList01.java and a complete listing appears near the end of this lesson.  It would probably help if you would compile and run it at this point so you can view it on the screen while I discuss it.

The purpose of this program is to show you how to use the Swing JList component in a simple and effective way.  The sample program in this lesson allows you to largely (but not completely) ignore the complexities of the JList class introduced by the model-delegate paradigm.

The program illustrates three different ways to create and populate a list using the JList object:

The program also illustrates the use of the ListSelectionListener interface to instantiate listener objects that listen for the selection of elements in the list, retrieve the values of those elements, and display the values.

Selection of elements (under Windows 95) is accomplished using the mouse or arrow keys and various combinations of the Shift and Ctrl keys.

When the program starts, four scrollable lists appear in a JFrame on the screen. On my screen, the four lists appear in the top left, top right, bottom left, and bottom right of the JFrame object. However, this layout may differ depending on the overall size and shape of the frame, so I will refer to these four lists as  listA, listB, listC, and displayList respectively. (If you change the shape of the frame to make it short and wide, the four lists will appear in a row from left to right, and if you change the shape of the frame to make it tall and narrow, the four lists will appear in a column from top to bottom.)

The list at the top left (listA) was populated at startup by passing an array of data to the constructor.

listA allows you to select one element at a time.  It has a selection listener registered on it to process your selection.  The listener causes your selection to be displayed in the list on the bottom right (displayList).

listA was allowed to establish its own width based on the length of the longest element used to populate the list when it was instantiated.

The list at the top right (listB) was populated at startup by passing a Vector object containing data to the constructor.

listB allows you to select one contiguous group of elements at a time.  The elements that you select are displayed in displayList.

listB was also allowed to establish its own width based on the length of the longest element used to populate the list when it was instantiated.

The list on the bottom left (listC) was constructed by instantiating an empty list and populating it by adding elements one at a time.

listC allows virtually unlimited selection capability.  The elements that you select are displayed in displayList.

The width of listC was specified by invoking the setFixedCellWidth() method on the list.

The list on the bottom right (displayList) was also constructed by instantiating an empty list and adding one element at a time.  At startup, it contains only one element.  However, displayList is used to display selection results for the other lists, so its contents change depending how you make selections in the other lists.  Whenever you make a new selection in any of the other lists, the previous information is removed from displayList and replaced by new information.  Thus, displayList illustrates the process of updating a list at runtime.

displayList does not have a selection listener, so making selections in displayList will have no effect (other than the visual indication that a selection has been made).

displayList was also allowed to establish its own width based on the length of the longest element used to populate the list when it was instantiated.  Later on, if you select elements from another list that are too long to fit in this list, you will see a horizontal scroll bar appear automatically.

The height of each of the four lists was specified by invoking the setVisibleRowCount() method on the list.  When the number of elements in the list is too great to fit in the available vertical space, a vertical scroll bar appears automatically.

This program was tested using JDK 1.1.6 and Swing 1.0.3 under Win95. It was also tested using JDK 1.2.

Interesting Code Fragments

This program, named SwingList01, is fairly repetitious, so I will try to avoid repeating the discussion of similar code fragments.  You can view all of the code in the complete listing of the program that appears later in this lesson.

The first fragment shows the beginning of the controlling class along with the declaration of some instance variables that are used later in the program.  I included the fragment here simply to acquaint you with the names of these variables. You will note that the names of four of these variables correspond to the names by which I referred to the four lists in the above discussion (listA, etc.).

The fragment also contains a main() method that makes it possible to run the program as an application.  The main method simply instantiates an object of the controlling class and the constructor takes over from there.  

public class SwingList01 extends JFrame{
  String testDirectory = "c:/jnk";
  JScrollPane scrollPaneA;
  JScrollPane scrollPaneB;
  JScrollPane scrollPaneC;
  JScrollPane displayScrollPane;

  JList listA;
  JList listB;
  JList listC;
  JList displayList;

  MyListSelectionListener listSelectionListenerA;
  MyListSelectionListener listSelectionListenerB;
  MyListSelectionListener listSelectionListenerC;

  JPanel panelA = new JPanel();
  JPanel panelB = new JPanel();
  JPanel panelC = new JPanel();
  JPanel displayPanel = new JPanel();
  
  //The following is a reference to the top-level JFrame
  // which contains everything else.
  SwingList01 thisObj;
  
  //-----------------------------------------------------//
  
  public static void main(String args[]) {
    //Instantiate the top-level JFrame object.
    new SwingList01();
  }//end main

The next fragment shows the beginning of the constructor which includes the instantiation of the JList object that appears in the top left corner of the outer frame (listA).  The important thing to note is that this version of the JList constructor allows you to populate the list using data from an array when you construct the list.  

  public SwingList01 () {//constructor

    String[] data = {"ax","b","c","d","e","f","g"};
    listA = new JList(data);

As with all other AWT and Swing components, simply instantiating an object doesn't cause it to become visible and usable.  A little more work is required to cause the list to appear on the screen.  There are probably several ways to do this.  I elected to place the list in a scroll pane, place the scroll pane in a panel, and place the panel in the main JFrame object.  The first two of these three steps are shown in the next fragment.  The panel is added to the frame later in the program.

In case you aren't familiar with the use of JScrollPane class, it has several constructors.  This particular constructor is easy to use.  Simply pass a reference to the list to the constructor when you instantiate the scroll pane.  This causes the list to become scrollable.  

    scrollPaneA = new JScrollPane(listA);
    panelA.add(scrollPaneA);

The next fragment is standard Delegation Event Model material.  I included it here simply for completeness.  This fragment instantiates a new ListSelectionListener object and registers it on the JList object.  

    
    listSelectionListenerA = new MyListSelectionListener();
    listA.addListSelectionListener(listSelectionListenerA);

The next fragment specifies the height of the list in rows of data by invoking the setVisibleRowCount() method, and passing the desired number of visible rows as a parameter.

The fragment also specifies that only one element can be selected at a time by invoking the setSelectionMode() method on the list and passing a symbolic constant as a parameter.  There are three such symbolic constants defined in the ListSelectionModel interface.  You can view all three in the complete listing of the program later.

One of the other symbolic constants specifies that a single contiguous group of elements can be selected at a time.  The third symbolic constant specifies that the selection possibilities are virtually unlimited.

This is one place where our simple program must touch on the complexities of the model-delegate paradigm.

The selection rules for a JList object are always controlled by a separate object that implements the ListSelectionModel interface.  I will discuss this in detail in a subsequent lesson.  For the time being, just accept the fact that the symbolic constants are defined in the ListSelectionModel interface.  

    listA.setVisibleRowCount(5);
    listA.setSelectionMode(
                      ListSelectionModel.SINGLE_SELECTION);

As I mentioned earlier, the list is placed in a scroll pane, which is placed in a panel, which is placed in the outer JFrame object.  The next fragment invokes the getPreferredScrollableViewportSize() method on the list and uses the returned information to set the size of the panel to match the size of the scroll pane.

Finally, the panel is added to the outer JFrame object.  If you aren't familiar with the getContentPane() method, you should go back and review an earlier lesson that discusses the use of panes with JFrame objects.  

    Dimension prefSize = 
                listA.getPreferredScrollableViewportSize();
    panelA.setBounds(0,0,prefSize.width,prefSize.height);

    getContentPane().add(panelA);

At this point, the first JList object appears in the outer frame, fully populated and ready to use.

The code to instantiate and populate the remaining three list objects is very similar, so I will only show and discuss code that is different.

The list in the upper right (listB) is populated by passing a Vector object to the Jlist constructor.  First, however, it is necessary to populate the Vector object.  I populated the object with all of the file and directory names in the root of the C-drive on my machine.  Since that is not the topic of discussion here, I will omit any further discussion of that code.  You can view the code in the complete program listing.

The name of the Vector object is theResults.  The next fragment shows the instantiation and population of the list by passing the vector object to the constructor for the JList object.  Once this statement executes, a fully populated JList object exists.  A reference to the list is assigned to the reference variable named listB.  

      listB = new JList(theResults);

The code to display this list in a scroll pane is essentially the same as for the previous list, so I won't discuss it further.

The next fragment instantiates and populates the list shown in the bottom left of the frame (listC).  As you can see, this code is considerably different from the code for the previous two lists.

This fragment instantiates an empty list and adds elements to it, one element at a time.

First let me mention the portion of this code that should be fairly obvious.  Earlier, I told you that the width of this list was set to a fixed width.  That was accomplished by invoking the setFixedCellWidth() method on the JList object.

Now for the code that's not so obvious.  I promised you at the beginning of this lesson that we could "largely" avoid the complexities of the model-delegate paradigm.  I didn't promise you that we could completely avoid those complexities.

IMPORTANT:  The data for every JList object is managed by a separate object that implements the ListModel interface.

You can define your own data management class to use for this purpose, and I will show you how to do that in a subsequent lesson.

There are four overloaded constructors for the JList class.  Two of those constructors are the two that we used to construct the two previous lists.  Those two automatically link the JList object to a suitable ListModel object.

One of the four constructors instantiates the JList object without linking it to a ListModel object, and you have to come back and establish that link later in the program.  This lesson doesn't illustrate the use of that constructor.

The fourth constructor is the one that I used here.  This constructor allows you to instantiate a JList object and link it to a ListModel object by passing a reference to a ListModel object as a parameter to the constructor.

There is a skeleton class named AbstractListModel that implements the ListModel interface.  (For convenience, you might decide to extend this class, instead of implementing the ListModel interface if you are creating your own data model.)

There is a fairly complete class named DefaultListModel that extends the AbstractListModel class, thereby implementing the ListModel interface through inheritance.

In this fragment, I elected to use a DefaultListModel object as the data model for my list.  Therefore, I instantiated a new object of this class and passed it as a parameter to the JList constructor to instantiate my list object.

IMPORTANT:  To manipulate the data in the list, you actually manipulate the data in the data model and the data is displayed as though it belongs to the list (the list behaves as a view on the model).

Therefore, to add elements to the list, I invoked the getModel() method on the list to get a reference to the model, and then invoked the addElement() method on that reference to add new elements to the list. Note the requirement for downcasting the value returned from getModel().

Repetitious code was omitted for brevity.  

    listC = new JList(new DefaultListModel());
    listC.setFixedCellWidth(100);//set width of cells

    ((DefaultListModel)listC.getModel()).addElement("Tom");
    ((DefaultListModel)listC.getModel()).addElement("Dick");
    //Code to add more elements omitted for brevity

Beyond this, the code to display this list in the frame is essentially the same as the previous lists, so I won't discuss it further.

After this, I created a list referred to by a reference variable named displayList and placed a single element in that list.  The code was essentially the same as for the previous list except that I did not register a selection listener on this list.  I won't discuss that code here.  You can view it in the full program listing.  There is also some additional code there to manage the outer frame object which I won't discuss.

That brings us to the definition of the listener class used to instantiate selection listener objects.  This class definition is shown in the next fragment.  Much of this is standard Delegation Event Model material, but there are a couple of statements that are peculiar to JList processing, so I will show and discuss it here.

To begin with, selection events occur when you select an element using either an arrow key or the mouse.  Although it isn't obvious here, (with JDK1.1.6/Swing1.0.3 for Win95) when you select an element with the mouse, you actually get two events in succession.  The first occurs when you press the mouse button and the second occurs when you release the mouse button.  You need to be aware of this because it could cause confusion, depending on what you plan to do in response to the events.

This code fragment uses standard event processing code to get a reference to the source list, and then invokes the getSelectedValues() method on the source to get an array of objects that constitute the elements that were selected to trigger the event in the first place.

My objective was to display the selection information from any of the first three lists in the fourth, or lower right list (displayList).  My approach was to remove all existing elements from displayList when an event occurs, and then add new elements to the list representing the selection information from one of the other lists.

The code is pretty straightforward except for the fact that again, to remove or add elements to the list, I actually had to remove and add them to the data model, not directly to the list.  This required that I invoke the getModel() method on the list to get a reference to the data model.  Using that reference, I removed, and then added elements to the data model, which caused them to appear in the list.

This code first displays the number of elements selected, and then displays the elements themselves.

Some downcasting was also required which always leads to ugly code.  

  class MyListSelectionListener 
                          implements ListSelectionListener{
    public void valueChanged(ListSelectionEvent e){
      JList theList = (JList)e.getSource();
      Object[] selValues = theList.getSelectedValues();
      if(selValues != null){        
        ((DefaultListModel)displayList.getModel()).
                                       removeAllElements();
        ((DefaultListModel)displayList.getModel()).
              addElement("Number:" + selValues.length);
        for(int cnt = 0; cnt < selValues.length; cnt++){
          ((DefaultListModel)displayList.getModel()).
                addElement("Items: " + selValues[cnt]);
        }//end for loop
      }//end if
    }//end valueChanged()
  }//end class MyListSelectionListener

The code in the program that was not highlighted in the fragments above can be viewed in the complete listing of the program that follows in the next section.

Program Listing

/*File SwingList01.java
Rev 12/16/98
The purpose of this program is to illustrate the use 
the Swing JList component in a simplified manner.
The sample programs in this lesson allow you to largely
ignore the complexities of the JList class introduced
by the model-delegate paradigm.

The program illustrates three different ways to create
and populate a list:  
1.  Using an array
2.  Using a Vector
3.  Adding one element at a time

It also illustrates the use of the ListSelectionListener
interface to instantiate listener objects that monitor
for the selection of an element in the list, retrieve the
value of the element, and display that value.

When the program starts, four scrollable lists appear on
the screen.  The list at the top left was populated at
startup by passing an array of data to the constructor.

This list allows you to select one element at a time.  It
has a selection listener registered on to process your
selection.  Your selection is displayed in the list on
the bottom right.

This list was allowed to establish its own width based on
the length of longest element used to populate the list
when it was instantiated.

The list at the top right was populated at startup by
passing a Vector object containing data to the constructor.

This list allows you to select one contiguous group of
elements at a time time.  The elements that you select are
displayed in the list on the bottom right.

This list was also allowed to establish its own width based
on the length of the longest element used to populate the
list when it was instantiated.

The list on the bottom left was constructed by 
instantiating an empty list and populating it by adding 
elements one at a time.

This list allows virtually unlimited selection.  The
elements that you select are displayed in the list on the
bottom right.

The width of this list was specified by invoking the 
setFixedCellWidth() method on the list.

The list on the bottom right was also constructed by
instantiating an empty list and adding one element at a
time.  At startup, it contains only one element.  However,
this list is used to display selection results for the
other lists, so its contents change with time depending
how you make selections in the other lists.  Whenever
your make a new selection in any of the lists, the 
previous information is removed and replaced by new
information.  Thus, this list illustrates the process
of updating a list at runtime.

This list does not have a selection listener, so making
selection in this list will have no effect.

This list was also allowed to establish its own width based
on the length of the longest element used to populate the
list when it was instantiated.  Later on, if you select
elements from the list at the top right that are too long
to fit in the list, you will see horizontal scroll bars
automatically appear.

The height of all four lists was specified by invoking the
listC.setVisibleRowCount() method on the list.  When the
number of elements in the list is too great to fit in the
available vertical space, vertical scroll bars 
automatically appear.

The program runs under JDK 1.1.6 but won't run under
Microsoft jview when compiled using jvc.

See the program named SwingCombo01.java for a similar
program that illustrates the use of a JComboBox component.

Tested using JDK 1.1.6 and Swing 1.0.3 under Win95. Also
tested using JDK 1.2 under Win95.
**********************************************************/
import java.io.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
//import com.sun.java.swing.*;//jdk 1.1 version
//import com.sun.java.swing.tree.*;//jdk 1.1 version
//import com.sun.java.swing.event.*;//jdk 1.1 version
import javax.swing.*;//jdk 1.2 version
import javax.swing.tree.*;//jdk 1.2 version
import javax.swing.event.*;//jdk 1.2 version

//=======================================================//
public class SwingList01 extends JFrame{
  String testDirectory = "c:/jnk";
  JScrollPane scrollPaneA;
  JScrollPane scrollPaneB;
  JScrollPane scrollPaneC;
  JScrollPane displayScrollPane;

  JList listA;
  JList listB;
  JList listC;
  JList displayList;

  MyListSelectionListener listSelectionListenerA;
  MyListSelectionListener listSelectionListenerB;
  MyListSelectionListener listSelectionListenerC;

  JPanel panelA = new JPanel();
  JPanel panelB = new JPanel();
  JPanel panelC = new JPanel();
  JPanel displayPanel = new JPanel();
  
  //The following is a reference to the top-level JFrame
  // which contains everything else.
  SwingList01 thisObj;
  
  //-----------------------------------------------------//
  
  public static void main(String args[]) {
    //Instantiate the top-level JFrame object.
    new SwingList01();
  }//end main
  //-----------------------------------------------------//

  public SwingList01 () {//constructor
    
    getContentPane().setLayout(new FlowLayout());

    //Populate and display listA using an array as input
    String[] data = {"ax","b","c","d","e","f","g"};
    listA = new JList(data);

    scrollPaneA = new JScrollPane(listA);

    panelA.add(scrollPaneA);
    
    listSelectionListenerA = new MyListSelectionListener();
    listA.addListSelectionListener(listSelectionListenerA);
    listA.setVisibleRowCount(5);
    //Allow selection of one list index at a time.
    listA.setSelectionMode(
                      ListSelectionModel.SINGLE_SELECTION);

    //Get size of list and adjust JPanel accordingly
    Dimension prefSize = 
                listA.getPreferredScrollableViewportSize();
    panelA.setBounds(0,0,prefSize.width,prefSize.height);

    getContentPane().add(panelA);
    
    //-----------------
    //Populate and display listB using a Vector as input
    Vector theResults = new Vector();
    
    //Populate a Vector with the files and directories in
    // the root directory
    String dir = "c:/";
    String[] fileList = new File(dir).list();//dir listing

    //Loop and process each file in the directory
    for(int fileCnt = 0;fileCnt<fileList.length;fileCnt++){
      if(new File(
             dir + "/" + fileList[fileCnt]).isDirectory()){
        theResults.addElement("dir: " + fileList[fileCnt]);
      }else{
        theResults.addElement("file: " + fileList[fileCnt]);
      }//end else
    }//end for loop

    if(theResults != null){
      listB = new JList(theResults);

      scrollPaneB = new JScrollPane(listB);

      panelB.add(scrollPaneB);
    
      listSelectionListenerB = 
                             new MyListSelectionListener();
      listB.addListSelectionListener(
                                   listSelectionListenerB);
      listB.setVisibleRowCount(6);
      //Allow selection of one contiguous range of indices
      // at a time.
      listB.setSelectionMode(
             ListSelectionModel.SINGLE_INTERVAL_SELECTION);

      //Get size of list and adjust JPanel accordingly
      prefSize = listB.getPreferredScrollableViewportSize();
      panelB.setBounds(0,0,prefSize.width,prefSize.height);

      getContentPane().add(panelB);
    }//end if
    //-----------------
    
    //Create a list by adding elements one at a time.
    listC = new JList(new DefaultListModel());
    listC.setFixedCellWidth(100);//set width of cells

    ((DefaultListModel)listC.getModel()).addElement("Tom");
    ((DefaultListModel)listC.getModel()).addElement("Dick");
    ((DefaultListModel)listC.getModel()).addElement("Harry");
    ((DefaultListModel)listC.getModel()).addElement("Sue");
    ((DefaultListModel)listC.getModel()).addElement("Mary");
    ((DefaultListModel)listC.getModel()).addElement("Bill");
    ((DefaultListModel)listC.getModel()).addElement("Andy");
    ((DefaultListModel)listC.getModel()).addElement("Alice");
    ((DefaultListModel)listC.getModel()).addElement("Fred");
    ((DefaultListModel)listC.getModel()).addElement("Jane");
    ((DefaultListModel)listC.getModel()).addElement("Willy");

    scrollPaneC = new JScrollPane(listC);

    panelC.add(scrollPaneC);
    
    listSelectionListenerC = new MyListSelectionListener();
    listC.addListSelectionListener(listSelectionListenerC);
    listC.setVisibleRowCount(7);
    //Allow selecttion of one or more contiguous ranges 
    // of indices at a time.
    listC.setSelectionMode(
           ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);

    //Get size of list and adjust JPanel accordingly
    prefSize = listC.getPreferredScrollableViewportSize();
    panelC.setBounds(0,0,prefSize.width,prefSize.height);

    getContentPane().add(panelC);
    //-----------------

    //Create a list to display selections in
    displayList = new JList(new DefaultListModel());

    ((DefaultListModel)displayList.getModel()).
                     addElement("Display Selections Here");

    displayScrollPane = new JScrollPane(displayList);

    displayPanel.add(displayScrollPane);

    displayList.setVisibleRowCount(7);

    //Get size of list and adjust JPanel accordingly
    prefSize = 
          displayList.getPreferredScrollableViewportSize();
    displayPanel.setBounds(
                       0,0,prefSize.width,prefSize.height);
    getContentPane().add(displayPanel);
        
    //-----------------


    //Save a reference to the top-level JFrame object
    // in an instance variable for later use.
    thisObj = this;
    setTitle("Copyright 1998, R.G.Baldwin");  
    setSize(400,350);
    setVisible(true);

    //An anonymous inner class to terminate the program
    // when the
    // user clicks the close button on the frame.
    this.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        System.exit(0);}
    });
    
  }//end constructor
  
  //=====================================================//
  
  //Inner class to monitor for selection events on the
  // JList object.  I get multiple events when
  // I make a selection. This appears to be the result of 
  // a mouse down followed by a mouse up.
  class MyListSelectionListener 
                          implements ListSelectionListener{
    public void valueChanged(ListSelectionEvent e){
      JList theList = (JList)e.getSource();
      Object[] selValues = theList.getSelectedValues();
      if(selValues != null){

        //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        //This is where the code should be placed to take
        // the required action based on the selected values
        
        //Display the selection information in the list
        // named displayList.        
        ((DefaultListModel)displayList.getModel()).
                                       removeAllElements();
        ((DefaultListModel)displayList.getModel()).
              addElement("Number:" + selValues.length);
        for(int cnt = 0; cnt < selValues.length; cnt++){
          ((DefaultListModel)displayList.getModel()).
                addElement("Items: " + selValues[cnt]);
        }//end for loop
        
        
        //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      }//end if
    }//end valueChanged()
  }//end class MyListSelectionListener
}//end class SwingList01

-end-