Archive

Posts Tagged ‘GUI’

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

ColorBrewer

April 11, 2010 Leave a comment

Just a quick post about a great online tool I was shown (thanks Eric) called Colorbrewer.

There are numerous books and articles online about color palette design, usually from a web-design / aesthetic standpoint.  But there is more to the use of color than mere aesthetics; color can be used as an effective tool in scientific data visualization.  One of the few books on the topic describes its contents as a guide to “how scientists and engineers can use color to help gain insight into their data sets through true color, false color, and pseudocolor imaging.”  While the content of the book is a bit beyond the scope of this post, it’s clear that color gets a lot of use in charts and graphs, and being able to better pick colors is beneficial.
ColorBrewer is designed to help users pick a set of colors that is best used to show data on a map.  Unlike most color scheme choosers where you pick whether you want muted colors, bright colors, pastel colors, etc., ColorBrewer starts by asking whether the data you are visualizing is sequential, diverging, or qualitative.  Sequential and diverging both have to do with quantitative data, e.g. average salaries.  Sequential is the more familiar for data; darker colors usually indicate a higher value on whatever metric is measured.  Diverging, on the other hand, treats the average as a neutral color and then uses colors with sharply contrasting hues for the high and low ends.  Qualitative could also be labeled as ‘categorical’; it means about the same thing.

Among its other features, ColorBrewer can exclude colors that would not come out well when photocopied, as well as those that would be confused by people with color blindness.  It also has mechanisms for exporting the RGB/Hex color codes of the generated color palettes for use in other applications.

Categories: UI Tags: , , ,

Java Swing Drag and Drop Troubleshooting

April 11, 2010 Leave a comment

Dragging domain objects between JTables

Drag and drop Troubleshooting

Drag and drop is an ubiquitous UI metaphor in common desktop/web applications.  I am in the process of finally learning how to accomplish drag and drop in Java through Swing; it hasn’t been an easy process.  I have been keeping track of some of the problems I have run into along the way, and will be updating this post as I find more.  Make sure you read through the whole set of Drag and Drop tutorials before you attempt to implement Drag and Drop; a lot of my mistakes came from jumping the gun and not reading through the whole document.  I will be making a post later with more complete source code illustrating how to drag and drop domain objects between components (the tutorials online usually show text being dragged and dropped rather than class instances).

Problem: When I attempt to drag an item out of the table, nothing happens
Solution:

  • Did you remember to call “component.setDragEnabled(true)” first?
  • Make sure you are not using a DataFlavor constructed with default constructor; if it is the drag action will not occur

Problem: When I complete a drag and drop, I get a class cast exception
Solution: Make sure you have created the DataFlavor correctly; you should define the class of the serialized object within the constructor to the DataFlavor.  E.g. if you are sending an instance of a class named Foo.java, you could create a DataFlavor as follows:

public class Foo implements Transferable, Serializable {

    public static final DataFlavor DATA_FLAVOR = new DataFlavor(Foo.class, "Foo");

    public DataFlavor[] getTransferDataFlavors() {
         return new DataFlavor[]{DATA_FLAVOR};
    }

    public boolean isDataFlavorSupported(DataFlavor flavor) {
        return flavor == FLAVOR;
    }

    public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
        return this;
    }
}

Problem: When I attempt to drop an item onto a table, nothing happens
Solution:
If the table is empty, make sure you called “JTable.setFillsViewportHeight(true)” first?  Explanation:

JTable.setFillsViewportHeight(true)is invoked to set the fillsViewportHeight property. When this property is true the table uses the entire height of the container, even if the table doesn’t have enough rows to use the whole vertical space. This makes it easier to use the table as a drag-and-drop target. (http://java.sun.com/docs/books/tutorial/uiswing/components/table.html)

See more info about and drag and drop with respect to empty tables.

Problem: When I move an item from one table to another, the item isn’t deleted from the first table
Solution: Make sure you do the modification to the underlying table model in the exportDone method.

For instance:

@Override
public void exportDone(JComponent c, Transferable t, int action) {
    if (action == TransferHandler.MOVE) {
        JTable table = (JTable) c;
        list.remove(table.getSelectedRow());
    }
}
Categories: Java, programming, UI Tags: , , ,

Touch screen interface considerations

February 24, 2010 5 comments
When moving to a new hardware platform such as a touch sensitive mobile device, it is important to design the UI with the constraints of the device in mind.  Early Windows tablets failed in large part due to the OS not being suited to touch interaction; icons were too tiny to press on without a stylus.  I have heard the same complaint about early generation Windows Mobile phones; the user in question said he had to literally press tiny squares on the screen where the up and down arrows were in the scroll bar.  The UI widgets from desktop programs were ported over to these new platforms without giving thought to the decreased screen resolution, screen size, and finger dexterity that a mobile platform provides.

The iPhone and its multitouch gestures provide very intuitive, natural ways of interacting with the reduced screen real estate.  Scroll bars are no longer controls that must be held and dragged; rather they are small, unobtrusive widgets that appear only while a page is being dragged.  The entire page becomes the draggable area, in keeping with the idea of maximizing the size of items that need to be interacted with.  Coupled with the pinch gestures for zooming in and out of webpages, the browsing experience on the iPhone is almost flawless.  There are two main problems stemming from the metaphor of dragging the entire webpage: the lack of hover and drag and drop.

Hover behavior

Given the fact that touch screens only pick up your inputs when you are touching the screen, there is no notion of hovering (the browser doesn’t display a cursor where you last touched).  Hovering over elements in a webpage usually has one of two effects:

1) Showing a tool tip

2) Displaying additional information in a section of the browser; e.g. mousing over an embedded link reveals the target of the link in the lower left corner while using firefox.

In very few cases, mousing over or hovering over elements actually acts like a click; see dontclick.it for an example.

Number 2 is not a big concern; when you long press on an embedded link, a screen pops up revealing the target of the link:

Number 1 is more of a concern.  If your site is designed to be viewed on a mobile browser, you should ensure that that you have a version of the CSS that does not require you to mouse over or hover over elements to reveal information.

Drag and drop

Drag and drop is a common desktop OS UI metaphor.  It can be slightly tricky for users to handle due to the dexterity required, especially if the drag and drop targets are small; most digital natives don’t have any problems with it.  It is less common on the web than traditional desktop fat clients, but there are certainly webpages that use it.  For instance, WordPress uses drag and drop to configure sidebar widgets:

The problem is that these movable elements cannot be selected and moved on the iPhone; they select when pressed but as soon as you move your finger, they deselect and instead the page moves.  While this would be a problem in and of itself, drag and drop is also used in common UI widgets such as sliders.

Sliders

Sliders are an excellent UI controller for selecting an approximate value from a bounded range of continuous values.  Look at Endless.com‘s elegant dual slider control for narrowing down price:
With a single control, we get two functions:
  1. Displaying the current range of values
  2. Allowing user to narrow down that range into a smaller subset.

For instance, if user only wants sneakers that cost between 50 and 100 dollars he just drags both ends of the slider bar:

This illustrates a drawback of the slider approach: it is imprecise when the range of available values is large and the control is small on the page; the slider would have to have subpixel precision in order to make user

The frustrating thing is that the iPhone has BRILLIANT slider support in its iPod app.  They get around the problem of precision by making use of the vertical dimension that normally is ignored.  When your finger is right on the slider, the slider follows your finger one to one.  When you move your finger down the screen, the sensitivity is turned down and it takes a greater horizontal change to elicit a change in the position of the slider (changing from “Hi-Speed Scrubbing” to half speed to quarter speed.)

White dot represents where the finger is held down

The drawback of this interaction is that it is novel; I discovered it by accident and I’m sure a lot of iPhone users don’t even know about it.  However, it is a wonderful solution to the problem of imprecise fingers; it could be used in traditional desktop apps as well.

Sites with interactive javascript graphs frequently use sliders; see for instance OKCupid’s blog post on why you should date older women:

On the iPhone, it is impossible to move the slider to interact with the graph.

The problem is that there is no standard HTML way of specifying a slider; instead sites use a mishmash of Javascript to get that effect.  (Read a tutorial how).  If there were a standard HTML component, it’s likely that the iPhone could handle it more gracefully.  For instance, the iPhone changes the standard way dropdown menus are implemented

Standard desktop view of a dropdown control

iPhone view of control – a finger-friendly wheel widget pops up, occupying half of the screen.

So one solution to the problem of lack of slider support would be to have a standard HTML widget for sliders that the iPhone could then interpret and render in a more useful way (e.g. with the vertical dimension added like on the iPod slider controls).

Another potential solution would be to make a gesture that indicates a mouse drag.  There is already a way to select text, which is done in a desktop environment via the same mechanism as drag and drop (press, move mouse, release mouse button).  I could see this working in one of two ways.

1) As a multitouch gesture.  One finger is held as still as possible indicating that the background should be anchored, while the other finger proceeds to press on the draggable component and move it around the screen.  The drawback of this is that the pinch gesture currently works even when one finger is held still; thus this gesture would overload that of pinch.  I know I personally pinch by moving both fingers, but some people might not.

2) When a draggable entity is long-pressed for long enough (1, 2 seconds?) browser enter different mode where motion events do not scroll the page but instead move the draggable item.

If you have a multitouch enabled browser, you can see how dragging might work in this proof of concept page. Unfortunately the pinch gesture does not work correctly – it’s much jerkier than a standard webpage.  Their implementation suggests a third alternative, immediately entering drag mode when a draggable item is touched.  The problem with this is that, if there were multiple draggable items on the screen it would be very difficult to actually grab a portion of the screen in order to pan around to new areas.

This way of handling drag and drop is used by games on iPhone, such as Words With Friends.  Note in the left picture the scroll bars indicating that the board is being moved.  On the right the finger is moving while over the E tile, so the E is dragged along with the finger.  Moving the finger off the screen is equivalent to releasing the mouse button in a standard drag and drop action; the tile is laid down in nearest square.

Conclusion

The iPhone has introduced some great UI metaphors such as pinch and zoom as well as making the entire area of the page draggable for easy scrolling of text.  In the realm of the web browser specifically, these advances prevent two common desktop OS UI metaphors from working, that of hovering over elements and dragging and dropping elements.  The problem of hovering is not insurmountable; web developers need to be aware of this drawback and code their websites in such a way that information is not lost if you cannot view tooltips or otherwise hover over elements.  The second problem, that of an inability to select an element, drag it, and drop it, is more pressing.  There are certain tasks that are absolutely impossible to complete while using an iPhone browser, such as anything requiring you to move a slider control.

Hopefully the new version of the iPhone OS can remedy the drag and drop problem.

Categories: UI Tags: , , ,