Welcome!

Java Authors: Hari Gottipati, Tad Anderson, Yakov Fain, Pat Romanski, Colin Walker

Related Topics: Java

Java: Article

List-Based UI Framework For Your Swing Applications

The primary concept behind this idea is a data model that contains elements that describe parts of a user interface

In this month's article I continue my discussion of a list-based UI framework that I started last month ("ArrayListModel," [JDJ, Vol. 10, issue 10]). The primary concept behind this idea is a data model that contains elements that describe parts of an application's user interface. Through a single model, various aspects of the user interface can be controlled, manipulated, and visually synchronized. There is a lot of interesting code that accompanies this article, so I encourage you to download it and check it out. Let's get started.

Data Model
Recall from last month's article the UIElement interface that describes our user interface element:

public interface UIElement {
Object getItem();
String getDescription();
Icon getSmallIcon();
Icon getLargeIcon();
JComponent getComponent();
}
The UIElement interface includes methods that allow other Swing component consumers to construct the user interface. The getDescription() method would normally be used to provide tooltip text, while the get*Icon() methods can be used appropriately to provide icons on such things that include but are not limited to menu items, toolbar buttons, internal window icons, and tabbed pane icons (we'll see examples of each of these in our discussion). The getComponent() method is used to associate some other visual component with the UIElement. This is typically used when the UIElement shown in the user interface is selected.

Our data model, UIElementListModel (see Listing 1), is based on the ArrayListModel that I also introduced in last month's article. This is an ArrayList sub-class that implements the ListModel interface, which allows a consumer to listen for changes in the underlying Collection via the ListDataListener interface. UIElementListModel extends ArrayListModel and types the collection to contain only UIElement objects. In addition, the class overrides the remove() method in the Collection, allowing for a VetoableChangeListener to intercept a remove operation. Remember that our data model consists of user interface elements (UIElement objects), which can be removed by some user-initiated operation. The classic example is the closing of an internal frame that contains unsaved data - the user must be prompted to save the data first or allow the close operation to proceed without saving. The remove (close) operation is done through the data model as other aspects of the user interface are listening to changes in the UIElementListModel model. We'll see an example of this later.

Since the UIElement construct is fairly ubiquitous in our framework, it begs for an AbstractUIElement implementation that handles some of the common tasks described by the interface. The code accompanying the article includes an immutable AbstractUIElement implementation. Probably the most interesting thing about it is that it handles the getLargeIcon() method by scaling the small icon associated with the UIElement, or vice versa (i.e., if a large icon is provided, but not a small icon, it will scale down the large one). Listing 2 shows the implementations of these methods. Obviously the scaling isn't perfect, and it certainly isn't meant to replace the work of a good graphics designer. Nevertheless, it's great for prototyping your UI when you might have only crude versions of one icon or the other.

Views on Our Model
Now that we have a solid foundation for the model side of our framework, we need views that work with this model. Our views are naturally going to implement some interface, and we'll provide an abstract implementation of this interface to simplify the task of implementing various views. Listing 3 defines our interface, UIElementListView.

Our view interface supports our data model (UIElementListModel) with a getter and setter. It also has the concept of a selected item/index and a SingleSelectionModel that is conveniently used to enable selection listeners via the add/remove ChangeListener methods. Finally, our interface is supported visually by some component - the getComponent() method. My choice of a SingleSelectionModel here is intentional, as usually there is only a single "active" item (window, internal frame, tabbed pane, etc.) in an application.

The accompanying code to this article, provides an AbstractListView implementation of the UIElementListView interface. The most interesting method in this class is the setModel() method (see Listing 4). This method first checks to see if it currently hosts a model and, if so, it removes all of the underlying views (there are abstract insert and remove methods in this class that are used to add/remove a view). Remember that a view corresponds to a UIElement (contained in our data model, UIElementListModel), with an associated component. So, for example, a tabbed pane AbstractListView implementation returns a JTabbedPane in its getComponent() method. Each tabbed pane in this view hosts a component that is returned by the getComponent() method of the UIElement object. The insert and remove methods of a tabbed pane AbstractListView would be used to add/remove a tabbed pane. You can see in Listing 4 where a view is inserted for each element in the new data model.

AbstractListView also implements ListDataListener and adds itself as a listener on the UIElementListModel. In this manner, it can respond to changes in the data model that are initiated in other parts of the application. For example, a menu item can be used to "close" a frame, internal frame, or tabbed pane of an application. The menu item action simply has to remove the element from the model and any views using the model will react accordingly.

Finally, AbstractListView provides a VetoableChangeListener on our UIElementListModel. The vetoableChange() method in the listener simply delegates to a canClose() method in AbstractListView, in which the default implementation returns true. A subclass can override canClose() if needed, for example, to prompt the user for unsaved data. There is an example of this below, too.

Putting It All Together
I've introduced quite a few concepts and classes up to this point, so it's time to show a few examples of how all of this is used (you may be wondering about this at this point as well). You may recall the sample program from last month's article. Figure 1 shows a similar application, consisting of a toolbar with toggle buttons and a JDesktopPane.

In the code provided with this article, you can run the ListViewTest Eclipse launch target to execute this sample program (you'll need a J2SE 5.0 JDK with Eclipse to compile and run the code). The toolbar is constructed from all of the elements returned by a UIElementFactory implementation. The JDesktopPane is provided in the getComponent() method of a class called MDIView, which simply extends AbstractListView. The model for the application is a UIElementListModel, where each element of the model corresponds to an internal frame in the JDesktopPane (MDIView). Selecting a toggle on the toolbar either adds or removes (based on the toggle state) the corresponding UIElement from the model. In Figure 1, I've selected the Green, Cyan, Pink, and White toolbar items, respectively.

When you run ListViewTest application, you specify the class name of the UIElementFactory and the class name of the underlying AbstractListView. Figure 2 shows another UIElementFactory implementation. (There are two UIElementFactory implementations provided with the sample code. Each returns instances of UIElement objects that are created by extending AbstractUIElement.)

There are two additional AbstractListView implementations provided as part of the sample code: ListView, which uses a JList as its view on the model (see Figure 5 below); and TabbedPaneView, which uses a JTabbedPane as the view (see Figure 3). Other AbstractListView implementations are possible - use your imagination. (For example, though not provided with the sample code, I have a ShortCutPanel implementation where a custom icon button on the left is used to select a view on the right [for an example, see the Tools->Options dialog in the Firefox browser]. The panel supports small and large icons - and these are gotten from the UIElement objects in the model).

Again, the different looks of each of Figures 1 through 3 are created simply by providing different runtime arguments to the ListViewTest application.

Figure 4 is a result of de-selecting the "Pink" item on the toolbar (refer back to Figure 1). This user-initiated action removed that UIElement from the model, and the AbstractListView (MDIView) reacted accordingly by removing the corresponding internal frame.

You can easily imagine this happening as a result of menu item selection, or even more directly from pressing the window close button (X) on the internal frame. In fact, MDIView does just that. For each JInternalFrame that's added, it adds an InternalFrameAdapter to the frame (see Listing 5).

Note that the internalFrameClosing() method does the removal from the UIElementListModel (and not the AbstractListView nor the JDesktopPane). The AbstractListView (MDIView) will react accordingly, as will other user interface elements that might be listening to changes in the model.

More Stories By Phil Herold

Phil Herold is VP and CTO of PocketScience LLC in Research Triangle Park, NC. He has over 24 years of experience in software engineering, and has been working with Java client technologies since 1996.

Comments (2) View Comments

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.


Most Recent Comments
Fernando Ortiz 12/23/05 02:47:51 PM EST

Phil,

Excellent article. I would like to know how I can get the source code for this article. The one you provide has a "dat" extension and don't know how to extract it.

Any help appreciated,

Fernando

JDJ News Desk 12/13/05 03:08:43 PM EST

Java Developer's Journal: List-Based UI Framework For Your Swing Applications. In this month's article I continue my discussion of a list-based UI framework that I started last month ('ArrayListModel,' [JDJ, Vol. 10, issue 10]). The primary concept behind this idea is a data model that contains elements that describe parts of an application's user interface. Through a single model, various aspects of the user interface can be controlled, manipulated, and visually synchronized. There is a lot of interesting code that accompanies this article, so I encourage you to download it and check it out. Let's get started.