The Source for Java Technology Collaboration
User: Password:



Hans Muller

Hans Muller's Blog

A Reusable BuddyList Component

Posted by hansmuller on February 27, 2006 at 03:30 PM | 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 .


Bookmark blog post: del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment

  • Hans--as usual, nice, clean, well-written article. What interests me is how this could be made a bit more generic, once there is some standard data binding library (SwingLabs, the infamous JSR, whatever). That is, each "row" in the list (here list of buddies) could be bound to a standard "form" (here, the buddy display) and that could be wired up to a standard JList. You'd need to set up the list binding, then the getListCellRendererComponent() would bind a reusable instance of the form with the row being requested. That way you could swap in different views or layouts and have them bound automatically and displayed in a list. Same would work for table or tree layouts. Nice article, please keep them coming. Cheers, Patrick

    Posted by: pdoubleya on February 28, 2006 at 05:30 AM

  • Hans,

    >I certainly like the idea of resuable, configurable/extensible, chat client GUI parts

    Check out JClaim.sourceforge.net

    You can pop your stuff in within an hour. Just replace the JLabel with a custom renderer (in ContactWrapper). And swap your list renderer with the one there (ListRenderer).

    You can make your client look however you want and you instantly get all support for all mediums too. Quite nice, actually.

    JClaim Link

    Posted by: alex_1 on February 28, 2006 at 07:25 AM

  • Nice article. I think Java/Swing would benefit TREMENDOUSLY if practical examples such as this one were included in the Java Tutorial (maybe a section, "Practical Swing"?). A problem I see now is that a lot of students go to the Java Tutorial and that is all they see unless they are very ambitious and start google around and go and dig into community site such as this excellent site. But the Java tutorial still forms the impression for Java new-comers on how to do things "the Java way" and what's possible. I think nice slick component examples such as this would be an excellent starting point for new programmers.

    Posted by: kristensson on February 28, 2006 at 08:35 AM

  • [pdoubleya] I agree with you that this is an example of a data binding
    problem, one that a Swing data binding system should support. It's
    tricky, because, as you pointed out, what you need to provide is
    a concrete binding so much as a binding template, that gets applied
    to a data row (the data source) and a cell renderer component (the
    binding's GUI target).

    Posted by: hansmuller on February 28, 2006 at 08:39 AM

  • [alex_1] JClaim sounds interesting. The folks at Sun who asked me to
    build BuddyList where using another multi-protocol Java IM library.
    I'm not going to say which one however its API is remarkably, uh,
    ornate. I'll suggest looking at JClaim. I tried running the web
    started demo against 1.6beta and got an exception from JDIC.
    I'd paste the essential backtrace frames here if I could; sadly
    the process (including the Java Console) is hung. I will ask the web
    start guys about that.

    Posted by: hansmuller on February 28, 2006 at 08:54 AM

  • [kristensson] I agree about the importance of practical examples.
    We're working on a new version of the desktop java part
    java.sun.com and will try and provide a well organized roll-up
    of this kind of practical example there.

    Posted by: hansmuller on February 28, 2006 at 09:18 AM

  • Even though the Extreme Makeover code is rough, I'd still like to see it.

    Posted by: neilweber on February 28, 2006 at 02:52 PM

  • Interestingly I spent some of yesterday updating the SwingModelFactory in the jYMSG support package. The list needs to be filterable - that is to say it should be easy to only show users by criteria. This is to allow features like hiding offline users, which some clients allow.

    This isn't the first time I've had to do filtering wrt. Swing models/cell renderers, and I'm beginning to think Java might benefit from a standard, efficient, filterable collection class. One who's size and contents show only a subset of its true contents, based upon a simple isVisible(Object) interface method. Sortable (as per other collections) naturally.

    Writing a slow one is easy - but writing an efficient one, which doesn't require a trawl over most of the contents to find the nth item, or the size of the collection, isn't so straight forward. Plus little things: like for example, adding or removing data is no longer a straight success/fail scenario. An item could be successfully added/removed, but the contents of the collection remain unchanged.

    Posted by: javakiddy on March 01, 2006 at 02:15 AM

  • BTW: The BuddyList is extremly slow and leaking image data. The cache will grow on each painting and the cache hit is zero.

    Posted by: tiom on March 01, 2006 at 02:26 AM

  • [tiom] You're right! I'd forgotten to cache ImageIcons
    I loaded in the demo BuddyAdapter class. I've fixed that.

    Posted by: hansmuller on March 01, 2006 at 08:52 AM

  • The ImageIcon caches should be cleared if somebody changes the buddy icon size.

    In use of this renderer, it's awkward that ImageIcon is used everywhere instead of the more general Icon interface. My particular use case is that I don't want to display buddy icons. My adapter should return null but that gets me the default buddy icon. If Icon were used rather than ImageIcon, my adapter could just return a SwingX EmptyIcon.

    Posted by: neilweber on March 28, 2006 at 01:29 PM

  • Wow...this is great. I will update my chat application to have this list. Please we need more examples like this and wat of the chat text area...pls explain dat too....

    Jaji

    Posted by: lukman_jaji on June 19, 2007 at 04:30 AM

  • A question pls......

    Instead of using an array, how can I popuate a vector instead so that my list component is initialized with it...and can this be used in a commercial chat application...?

    Posted by: lukman_jaji on July 12, 2007 at 10:30 AM



Only logged in users may post comments. Login Here.


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