Skip to main content

A Reusable BuddyList Component

Posted by hansmuller on February 27, 2006 at 3:30 PM PST

Every now and then someone drops by to ask about the slick chat/IM
demo components that were shown in the href="http://developers.sun.com/learning/javaoneonline/2005/desktop/TS-3605.pdf">
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 src="http://weblogs.java.net/blog/hansmuller/archive/jws-launch-button.png"
width="88"
height="23"
style="border-style:none; margin-right:44px"/>
style="border: none; "/>

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
.

Related Topics >>