The Source for Java Technology Collaboration
User: Password:



   

Interaction Happens: Thinking Graphically Interaction Happens: Thinking Graphically

by Jonathan Simon
07/12/2005

Contents
Example 1: Address Detail Panel
   Implementation
Example 2: Frame Title Abuse!
   Implementation
Example 3: Information Messages
   Writing Your own Message Bubble
Wrap-Up
Props
Resources

Making a polished user interface is hard work. Interaction design takes experience and time, which are usually inconceivable luxuries. And that's before we mention the technical limitations in Swing, which we are all too familiar with. On top of that, programming and interaction design are often at direct odds with each other. Making a polished, functioning interface can take much more code (and much more complicated code) than a less-polished alternative. Similarly, developing with graphics toolkits and widgets often forces the implementation to use a less than perfect solution from an interaction perspective. The combined effect of these forces is a difficult playing field where we try to solve these problems and develop a product.

This article takes a holistic view at the problem space. We'll take a look at three different user interface design issues from an interaction perspective. Following that, we'll look at an interaction design solutions to these problems that I've used and seen others use on countless projects. Finally, we'll implement them in Swing, exploring the technical details of implementing these designs. The goal is to understand, through practical examples, that there is a single problem space from interaction design to code. Along the way, you'll also see some code examples of Swing techniques you may not have used before, but might find interesting (think transparent painting and rounded borders).

Example 1: Address Detail Panel

A common way to display detailed information is to create a panel with one label per field, and the field displayed alongside it. Figure 1 shows an example of one of these panels for address details of someone named "What A. Guy."

Figure 1

Figure 1. Standard details panel displaying an address

Although it's easy to create a one-to-one mapping between the address fields and this panel, there are a number of interaction issues with this design.

  • Difficult alignment: In this example, the field names are all right-aligned, but their lengths vary. Some field names have more space between the end of the field name and the beginning of the value, making it difficult to correlate the field name and the value. Some interaction designers prefer to right align the field name and left align the value, but then your detail panels start to look an awful lot like ASCII Christmas trees. You can improve the alignment significantly by standardizing the field name length (say, between five and ten characters) but it's still going to be awkward.
  • Waste of space: As explained above, the field names are different lengths, and you have to make the field labels wide enough to accommodate the widest field name. Some of the fields will undoubtedly be shorter. So in addition to making the alignment difficult, it's a waste of screen real estate. Save the pixels!
  • Non-dynamic: These displays don't usually adjust themselves for the information they are displaying. For example, if there is no middle initial, the value will simply be blank. But like looking through a database table, the blank fields are always there--you'll always have the field label there, and the space is still taken up. And if you try and make it dynamic, the screen is very noticeably changing all the time.

On the upside, these panels are pretty easy to build. You can even generate them automatically from a data object using reflection or a field mapping. This is a great example of where interaction design goals differ from coding simplicity. Even though they might be difficult from an interaction perspective, they do have some redeeming features. But we're here to focus on interaction and work towards the code, so let's see if we can do a little better.

Figure 2 shows a more graphically oriented representation of the same address.

Figure 2

Figure 2. Graphical address

The formatting in this panel is very close to how you would write or print the same address. This allows you to remove the labels, saving screen real estate, simplifying alignment, removing clutter, and more. It's easy for users to tell the information from the context. For example, addresses in the United States are always street address, then city <comma> state <space> zip code on the following line. Quickly glancing for the comma and looking after it is a quick way to find the zip code. So you don't need a label marked "Zip Code" as in Figure 1. Other good examples of things that don't need labels would be email addresses and websites. As soon as someone sees "http," they know it refers to a website. The same applies to the "@" in an email address. In those cases, the label is simply redundant and potentially distracting.

But sometimes you do need labels. As an example, the last line shows two phone numbers. Cell phones and home phones have identical formatting, so there's no way to tell the difference by context alone. Yet this doesn't mean that you need to have full labels that say "Home Phone" and "Cell Phone." Its already noticeable that they are phone numbers due to the formatting, so you can eliminate the "Phone" from the label. Also, there are a limited number of types of common phone numbers--home, cell, office, etc. Notice that the phone labels in Figure 2 only have a single letter, "C" or "O," for home or office.

I can't stress enough how careful you have to be with this type of logic, however. It's important to analyze the data you're working with before doing a graphical display panel. You need to ask yourself (and your users, if possible) whether they can figure out the data from the context. For example, I built a trading system and used these types of panels to display bond information. Bonds are often identified by a combination of three data elements: the issuer, the coupon rate, and the maturity date. Since these traders deal with this information all day long, you can display the ticker, coupon, and maturity in order--"F 2.7 2010," for example--and expect these users to immediately understand it.

Implementation

There are a couple of different ways to implement this. You could use a series of JLabels and other text widgets, or you could use a JTextPanes with styled text. We'll implement a later example with a JTextPane and styled text, so let's implement this example with a series of JLabels for variety. Here is the code to set up the JLabel-based GraphicalAddressPanel, starting with the titled border and label instantiation.


public GraphicalAddressPanel() {
    setBorder(BorderFactory.createTitledBorder("Address Details"));
    nameLabel = new JLabel();
    addressOneLabel = new JLabel();
    addressTwoLabel = new JLabel();
    phoneLabel = new JLabel();
        
        ...
}

Next, we'll continue the constructor implementation with the layout. Since the top label is going to be larger (because of the font size) we can't use a simple GridLayout, which would equally space all of the components. Instead, we'll use a few panels and nested layouts. Start by setting the panel layout to BorderLayout and adding the name label to BorderLayout.NORTH.


setLayout(new BorderLayout());
add(nameLabel, BorderLayout.NORTH);

Now, make a subpanel with a GridLayout, named detailsPanel, for the remaining detail labels. Add the remaining labels.


JPanel detailsPanel = new JPanel(new GridLayout(0,1));
detailsPanel.add(addressOneLabel);
detailsPanel.add(addressTwoLabel);
detailsPanel.add(phoneLabel);

Next, create a panel named bottomPanel with a BorderLayout. Add detailsPanel to BorderLayout.NORTH and add it to the main panel. This is a quick and dirty method of using nested panels and layouts to keep the labels oriented to the top.


JPanel bottomPanel = new JPanel(new BorderLayout());
bottomPanel.add(detailsPanel, BorderLayout.NORTH);

add(bottomPanel, BorderLayout.CENTER);

Finally, set the fonts of the labels. This uses the deriveFont() helper method of Font. It creates a new Font with the same style as the old Font with a few changed parameters--very useful.


nameLabel.setFont(
    nameLabel.getFont().deriveFont(Font.BOLD, 18));
addressOneLabel.setFont(
    nameLabel.getFont().deriveFont(Font.PLAIN, 12));
addressTwoLabel.setFont(
    nameLabel.getFont().deriveFont(Font.PLAIN, 12));
phoneLabel.setFont(
    nameLabel.getFont().deriveFont(Font.PLAIN, 12));

Now to display the address. Create a method called displayAddress(). This method is pretty lightweight. We'll just use it to forward off the request for each line to a helper method. This method takes in an address data object called AddressDO. This is a simple data object with getters and setters for each field.

 
public void displayAddress(AddressDO addressDO) { 
    nameLabel.setText(getName(addressDO)); 
    addressOneLabel.setText(getAddressOne(addressDO)); 
    addressTwoLabel.setText(getAddressTwo(addressDO));
    phoneLabel.setText(getPhone(addressDO)); 
} 

Let's take a look at the getPhone() method as an example of the type of dynamic formatting you'll do with these types of display panels. The logic is straightforward--if the home phone number exists (i.e., it is non-empty and non-null), add the "(H)" indicator and the home phone number. Do the same for the cell phone number. If we didn't do this, we would display labels for phone numbers that don't exist.


private String getPhone(AddressDO addressDO) {
    StringBuffer buffer = new StringBuffer();

    if (addressDO.getHomePhone() != null
            && addressDO.getHomePhone() != ""){
        buffer.append("(H)");
        buffer.append(addressDO.getHomePhone());
        buffer.append("  ");
    }

    if (addressDO.getHomePhone() != null
        && addressDO.getHomePhone() != ""){
        buffer.append("(C)");
        buffer.append(addressDO.getCellPhone());
        buffer.append("");
    }

    return buffer.toString();
}

It's pretty easy to take it to the next level from here. If you have your application deployed to multiple regions, you can implement a display decorator to format names and addresses as appropriate for current locale. You can also add and remove fields at will. The end result is that it's easier to change the formatting and display without a rigid label/value display like Figure 1. At the same time, you conserve a lot of screen real estate and can make the display more intuitive to your users at the same time.

Pages: 1, 2, 3

Next Page » 

View all java.net Articles.

 Feed java.net RSS Feeds