Archive

Posts Tagged ‘jtable’

Glazed Lists – an essential Java library for lists and tables

May 2, 2012 9 comments

Swing is the built in toolkit for creating user interfaces for Java programs. While these types of standalone desktop applications are becoming less prevalent, perhaps due to increasing functionality of webapps, there are still some industries which are highly reliant on them. If you find yourself creating a Java desktop application, you will probably have to learn Swing, and you will also probably have to learn to display information to the user in list or table form. In standard Java Swing applications, it is difficult, or at least annoying, to do the following three tasks:

  1. Displaying domain specific models
  2. Filtering
  3. Sorting

Glazed Lists is an open source project that makes all three of these tasks trivial. Its primary author, Jesse Wilson, is a current Google employee. Let’s examine each of these aspects in turn.

Provides a simplified API for representing objects within a JTable

Swing uses the Model View Controller paradigm throughout. Thus the table or list merely presents a view for an underlying model data structure. Part of your job in displaying data in a Swing table is to define the TableModel implementation which provides the data for the JTable to display.

Swing provides an AbstractTableModel that does most of the work for you, requiring you only to implement the following methods:

public int getRowCount();
public int getColumnCount();
public Object getValueAt(int row, int column);

Here’s a simple domain model object we might want to visualize in a table:

public class Person {
    int age;
    String name;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public int getAge() { return age; }

    public String getName() { return name; }
}

The logical way of doing that would be two have two columns, one for the age, one for the name. Let’s make a table model for this case:

public class PersonTableModel extends AbstractTableModel {
    private static final String[] columns = {"Name", "Age"};
    private final List people;

    public PersonTableModel(List people) {
        // Make a defensive copy
        this.people = new ArrayList(people);
    }

    public int getRowCount() {
        return people.size();
    }
    public int getColumnCount() {
        return columns.length;
    }
    public Object getValueAt(int row, int column) {
        Person p = people.get(row);
        if (column == 0) {
            return p.getName();
        } else {
            return p.getAge();
        }
    }
}

This certainly works, but it requires a fair bit of boilerplate. Furthermore, the code above does not provide any way of modifying the list of people after it is copied by the TableModel.

Glazed Lists simplifies your life by treating the table not as an arbitrary two dimensional grid, but instead as a collection of rows, where the rows are kept in sync with changes to the domain models that they represent. All you have to do is define how a row is laid out, and Glazed Lists takes care of the rest.

The interface you need to use in order to define how the table looks and which aspects of your model objects are exposed is called [TableFormat][].

The interface is as follows:

  • int getColumnCount() – The number of columns to display.
  • String getColumnName(int column) – Gets the title of the specified column.
  • Object getColumnValue(E baseObject, int column) – Gets the value of the specified field for the specified object.

This should remind you of the TableModel interface presented previously, but note how the getColumnValue method is different – rather than getting a row and column, and forcing you to look up the object corresponding to that row, you are provided the object directly.

Here is a TableFormat which allows Person objects to be easily visible in a JTable:

public class PersonTableFormat implements TableFormat {

    String[] columnNames = {"Name", "Age"};
    private static final int NAME_INDEX = 0;
    private static final int AGE_INDEX = 1;

    public int getColumnCount() { return columnNames.length; }

    public String getColumnName(int column) { return columnNames[i]; }

    public Object getColumnValue(Person baseObject, int column) {
        switch (column) {
            case NAME_INDEX:
                return baseObject.getName();
            case AGE_INDEX:
                return baseObject.getAge();
            default:
                throw new IllegalArgumentException("Expected column 0 or 1, got " + column);
        }
    }
}

While this isn’t too hard to write, it’s still a lot of boilerplate (and not significantly different from the previous example). Glazed Lists makes it even easier than this. The entire class definition above can be replaced with three lines:

TableFormat personTableFormat = GlazedLists.tableFormat(Person.class,
    // Names of the properties to fetch
    new String[] {"name","age"},
    // Names for the columns
    new String[] {"Name", "Age"});

What’s this doing? And how can it do all that I had previously in one line of code? Well, it requires and takes advantage of JavaBeans naming convention. The static function uses reflection to find the methods mapping to properties named “name” and “age”. In this case, it looks for two methods, getName() and getAge(), both of which it finds. (If I didn’t name my methods appropriately, I would get a runtime exception). The second array defines the strings that should be used to identify the corresponding entry in the properties array. In other words, element 0 in the names column is used to identify the property name at index 0.

This TableFormat class alone is insufficient to display data in a table. To do that, you need a class which fulfills the TableModel interface I described previously. Fortunately, Glazed Lists makes this easy.

The fundamental building block of Glazed Lists is the EventList class. It is similar to the ArrayList class in Java, except that it has support for observers. If you’re not familiar with the Observer/Observable design pattern, it allows objects (observers) to register themselves and receive notifications whenever a different object (the observable) is changed. For instance, when a new item is added to the EventList, the UI element representing it on screen automatically refreshes itself.

The EventTableModel class fulfills the TableModel interface, making use of the EventList and TableFormat we described earlier. The EventList is the data provider, and the TableFormat determines how to extract the data from the EventList and display it in the table.

EventList people = new BasicEventList();
// Add all the elements
for (Person p : getPeople()) {
    personList.add(p);
}
TableFormat personTableFormat = GlazedLists.tableFormat(Person.class,
    // Names of the properties to fetch
    new String[] {"name","age"},
    // Names for the columns
    new String[] {"Name", "Age"});
EventTableModel tableModel = new EventTableModel(people, personTableFormat);
JTable table = new JTable(tableModel);
// Any modifications to the ‘people’ list is automatically reflected in the table

Provides a simplified means of filtering a table or list

Perhaps one of the most important features of any interactive table is the ability to filter out extraneous information. Glazed Lists makes this possible by chaining together EventList transformations; these transformations provide a different view of the underlying data. When the original model is modified, the filtered views automatically pick up the changes and update accordingly.

Say we want to provide the ability to filter the list based on people’s names. We will add a listener to a text field which listens for changes (new letters typed or deleted), and filters the list in real time. Once we have an EventList of some sort, it is easy to create a new “view” of that same list, filtering out entries you don’t want to see. You do this by wrapping the list in a FilterList, and then assigning some sort of filter criterion. Let’s start simple with a filtered list which only shows those users whose names start with the letter ‘A’.

EventList personList = new BasicEventList();
personList.add(new Person("Anthony Hopkins", 74));
personList.add(new Person("Barack Obama", 50));
personList.add(new Person("American McGee", 39));

Matcher personFilter = new Matcher() {
    public boolean matches(Person p) {
        return p.getName().startsWith("A");
    }
};
// Create a filtered list
FilterList filteredList = new FilterList(personList, personFilter);
// Displaying the people in a list as opposed to a table; could also create EventTableModel
// as in the last example.
EventListModel filteredListModel = new EventListModel(personList)
JList list = new JList(filteredListModel);
// At this point, shows Anthony Hopkins and American McGee

The filter I’ve defined above is static – once it’s instantiated, its filter condition never changes. Glazed Lists supports dynamic filters as well, through the MatcherEditor interface. We will see how to use a MatcherEditor instance for a text field, but first we need to tell Glazed Lists which strings to use when filtering for a given object. We do this with the TextFilterator interface.

Picture illustrating a FilterList which accepts only those people whose name starts with 'A'

 

public class PersonTextFilterator imlements TextFilterator {
    // Slightly strange interface, but done for efficiency reasons
    public getFilterStrings(List baseList, Person element) {
        baseList.add(element.getName());
        // Allow users to filter by age as well
        baseList.add(String.valueOf(element.getAge()));
    }
}

The MatcherEditor class to use in our case is TextComponentMatcherEditor. We provide it with the text field that it will use as the filter source, as well as an instance of the PersonTextFilterator class we just defined.

EventList personList = new BasicEventList();
personList.add(new Person("Anthony Hopkins", 74));
personList.add(new Person("Barack Obama", 50));
personList.add(new Person("American McGee", 39));

JTextField filterTextField = new JTextField();
// Add the text field to the UI - add to a JPanel

// Hook the text field up to a filter list
MatcherEditor filter = new TextComponentMatcherEditor(filterTextField, new PersonTextFilterator());

// Create a filtered list
FilterList filteredList = new FilterList(personList, filter);
EventListModel filteredListModel = new EventListModel(filteredList)
JList list = new JList(filteredListModel);
// List automatically updates in response to typing in the text field

Each transformed EventList is itself an EventList, meaning it can also be used as the basis of an EventListModel or EventTableModel. This chaining capability is extremely powerful.

Provides sorting capabilities

Finally, Glazed Lists makes it extremely easy to implement rich sorting capabilities in your tables or lists.

As we saw in the last example, it is possible to wrap a given EventList to provide a different view. In this case, we will have a sorted view of the data, which automatically updates whenever the underlying data changes.

To create the SortedList, you need to make your domain object implement Comparable, or create a Comparator. For instance,

public class PersonNameComparator implements Comparator {
    public int compare(Person p1, Person p2) {
        return p1.getName().compareTo(p2.getName());
    }
}
EventList personList = new BasicEventList();
personList.add(new Person("Anthony Hopkins", 74));
personList.add(new Person("Barack Obama", 50));
personList.add(new Person("American McGee", 39));

Comparator nameComparator = new PersonNameComparator();
// Create a sorted list decorator
SortedList sortedList = new SortedList(personList, nameComparator);
EventListModel sortedListModel = new EventListModel(sortedList)
JList list = new JList(filteredListModel);

A SortedList, wrapping a standard EventList

While the above example works for JLists, it’s nice to be able to sort a JTable as well. This is not too hard, either, as long as you have set up a TableFormat instance as described in the first section of this post. In essence, the TableFormat defines the type of each column, which is then used to sort the table whenever the corresponding column header is clicked. This behavior is defined in the TableComparatorChooser class, which exposes a static method to perform the installation on the target JTable. Here’s an example:

Comparator nameComparator = new PersonNameComparator();
// Create a sorted list decorator
SortedList sortedList = new SortedList(personList, nameComparator);
EventTableModel peopleTableModel = new EventTableModel(sortedList, new PersonTableFormat());
JTable peopleTable = new JTable(peopleTableModel);

// Use MULTIPLE_COLUMN_MOUSE to allow sorting by multiple columns, or SINGLE_COLUMN
// to sort by just a single column
TableComparatorChooser tableSorter = TableComparatorChooser.install(
    peopleTable, sortedList, TableComparatorChooser.MULTIPLE_COLUMN_MOUSE);

// At this point, clicking on the table headers will sort by this column

As the more detailed Glazed Lists tutorial warns,

By default, TableComparatorChooser sorts by casting column values to Comparable. If your column’s values are not Comparable, you’ll have to manually remove the default Comparator using TableComparatorChooser.getComparatorsForColumn(column).clear().

As long as your columns are represented by Comparable classes such as Number or String, you shouldn’t have to worry about this caveat.

Conclusion

Glazed Lists is one of the best Java Swing libraries I’ve ever used. It simplifies life for the programmer as well as the end user of the software project, since tables that allow sorting and filtering are far more useful than those which do not. If you do any sort of Swing programming, you owe it to yourself to try this library out. You can find much more information, including the aforementioned tutorial, on the Glazed List website.

 

Chaining together multiple list transformations makes it easy to create powerful programs

Advertisements

Of Rubber Stamps and CheckBoxes: Why your Android ListView is broken

November 5, 2010 5 comments
There are a lot of tutorials on how to use ListViews, the ubiquitous UI element in which a list of information is presented in a linear, scrolling view.  However, I do not think they do enough at explaining just why they behave in the way they do, especially with respect to stateful UI components. 

An example list view, with a very simple "view" for each row

The problem is, people do not quite understand what it means for views to be reused in the context of a ListView. This blog post is intended to explain what that means, and what consequences that has for your list views and your adapters.

The following description comes from the Swing documentation on how JTables are rendered, but it is instructive for this description, and introduces the central metaphor that helps explain what’s going on with the way ListViews are rendered.

Before you go on to the next few tasks, you need to understand how tables draw their cells. You might expect each cell in a table to be a component. However, for performance reasons, Swing tables are implemented differently.

Instead, a single cell renderer is generally used to draw all of the cells that contain the same type of dataYou can think of the renderer as a configurable ink stamp that the table uses to stamp appropriately formatted data onto each cell.

Why would the designers implement the rendering this way?  Imagine if each row of the table (or list) were a separate object.  Each row would hold references to its own UI widgets, e.g. for TextViews (labels), ImageViews (images).  If there were N rows and each row takes on average M bytes of memory, you’d have an average memory footprint of N*M bytes.  Given that these lists need to be able to display a large number of items, this would be an unacceptable memory footprint.  Furthermore, since most of the list would be offscreen, most of the memory would be completely wasted.  While it would be simpler to implement, it would bring any Android phone to its virtual knees.

The designers, borrowing from the Swing designers, chose instead a much more memory efficient way of rendering the lists – if only X items are visible on the screen, where X is much less than N, the total memory footprint would be X*M bytes.  It takes more work in the ListView class to calculate exactly which rows are visible and to ensure that only their views are rendered, but it’s worthwhile.  Each visible row uses its own “rubber stamp”, filling in the details specific to that row. 

What you do by defining your adapter view in XML is define the raw features that make up your rubber stamp.

This is the basic rubber stamp that might be used for displaying tweets in a Twitter application.  There are four elements: an image, a username label, a paragraph text label, and another label for how the tweet was posted.
Let’s say that at most four tweets can fit on the screen at once.  When the first row needs to be rendered, the view is null (hasn’t been instantiated) and must be inflated from XML.  This process is repeated for the second and third row.  When the next tweet needs to be loaded on the screen, it can reuse the view (rubber stamp) of the view that’s just left the screen at the top.

Row views are reused

This leads us into what trips a lot of Android developers up.  You cannot maintain state within the UI components alone.  What do I mean?  You cannot use, for instance, Checkboxes in each row and hope to have them keep track of which items are checked or not.  Why?  This will work for a small number of rows, but once you get past the number that can fit on one screen, you’ll start to see weird problems.  If you can fit X rows on the screen, then item 1, X+1, 2X+1, … will all share the same checkbox.  Thus by changing the state of item 1, you will change the status of all the rows further down the line that are a multiple of X away from it.  The correct way of handling this is to have each row data object, that which is being visualized by the Adapter, maintain this state.  You can see an example of this in a tutorial on how to use checkboxes in listviews. I see this question come up repeatedly on StackOverflow, and it stems from the programmer not understanding how these views are reused in each row.
The metaphor of the rubber stamp fits more with that of JTable rendering, where there truly is a single component that gets reused over and over again, whereas there are multiple reused row View objects in the case of Android.  Why isn’t the single component approach used for Android?  At first I assumed it was, until I read this great explanation on android.amberfog which illustrates how there are actually as many views inflated as there are potential rows visible at any time.  The reason as far as I can imagine is that the user needs to be able to interact with each rows.  If all that was required was the static view of each row, then it would be fine to use a single rubber stamp, render a row, change an offset in the graphics context, and render the next row.  As it stands, however, each row needs to be able to detect when it is clicked on, long clicked on, etc.  Furthermore, each row could have UI elements which themselves could be interactive.  Thus it is insufficient to present static views of the data; the user would be unable to interact with the rows in the way he expects.
I hope this explanation is helpful, and if I’ve gotten anything wrong, I’d love to hear about it in the comments.

(All images were created with the use of Balsamiq, a great tool for mockups).