The Source for Java Technology Collaboration
User: Password:



Hans Muller's Blog

February 2006 Archives


A Reusable BuddyList Component

Posted by hansmuller on February 27, 2006 at 03:30 PM | Permalink | Comments (13)

Every now and then someone drops by to ask about the slick chat/IM demo components that were shown in the Extreme GUI Makeover JavaOne session last year. The Swing components created for those demos where hacked together in order to show what's possible and sadly, they're not available as production quality components just yet. I certainly like the idea of resuable, configurable/extensible, chat client GUI parts. If I were building that kind of application I'd be happy to avoid starting from scratch. This blog is a brief look at one such part. You can try it out by pressing the launch button.

BuddyList demo screenshot
BuddyList Demo Screenshot

BuddyCellRenderer is an attempt to build a somewhat reusable JList CellRenderer for Chat/IM buddy lists. It's job is to render an object that represents a Buddy roughly like this:

		  screen name
	[status]  short message   [icon]

Here "status" is one of online, offline, or away. Away status means that the user is online but busy. The "screen name" is the Buddy's name, "short message" is an optional short message from the Buddy, and icon is a picture that represents the Buddy. All of this is quite conventional. These elements appear in most chat/IM application buddy-lists in one form or another. If a "short message" isn't provided we change the layout just slightly:

	[status] screen name [icon]

The BuddyListCellRenderer must also provide a Buddy-specific tooltip that's displayed if the user lingers over one BuddyList element.

JList renders list elements or "cells" by delegating to an implementation of ListCellRenderer. ListCellRenderers have only one method, getListCellRendererComponent(), which returns a Component that the JList uses to paint a single list element. The JList really just uses the cell renderer component's paint() method to draw or "rubber stamp" a list element. The getListCellRendererComponent() method is passed the JList model's "value" for each list element, and its responsibility is to return a component that's been configured to display that value.

The default ListCellRenderer is quite simple. It just uses the same JLabel for every list element, roughly like this:

JLabel label = new JLabel();
Component getListCellRendererComponent(JList l, Object value, ...) {
    jLabel.setText(value.toString());
    return jLabel;
}

To display the properties of a Buddy in the way we've layed out above will require more than just a JLabel. BuddyCellRenderer uses a JPanel with subcomponents for the various properties and GridBagLayout to define the layout.

A generic ListCellRenderer that configures our JPanel composite to display a Buddy value is difficult because we don't want to dictate the type of the Buddy object but we do need to extract its status, screen name, icon, and message. What's needed is an adapter that extracts the properties needed by the BuddyCellRenderer from the app-specific Buddy object. The BuddyListCellRenderer.Adapter class does this. The way it works is easiest to explain with an example. Lets assume that our chat/IM application has a Buddy class that looks like this:

class MyBuddy {
    boolean isOnline() { ... }
    boolean isAway() { ... }
    String getScreenName() { ... }
    ImageIcon getIcon() { ... }
}

A JList ListModel that encapsulated the list of MyBuddy objects would have to be created; I will not delve into that here. The adapter for MyBuddy objects could be defined and used like this:

class MyBuddyAdapter extends BuddyCellRenderer.Adapter {
    private MyBuddy getBuddy() { return (MyBuddy)getValue(); }
    public String getName() { return getBuddy().getScreenName(); }
    public String getMessage() { return getBuddy().getMessage(); }
    public ImageIcon getBuddyIcon() { return getBuddy().getIcon(); }
    public Status getStatus() { 
        if (getBuddy().isAway()) {
            return Status.AWAY;
        }
        else if (getBuddy().isOnline()) {
            return Status.ONLINE;
        }
        else {
            return Status.OFFLINE;
        }
    }
}

BuddyCellRenderer cellRenderer = new BuddyCellRenderer();
cellRenderer.setAdapter(new MyBuddyAdapter());
myBuddyJList.setCellRenderer(cellRenderer);

That's pretty much all there is to it. The BuddyCellRenderer scales (and caches) the Icons provided by the Adapter if they're bigger than BuddyCellRenderer.getBuddyIconSize(). It also caches the "grayed out" version of the icon that's used when a Buddy's status is offline. Alternating rows are rendered in an off-white color to help with readability and the whole thing is layed out internally with the old Swing layout veteran: GridBagLayout.

If you'd like to try making some changes to BuddyCellRenderer and the demo, you can download a NetBeans project with the source code and the jar files here: Download BuddyList NetBeans Project .





Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds