 |
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
|
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 Digg DZone Furl 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
|