58
Chapter 2
C H A P T E R T W O
Lists and Combos
Hacks 13–20
Lists are underrated and underappreciated, and developers who don’t
appreciate JLists often use JTables when they don’t need to. But lists seem
to be making a comeback in desktop applications, and with good reason. A
lot of the data we deal with are single-dimension collections—search results,
recent URLs, downloaded files, etc.—and by making the onscreen version of
them more appealing and more usable, a list is the right way to present this
data to the user.
H A C K
#13
Filter JLists Hack #13
Make your 1,000-item list a lot more manageable.
One of the nicest things you can do with a large list is to make it manage-
able with a filter box. This is a text area that, as you type into it, removes list
elements so that only those that contain the typed text are visible.
The hack to do this basically involves having a list model with two represen-
tations of its contents: everything that is in the list, and a subset with just the
items to be displayed (i.e., those from the first list that match the filter). The
model’s
get methods are then rewired to use only the second list.
The implementation in this hack,
FilteredJList, is a single class with two
inner subclasses:
FilterModel and FilterField. The list owns the field, so a
caller can create the
JList fairly typically and then just ask for the field and
add it wherever it makes sense in the layout.
Start by declaring
FilteredJList as a subclass of JList, and provide a con-
structor and some convenience methods, as seen in Example 2-1.
Filter JLists #13
Chapter 2, Lists and Combos
|
59
HACK
Notice that along with holding onto the FilterField, the JList also creates
its own
FilterModel in the constructor, and overrides setModel( ) to ensure
that you can’t push in an incompatible model. It also contains an
addItem( )
method, which really just delegates to the FilterModel.
FilterModel, shown in Example 2-2, is where the magic happens.
Example 2-1. FilterList constructor and convenience methods
public class FilteredJList extends JList {
private FilterField filterField;
private int DEFAULT_FIELD_WIDTH = 20;
public FilteredJList( ) {
super( );
setModel (new FilterModel( ));
filterField = new FilterField (DEFAULT_FIELD_WIDTH);
}
public void setModel (ListModel m) {
if (! (m instanceof FilterModel))
throw new IllegalArgumentException( );
super.setModel (m);
}
public void addItem (Object o) {
((FilterModel)getModel( )).addElement (o);
}
public JTextField getFilterField( ) {
return filterField;
}
Example 2-2. Inner class to provide a filtered model
class FilterModel extends AbstractListModel {
ArrayList items;
ArrayList filterItems;
public FilterModel( ) {
super( );
items = new ArrayList( );
filterItems = new ArrayList( );
}
public Object getElementAt (int index) {
if (index < filterItems.size( ))
return filterItems.get (index);
else
return null;
}
public int getSize( ) {
return filterItems.size( );
}
60
|
Chapter 2, Lists and Combos
#13 Filter JLists
HACK
This model has two ArrayLists for its contents: items contains all the items
that have been added to the model;
filterItems contains only the items that
match the filter. The
getSize( ) and getElementAt( ) methods, required by
ListModel, draw not from the real items list, but from the filterItems list.
The
filterItems list is reconstituted via calls to the refilter( ) method,
which fires off a
ListDataEvent to inform the JList that the contents have
changed and require a repaint.
The refilter( ) method works on the String representation
of the list contents—if your objects are more sophisticated,
you might need to adapt the matching logic; e.g., searching
the content of email messages represented as objects.
There’s also an addItem( ) method, patterned after the equivalent method in
DefaultListModel that, unlike AbstractListModel, assumes mutability (i.e., the
ability to add and remove list contents). A more complete implementation of
this model would probably need to provide equivalents for all of
DefaultListModel’s add and remove methods. Notice that addItem( ) calls
refilter( ) on each add, so that an added item is immediately added to the
visible list, assuming that it matches the search term.
The
FilterField, shown in Example 2-3, is fairly trivial, and it is responsi-
ble for forcing a
refilter when its contents change.
public void addElement (Object o) {
items.add (o);
refilter( );
}
private void refilter( ) {
filterItems.clear( );
String term = getFilterField().getText( );
for (int i=0; i<items.size( ); i++)
if (items.get(i).toString( ).indexOf(term, 0) != -1)
filterItems.add (items.get(i));
fireContentsChanged (this, 0, getSize( ));
}
}
// FilterField inner class listed below
}
Example 2-3. Text field that refilters the model on each keystroke
// inner class provides filter-by-keystroke field
class FilterField extends JTextField implements DocumentListener {
public FilterField (int width) {
super(width);
Example 2-2. Inner class to provide a filtered model (continued)

Get Swing Hacks now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.