I l@ve RuBoard |
![]() ![]() |
7.2 Representing List DataSwing uses one interface and two classes to maintain a model of the list elements. When programming with lists, you often find that you can reuse these classes without modification. Occasionally, you may find it necessary to extend or even rewrite these classes to provide special functionality. In either case, it's important to examine all three in detail. Let's start with the easiest: ListModel. 7.2.1 The ListModel InterfaceListModel is a simple interface for accessing the data of the list. It has four methods: one method to retrieve data in the list, one method to obtain the total size of the list, and two methods to register and unregister change listeners on the list data. Note that the ListModel interface itself contains a method only for retrieving the list elements — not for setting them. Methods that set list values are defined in classes that implement this interface. 7.2.1.1 PropertiesThe ListModel interface defines two properties, shown in Table 7-1. elementAt is an indexed property that lets you retrieve individual objects from the list; size tells you the total number of elements.
7.2.1.2 EventsThe ListModel interface also contains the standard addListDataListener( ) and removeListDataListener( ) event subscription methods. These methods accept listeners that notify when the contents of the list have changed. A ListDataEvent should be generated when elements in the list are added, removed, or modified. ListDataEvent and the ListDataListener interface are discussed later in this chapter.
7.2.2 The AbstractListModel ClassThe AbstractListModel class is a skeletal framework to simplify the life of programmers who want to implement the ListModel interface. It provides the required addListDataListener( ) and removeListDataListener( ) event registration methods. It also provides three protected methods that subclasses can use to fire ListDataEvent objects. These methods are triggered when an addition, subtraction, or modification to the list data has taken place. Note that a ListDataEvent is not the same as a PropertyChangeEvent, which is more general in nature. (ListDataEvent is covered later in this chapter.) 7.2.2.1 Methods
Although the AbstractListModel class completes the event framework defined by the list model interface, it does not implement the remaining two methods of the ListModel interface: getSize( ) and getElementAt( ). Instead, it defines these as abstract (making the entire class abstract), leaving the actual implementation choices of the list storage to a subclass, such as DefaultListModel. Providing an abstract class that takes care of the mundane tasks required by an interface is characteristic of the useful Skeletal Implementation design pattern found throughout Java's Collections classes.[1]
SDK 1.3 introduced a method to get the list of registered event listeners:
SDK 1.4 introduced a simpler way to do the same thing:
7.2.3 The DefaultListModel ClassSwing provides a default implementation of the ListModel interface called DefaultListModel . This class is based on the java.util.Vector class, a resizable array of objects that has been around since the early days of Java (the comments keep saying that there are plans to replace this with a more modern Collection-based implementation, but it hasn't happened yet). A majority of the methods of the DefaultListModel class are identical to those of Vector, with the added (and necessary) feature that those methods fire a ListDataEvent each time the vector changes. DefaultListModel extends AbstractListModel to take advantage of its listener-list management features. 7.2.3.1 PropertiesThe DefaultListModel class has three properties, shown in Table 7-2. The size property indicates how many elements are currently stored in the list. You can use the setSize( ) method to alter the size of the list. If the new size is larger than the previous size, the additional elements are populated with null references, and the method fires a ListDataEvent describing the range that was added. If the new size is smaller, the list is truncated, and the method fires a ListDataEvent describing the range that was removed.
The empty property is a boolean that indicates whether the list has no elements. elementAt is an indexed property that you can use to access the list elements. If you set a new element using the setElementAt( ) method, the method fires a ListDataEvent describing the element that was changed. 7.2.3.2 Constructor
7.2.3.3 Methods
7.2.3.4 A JList with changing contentsHere's a simple program that dynamically adds and removes elements from a JList. To do so, we work with the DefaultListModel that keeps track of the list's contents. // ListModelExample.java // import java.awt.*; import java.awt.event.*; import javax.swing.*; public class ListModelExample extends JPanel { JList list; DefaultListModel model; int counter = 15; public ListModelExample( ) { setLayout(new BorderLayout( )); model = new DefaultListModel( ); list = new JList(model); JScrollPane pane = new JScrollPane(list); JButton addButton = new JButton("Add Element"); JButton removeButton = new JButton("Remove Element"); for (int i = 0; i < 15; i++) model.addElement("Element " + i); addButton.addActionListener(new ActionListener( ) { public void actionPerformed(ActionEvent e) { model.addElement("Element " + counter); counter++; } }); removeButton.addActionListener(new ActionListener( ) { public void actionPerformed(ActionEvent e) { if (model.getSize( ) > 0) model.removeElementAt(0); } }); add(pane, BorderLayout.NORTH); add(addButton, BorderLayout.WEST); add(removeButton, BorderLayout.EAST); } public static void main(String s[]) { JFrame frame = new JFrame("List Model Example"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setContentPane(new ListModelExample( )); frame.setSize(260, 200); frame.setVisible(true); } } The result is shown in Figure 7-5. Figure 7-5. Dynamically adding and removing elements from a list![]() This example demonstrates a few important concepts. First, we instantiated our own DefaultListModel instead of using the default provided with the JList. If we hadn't done this, we wouldn't have been able to add anything to the list since the ListModel interface doesn't provide any methods to add or remove items. Working with your own instantiation is generally easier when you need to make runtime changes to any model — again, assigning new models is a benefit of the MVC architecture in Swing. We've provided two ways for changing the list's contents: the Add Element button and the Remove Element button at the bottom. Clicking on Add Element calls our actionPerformed( ) method and appends an element to the end of the list. Clicking on Remove Element calls the same method and deletes an element from the front of the list. After either button is pressed, the JList is notified of the change in the model and updates itself automatically. If you watch carefully, you can see the scrollbar thumb grow or shrink as the list size changes. Try selecting some elements, then click on the Remove Element button a couple of times. Note that the list model and selection models communicate: as the top element is removed and the others move up, the selection moves too, in order to keep the same elements selected even though their indices have changed. This is an example of objects collaborating through event listeners, which you'll find throughout Swing. There is one little bug, though.[2] The selection model's lead and anchor positions are not updated when elements are moved around. Although there's no visible evidence of this, you can prove it by running the program, clicking on Element 3, clicking Remove Element twice, then Shift-clicking on Element 7. You'd expect to see the range from Element 3 (which you last selected, and which was selected before your Shift-click) to Element 7 become highlighted. Instead, you end up with just the range from Element 5 (which is now positioned where you clicked before removing any elements) through Element 7 as the new selection.
7.2.4 ListDataEventListDataEvent is an extension of java.util.EventObject that holds information about a change in the list data model. The event describes the nature of the change as well as the bounding indices of the elements involved. However, it does not send the actual elements. Listeners must query the source of the event if they're interested in the new contents of the affected elements. There are three types of changes that can occur to the list data: elements can be altered, inserted, or removed from the list. Note that the indices passed in form a closed interval (i.e., both indices are included in the affected range). If a ListDataEvent claiming that list elements have been altered is received, the bounding indices typically describe the smallest range of data elements that have changed. If elements have been removed, the indices describe the range of elements that have been deleted. If elements have been added, the indices describe the new elements that have been inserted into the list. 7.2.4.1 PropertiesThe ListDataEvent contains four properties, each with its own accessor, as shown in Table 7-3. The source property indicates the object that is firing the event. The type property represents the type of change that has occurred, represented by one of the constants in Table 7-4. The index0 and index1 properties outline the range of affected elements. index0 does not need to be less than index1 for the ListDataEvent to be valid.
7.2.4.2 ConstantsTable 7-4 lists the event type constants used by the ListDataEvent.
7.2.4.3 Constructor
7.2.4.4 Method
7.2.5 The ListDataListener InterfaceThe ListDataListener interface, which is the conduit for receiving the ListDataEvent objects, contains three methods. Each method receives a different ListDataEvent type that can be generated. This interface must be implemented by any listener object that wishes to be notified of changes to the list model. 7.2.5.1 Methods
![]() |
I l@ve RuBoard |
![]() ![]() |