7.2 Representing List Data


Swing 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 Interface

ListModel 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 Properties

The 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.

Table 7-1. ListModel properties

Property

Data type

get

is

set

Default value

elementAti

Object

·

     

size

int

·

     

iindexed

7.2.1.2 Events

The 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.

public abstract void addListDataListener(ListDataListener l)
public abstract void removeListDataListener(ListDataListener l)

Add or remove a specific listener for ListDataEvent notifications.

7.2.2 The AbstractListModel Class

The 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
protected void fireContentsChanged(Object source, int index1, int index2)

Called by subclasses to trigger a ListDataEvent, which indicates that a modification has occurred in the list elements between index1 and index2. index2 can be less than index1. The source parameter provides a reference to the ListModel that signaled the change.

protected void fireIntervalAdded(Object source, int index1, int index2)

Called by subclasses to trigger a ListDataEvent, which indicates that the list elements between index1 and index2 (inclusive) have been added to the list. Assuming that index2 is the greater index, the element previously at index1 in the list is now element index2+1. All subsequent elements are shifted as well. index2 can be less than index1. The source parameter provides a reference to the ListModel that signaled the change.

protected void fireIntervalRemoved(Object source, int index1, int index2)

Called by subclasses to trigger a ListDataEvent, which indicates to a listener that the list elements from index1 to index2 have been removed from the list. Assuming that index2 is the larger index, the element previously at index2+1 now becomes index1, and all greater elements are shifted down accordingly. index2 can be less than index1. The source parameter provides a reference to the ListModel that signaled the change.

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]

[1] For a detailed discussion of this approach and its benefits, see "Item 16: Prefer interfaces to abstract classes" in Joshua Bloch's Effective Java Programming Language Guide (Addison-Wesley).

SDK 1.3 introduced a method to get the list of registered event listeners:

public EventListener[] getListeners(Class listenerType)

You need to pass in the type of listener you're interested in, which is generally the ListDataListener.class, and you need to cast the result to that specific type.

SDK 1.4 introduced a simpler way to do the same thing:

public ListDataListener[] getListDataListeners( )

Return an array of all the list data listeners that have been registered.

7.2.3 The DefaultListModel Class

Swing 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 Properties

The 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.

Table 7-2. DefaultListModel properties

Property

Data type

get

is

set

Default value

elementAti

Object

·

 

·

 

empty

boolean

 

·

 

true

size

int

·

 

·

0

iindexed

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
public DefaultListModel( )

Create an empty vector to be used as the list model.

7.2.3.3 Methods
public void copyInto(Object anArray[])

Copy all of the objects in the list into the array anArray, which must be large enough to hold the contents of the model.

public void trimToSize( )

Collapse the capacity of the list to match its current size, removing any empty storage.

public void ensureCapacity(int minCapacity)

Tell the list to make sure that its capacity is at least minCapacity.

public int capacity( )

Return the current capacity of the list. The capacity is the number of objects the list can hold without reallocating for more space.

public int size( )

Return the number of elements currently contained in the list. It is equivalent to getSize( ).

public Enumeration elements( )

Return an Enumeration that iterates over each of the elements in the list.

public boolean contains(Object elem)

Return an indication of whether the object elem is currently contained in the list.

public int indexOf(Object elem)

Return the first index at which the object elem can be found in the list, or -1 if the object is not contained in the list.

public int indexOf(Object elem, int index)

Return the first index at which the object elem can be found in the list, beginning its search at the element specified by index and moving forward through the list. The method returns -1 if the object is not contained in the list at or beyond index.

public int lastIndexOf(Object elem)

Return the last index at which the object elem can be found in the list. The method returns -1 if the object is not contained in the list.

public int lastIndexOf(Object elem, int index)

Return the last index at which the object elem can be found in the list, searching backwards from the element specified by index to the front of the list. The method returns -1 if the object is not contained in the list at or before index.

public Object elementAt(int index)

Return a reference to the object at the specified index. It is equivalent to getElementAt(index).

public Object firstElement( )

Return a reference to the first object in the list.

public Object lastElement( )

Return a reference to the last object in the list.

public void removeElementAt(int index)

Remove the element at the specified index. The method then fires off a ListDataEvent to all registered listeners, describing the element that was removed.

public void insertElementAt(Object obj, int index)

Insert the object obj into the list at the given index, incrementing the index of the element previously at that index and any elements above it. (That is, it adds obj before the element at index .) The total size of the list is increased by one. The method then fires off a ListDataEvent to all registered listeners, describing the element that was inserted.

public void addElement(Object obj)

Add the object obj to the end of the list and fire off a ListDataEvent to all registered listeners, describing the element that was appended.

public boolean removeElement(Object obj)

Attempt to remove the first occurrence of the object obj from the list, returning true if successful and false if no such object existed in the list. If the method is successful, the indices of all later elements are decremented, and the size of the list is reduced by one. The method then fires off a ListDataEvent to all registered listeners, describing the element that was removed.

public void removeAllElements( )

Remove all the elements from the list. It then fires off a ListDataEvent, indicating that the entire range was removed.

public String toString( )

Provide a comma-separated list of each element currently in the list.

public Object[] toArray( )

Return the contents of the list as an array of type Object. It is functionally equivalent to the copyInto( ) method, except that it allocates an array of the appropriate size and returns it.

public Object get(int index)

Equivalent to getElementAt(index).

public Object set(int index, Object element)

Equivalent to setElementAt(element, index).

public void add(int index, Object element)

Equivalent to insertElementAt(element, index).

public Object remove(int index)

Equivalent to removeElementAt(index).

public void clear( )

Equivalent to removeAllElements( ).

public void removeRange(int fromIndex, int toIndex)

Remove all elements between the first and second index (including the boundary elements) from the list. The method fires a ListDataEvent describing the interval that was removed.

7.2.3.4 A JList with changing contents

Here'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
figs/swng2.0705.gif

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.

[2] Perhaps by the time you read this, the bug will have been fixed, but it was reported against version 1.2.2 and was still present in 1.4.1 as this book went to press.

7.2.4 ListDataEvent

ListDataEvent 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 Properties

The 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.

Table 7-3. ListDataEvent properties

Property

Data type

get

is

set

Default value

index0

int

·

     

index1

int

·

     

sourceo

Object

·

     

type

int

·

     

ooverridden

7.2.4.2 Constants

Table 7-4 lists the event type constants used by the ListDataEvent.

Table 7-4. Constants for ListDataEvent

Constant

Data type

Description

CONTENTS_CHANGED

int

The elements between the two indices (inclusive) have been altered.

INTERVAL_ADDED

int

The elements now between the two indices (inclusive) have just been inserted into the list.

INTERVAL_REMOVED

int

The elements previously between the two indices (inclusive) have now been removed from the list.

7.2.4.3 Constructor
public ListDataEvent(Object source, int type, int index0, int index1)

Take a reference to the object that is firing this event, as well as the event type and bounding indices.

7.2.4.4 Method
public String toString( )

SDK 1.4 added a toString method that provides useful debugging information about the contents of the event, which is suitable for logging. The String returned includes the values of all the event's properties other than source.

7.2.5 The ListDataListener Interface

The 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
public abstract void intervalAdded(ListDataEvent e)

Called after the range of elements specified in the ListDataEvent has been added to the list. The specified interval includes both endpoints. Listeners may want to query the source of the event for the contents of the new interval.

public abstract void intervalRemoved(ListDataEvent e)

Called after the range of elements specified in the ListDataEvent has been deleted. The specified interval includes both endpoints.

public abstract void contentsChanged(ListDataEvent e)

Called when the range of elements specified in the ListDataEvent has been altered. The specified interval includes both endpoints, although not all elements are guaranteed to have changed. Listeners may want to query the source of the event for the contents of the range.



Java Swing
Graphic Java 2: Mastering the Jfc, By Geary, 3Rd Edition, Volume 2: Swing
ISBN: 0130796670
EAN: 2147483647
Year: 2001
Pages: 289
Authors: David Geary

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net