Table Presentation Matters
People have come to expect applications with table components to make their table views sortable by clicking on the header columns of the table. It takes some by surprise to find that the Swing toolkit has no built in functionality to support this easily. The oddest part of this is that Sun actually give people table sorting in plain sight. The Sun JavaDocs point to the extensive Sun Java tutorials online. If you follow the JTable links you get to a tutorial which eventually mentions, amongst lots of [Pending] comments in the text, the TableSorter
class which you can find at http://java.sun.com/docs/books/tutorial/uiswing/components/example-1dot4/TableSorter.java.
Now the TableSorter is a decorator model which can wrap an existing TableModel and add all the sorting functionality without changing the underlying TableModel. It doesn't store the contents of the table; it just does the bare minimum to work out how to sort the contents and map calls through to the wrapped model. In ToDoTasks, we have the TasksModel which is a simple view on the Tasks class with no sorting. In the TasksFrame class, once we have created the GUI, we call the Controller to get the TasksModel and then set that as the JTable model.
TasksModel tasksModel;
...
tasksModel=controller.getTasksModel();
tasksTable.setModel(tasksModel);
To incorporate the TableSorter, we first need to wrap the the TaskModel in a TableSorter and then set the sorter as the JTable model like so:
TasksModel tasksModel;
TableSorter sorter;
...
tasksModel=controller.getTasksModel();
sorter=new TableSorter(tasksModel);
tasksTable.setModel(sorter);
We keep a handle on the sorter as there's one thing more to do, hand the sorter the JTable's table header so it can step through it and add in mouse event listeners to make the headings trigger sorting behaviour:
sorter.setTableHeader(tasksTable.getTableHeader());
And that's it. You now have a JTable which supports clicking on a heading to toggle between no sorting, ascending order and descending order. It also supports a secondary column for sorting by pressing control and clicking on the headings. Yes, it was that simple, and leaves you wondering why TableSorter isn't an official part of Swing.
We'll wrap up this article with a tip for more rapid prototyping. You can find yourself burning time handling enumerations when presenting them in a table, translating from an enum value to a string and vice versa. In ToDoTasks, the task priority is represented as a Java 5.0 enum in Priority.java, like so:
enum Priority {
NONE(0),LOW(1),MEDIUM(2),HIGH(3),CRITICAL(4);
Priority(int priority) { this.priority=priority; }
final int priority;
}
Now, to quickly make that appear in the table, with names, you could write a custom cell renderer for the table which translated the enum value. Because enums are proper objects now, we can tell the JTable to use its defaults to render the value, and what is displayed is not the numerical value, but the name of the enum value.
To allow for editing of the value, again, we could write a custom CellEditor to do the work. But it's the matter of 3 lines of code to make the editor a drop down with all the enum values. To get all the values of an enum, you can call its values method. This returns an array in the order the enum values were declared. Once we've created our JTable in TasksFrame, we can create a DefaultCellEditor with a JComboBox.
DefaultCellEditor priorityEditor=new DefaultCellEditor(
new JComboBox(Priority.values()));
We initialise the JComboBox with the array from Priority's values. For usability's sake, we set the CellEditor to need two clicks to start editing the cell:
priorityEditor.setClickCountToStart(2);
If we had left it at one click, it would very hard to select a row using that cell because it will immediately show a combobox menu to change it. The last step is to tell the table that whenever it sees a Priority value in a cell to use our new editor:
tasksTable.setDefaultEditor(Priority.class,priorityEditor);
We now have a combobox editor which shows the enum's names as they appear in the code; a useful timesaver when you are prototyping.
tutorial-jdbm-1/2