JTree Best Practices

O

Omega

This is a question for the experts (obviously).

I'm working on a small project to develop a messenger-type application.
I'm planning on using the JTree object to represent my contact list.
It seems like a very nice all-in-one expandable (in the future) object.
(I don't need alternative pre-made messenger solutions to solve all my
woes, I have to write this program myself.)

Sadly, my Swing experience is still limited - just the usual 2D
graphics demos. I don't have a lot of experience working with the more
complicated objects. JTree appears to make use of a lot of different
objects apart from itself.

Is anyone able to explain to me how to populate, modify (at run time)
and in general get really good at using the JTree object?

Specifically:
- Should I be extending JTree into an object called "ContactListUI" and
then customizing it?
- What are the best practices to add/delete leaf & branch nodes?
- Should objects from the outside be generating nodes and then push
them into the tree or should the tree just be told to make a node with
a specific title? (Encapsulation - grr!)

My idea is that I can extend JTree to become my all-in-one contact list
UI (no networking code), and have it track the association between user
names and unique user IDs...Any interactions from the user will cause
the JTree to poke & prod the appropriate control classes with the right
unique IDs.

I would LOVE for this to develop into a philosophical discussion. All
the ideas deserve to get out on the table here!

:)

- Omega
 
O

Oliver Wong

Omega said:
This is a question for the experts (obviously).

I'm working on a small project to develop a messenger-type application.
I'm planning on using the JTree object to represent my contact list.
It seems like a very nice all-in-one expandable (in the future) object.
(I don't need alternative pre-made messenger solutions to solve all my
woes, I have to write this program myself.)
[snip]

Is anyone able to explain to me how to populate, modify (at run time)
and in general get really good at using the JTree object?

Have you looked at the TreeModel interface?

http://java.sun.com/j2se/1.5.0/docs/api/javax/swing/tree/TreeModel.html

[snip]
My idea is that I can extend JTree to become my all-in-one contact list
UI (no networking code), and have it track the association between user
names and unique user IDs...Any interactions from the user will cause
the JTree to poke & prod the appropriate control classes with the right
unique IDs.

Seems like internally, you want to treat your collection of users as a
map (from their unique user IDs to the actual user objects), but want to
provide a "tree-like" view of them, for the purpose of displaying them on a
contact list.

- Oliver
 
T

Thomas Fritsch

Omega said:
Sadly, my Swing experience is still limited - just the usual 2D
graphics demos. I don't have a lot of experience working with the more
complicated objects. JTree appears to make use of a lot of different
objects apart from itself. Yes ;-)

Is anyone able to explain to me how to populate, modify (at run time)
and in general get really good at using the JTree object?
A good entry point for reading is:
http://java.sun.com/docs/books/tutorial/uiswing/components/tree.html
Specifically:
- Should I be extending JTree into an object called "ContactListUI" and
then customizing it?
I think the most recommended practice is:
(*) Do not extend JTree, but use JTree as is.
(*) Either develop your own implementation of TreeModel,
or use DefaultTreeModel together with an implementation of
TreeNode (DefaultMutableTreeNode or a class of your own).
(*) Create your tree with the JTree(TreeModel) constructor.
- What are the best practices to add/delete leaf & branch nodes?
Just call the appropriate methods of your TreeModel:
(*) If you use DefaultTreeModel: call its methods
insertNodeInto(..) or removeNodeFromParent(..)
(*) If you use your own implementation of TreeModel: you obviously have
to implement your own methods for inserting/removing a node.
 
O

Omega

Thank you both of you for such prompt responses.

I certainly don't mind looking at the documentation, I had been looking
mostly at JTree, so the TreeModel stuff clearly seems to be what I
should be focusing on.

The first one I looked at was the TreeModel interface. Am I correct in
assuming then that I would create a class that manages the listings and
associations between all my users, and implement TreeModel as one of
it's interfaces?
Again the question of encapsulation comes up here. Is my TreeModel
object then the right place to have a bunch of vectors (arrays,
whatever) pointing to User objects? Would my TreeModel be responsible
for creating and destroying instances of Users (in this case)?
Looking at the interface again, I'm guessing my User objects would then
also have to extend some kind of interface to properly support being
handed over to the JTree?

(When I say "User objects" I mean objects of the class "User"...heh)

This is all really cool. The ideas you can get with this sort of
flexibility can kind of overwhelm your planning. Hopefully someone
desperatley searching newsgroups in the future will find this useful.

- Omega
 
O

Oliver Wong

Omega said:
Thank you both of you for such prompt responses.

I certainly don't mind looking at the documentation, I had been looking
mostly at JTree, so the TreeModel stuff clearly seems to be what I
should be focusing on.

The first one I looked at was the TreeModel interface. Am I correct in
assuming then that I would create a class that manages the listings and
associations between all my users, and implement TreeModel as one of
it's interfaces?
Again the question of encapsulation comes up here. Is my TreeModel
object then the right place to have a bunch of vectors (arrays,
whatever) pointing to User objects? Would my TreeModel be responsible
for creating and destroying instances of Users (in this case)?
Looking at the interface again, I'm guessing my User objects would then
also have to extend some kind of interface to properly support being
handed over to the JTree?

(When I say "User objects" I mean objects of the class "User"...heh)

This is all really cool. The ideas you can get with this sort of
flexibility can kind of overwhelm your planning. Hopefully someone
desperatley searching newsgroups in the future will find this useful.

I'm making wild speculations here because I don't know much about your
IM program. I'm assuming it's like most other IM programs I've encountered
(e.g. MSN, AIM, YIM, ICQ, GAIM, Trillian, etc.)

A tree structure isn't the best way to (internally) store the users.
Rather, you'd probably want a list of groups, with each group consisting of
a list of users. This is based on the fact that all IMs I've seen are build
such that groups cannot contain other groups, and users cannot contain other
groups or other users.

Some IMs let a user belong to more than one group simultaneously, others
don't. If you do let a user belong to more than one group, then I think a
list of groups, each of which is a list of users, is definitely the way to
go. Otherwise, if a user can only belong to one group, then perhaps
internally, the users would contain a reference to the group they belong to.

You probably want to do a have a quick lookup from user ID to user
object. Presumably your messaging protocol will be such that a message from
a given user will sent in the form of something containing the actual
message typed by the user, and their user ID.

So as I mentioned in another post, you probably want a map from user-id
to users.

For the purpose of the the JTree, you need to present a "tree-like"
view, but internally, there won't be any tree. So for your tree-like view,
when they ask for a list of all the children of the root, just return all
the groups. When they ask for a list of children for a particular group,
return all the users part of that group. When they ask for a list of
children for a particular user, say there aren't any.

- Oliver
 
T

Thomas Fritsch

Omega said:
I certainly don't mind looking at the documentation, I had been looking
mostly at JTree, so the TreeModel stuff clearly seems to be what I
should be focusing on. Fine!

The first one I looked at was the TreeModel interface. Am I correct in
assuming then that I would create a class that manages the listings and
associations between all my users, and implement TreeModel as one of
it's interfaces?
Yes, the main job of the TreeModel is to know the tree-like connections
between the nodes, i.e.:
(*) which is the root object?
(May be you choose that root object not to be of type User, but
something else)
(*) which is the parent User of each User?
(*) which are the child Users of each User?
You could achieve all that by having the following things in your TreeModel:
private Node root;
private class Node {User user; Node parent; List said:
Again the question of encapsulation comes up here. Is my TreeModel
object then the right place to have a bunch of vectors (arrays,
whatever) pointing to User objects?
You can implement your TreeModel in any way you like.
One design (my Node-approach above) might be, that only your TreeModel knows
the connections between your User objects, and the User objects don't know.
An alternative design might be, that each User object knows about its own
parent and children, and the TreeModel asks the User object about it.
Would my TreeModel be responsible
for creating and destroying instances of Users (in this case)?
Not necessarily. In my opinion this should not be the TreeModel's job.
Looking at the interface again, I'm guessing my User objects would then
also have to extend some kind of interface to properly support being
handed over to the JTree?
No, the JTree does not all care about the type of the tree nodes. It just
dumbly asks the TreeModel and works with any kind of Object. (Remember that
the TreeModel interface deals only with Object)
 
O

Omega

Hmm...More questions of practice.

Oliver:
I think you've caught the essence of what I'm looking for. A one-deep
tree of groups and users. Users will be members of multiple groups -
thus - I wasn't initially trying to store it as a tree. Just
associations between Group objects and a User object. Then, develop my
associations and then enable them to be queried by a JTree. (Two
perhaps, one for a listing by group, and another alphabetical listing
of the whole.)

Thomas:
Not necessarily. In my opinion this should not be the TreeModel's job.

In this instance would the fact that the TreeModel may be some other
object capable of responding to tree model messages give any sort of
leniance? Or would I have my communications objects (the ones actually
receiving notifications from the server) just register the User objects
it creates to the tree model?

What I'm seeing here - and I could be mistaken, is a collection class
(thus - the duties of creating and destroying) that has been granted
the ability to present it's information to any JTree that it is
associated with...
I think your node approach is the ideal solution as it keeps the
responsibility of managing a purely UI function to one object - rather
than each User object.
No, the JTree does not all care about the type of the tree nodes. It just
dumbly asks the TreeModel and works with any kind of Object. (Remember that
the TreeModel interface deals only with Object)

A better question would be: What method on the User object will JTree
call to derive the visual representation? I think we're getting into
the renderer classes here, aren't we?
Does it just do a toString(), and call it a day? I've seen it in
action with other Swing components, but I'm not aware of whether their
use with a JTree was anticipated. Will my User objects have to be in
any way prepared to be referenced to a JTree from a TreeModel?
 
T

Thomas Fritsch

Omega said:
A better question would be: What method on the User object will JTree
call to derive the visual representation?
The JTree calls no User-method at all. Instead it asks its TreeCellRenderer
for a Component to be rendered.
I think we're getting into the renderer classes here, aren't we?
Exactly. And if the JTree has no explicit TreeCellRenderer set, it uses a
DefaultTreeCellRenderer. This beast is a subclass of JLabel, which (you
guess it) simply renders what your User's toString() method returns. You can
call the JTree's setCellRenderer method to provide a smarter renderer.
Does it just do a toString(), and call it a day? I've seen it in
action with other Swing components, but I'm not aware of whether their
use with a JTree was anticipated. Will my User objects have to be in
any way prepared to be referenced to a JTree from a TreeModel?
No, your User doesn't have to be prepared in any way. But your
TreeCellRenderer probably should be. You may want to implement one:
public class UserTreeCellRenderer extends JPanel implements
TreeCellRenderer {
// the rest is left to the reader as an exercise ;-)
}
 
O

Omega

Haha. Certainly an exercise, and I'm sure if I ever tackle it, you'll
see me on here again.

Right now I'd be happy with the default TreeCellRenderer class, I did
start playing around with that - but found the results somewhat
unpredictable. I'm sure I was doing something silly.
For my purposes right now, I don't need to do a renderer, and it's well
done enough that should I ever need to make one, it's a drop-in
addition of one class and a call to set it as the renderer. Bravo
Java.

And on the TreeModel being a collection class as well, I assume you
would suggest maintaning my User objects more on the data side of
things with my connection classes? Rather than lump it all into a
collection class that implements TreeModel?

I think that just about covers it!

Thank you so much for the help, and here's to hoping this thread can
help others in the future!
 
O

Omega

Just as a note for over the weekend, I've started working on my custom
TreeModel, and I'm having a few problems.

While I can get my root node to show up (it's specified within my
model, as it never changes, and there's only one), none of the Groups
that I add underneath are showing up. I've created custom classes that
implement the MutableTreeNode interface. All interfaces have what I
beleive to be the required code to represent my very simple model. Of
course, I can't really test it until I get the JTree actually trying to
render the tree.

The only methods in my model that I haven't written code for are the
addTreeModelListener() and removeTreeModelListener() events. I looked
at the JTree source, and it does seem like it makes calls to it. I'm
just not sure if those calls are the ones that are not getting
satisfied.

Do I have to implement those two listener methods (thus, create my own
event that generates TreePaths) - or is my problem elsewhere?
 
T

Thomas Fritsch

Omega said:
Just as a note for over the weekend, I've started working on my custom
TreeModel, and I'm having a few problems.

While I can get my root node to show up (it's specified within my
model, as it never changes, and there's only one), none of the Groups
that I add underneath are showing up. I've created custom classes that
implement the MutableTreeNode interface. All interfaces have what I
beleive to be the required code to represent my very simple model. Of
course, I can't really test it until I get the JTree actually trying to
render the tree.

The only methods in my model that I haven't written code for are the
addTreeModelListener() and removeTreeModelListener() events. I looked
at the JTree source, and it does seem like it makes calls to it. I'm
just not sure if those calls are the ones that are not getting
satisfied.

Do I have to implement those two listener methods (thus, create my own
event that generates TreePaths) - or is my problem elsewhere?
Of course the add/removeTreeModelListener methods of your TreeModel must do,
what they are supposed to do: Add the listener, or remove the listener. (See
the API doc of java.swing.event.EventListenerList for how you can implement
these.) Furthermore: Your TreeModel must fire TreeModelEvents to all the
registered listeners, when it is appropriate.
These things are essential, because the JTree will add itself as a
TreeModelListener of your TreeModel, and it will rely on the events it will
receive.
 
T

Thomas Fritsch

Thomas Fritsch said:
Of course the add/removeTreeModelListener methods of your TreeModel must
do, what they are supposed to do: Add the listener, or remove the
listener. (See the API doc of java.swing.event.EventListenerList for how
you can implement these.) Furthermore: Your TreeModel must fire
TreeModelEvents to all the registered listeners, when it is appropriate.
These things are essential, because the JTree will add itself as a
TreeModelListener of your TreeModel, and it will rely on the events it
will receive.
Actually the JTree does not register *itself* as a TreeModelListener, but it
does so with a helper object (internal class JTree.TreeModelHandler).
Of course that doesn't change the point, that you have to manage the
listeners in your TreeModel.
 
O

Omega

I've done the best I can according to the documentation...But I can't
help but feel like I'm just doing everything wrong.
It seems like all my functions just store references in vectors, search
vectors and return all of it when asked.

Yet it still isn't working...Any interest in seeing my code?
 
Z

zero

I've done the best I can according to the documentation...But I can't
help but feel like I'm just doing everything wrong.
It seems like all my functions just store references in vectors, search
vectors and return all of it when asked.

Yet it still isn't working...Any interest in seeing my code?

A general hint when working with TreeModels or TableModels or
ComboBoxModels or ...: start with Default<Foo>Model - in your case
DefaultTreeModel. Extend it, and override only what you need. (As a side
note, ask yourself if you really need to have your own Model. It's quite
possible the default will do fine, where you just add appropriate
ModelListeners to get your custom behaviour.)

As an alternative, you can examine the source of the Default<Foo>Model, and
change some parts of it. Not really OO-like, but it may help you
understand the model better.

Only if you're really sure that you need some other class to be the
superclass of your model, should you create your own model. By this I mean
that unless you really need for example "MyTreeModel extends HashMap
implements TreeModel", you should instead use "MyTreeModel extends
DefaultTreeModel".

First follow this advice, then consider posting code if you still can't get
it to work.

Btw, I didn't examine your original post with what you're trying to do, but
from skimming it I think a JTree is not your best choice. You're only
going one level deep (groups that have users), so you don't need the
(considerable) complexity of a TreeModel.
 
O

Oliver Wong

Omega said:
I've done the best I can according to the documentation...But I can't
help but feel like I'm just doing everything wrong.
It seems like all my functions just store references in vectors, search
vectors and return all of it when asked.

The idea behind the listeners is that when your model changes, you
notify anyone who's listening about those changes, so that they can take not
of the change and behave accordingly. In this case, the JTree will want to
listen to your model so that when it changes (e.g. a new user is added to a
certain group), the JTree can re-render itself on screen with the new data.

You might code in your own listeners too. For example, to implement an
"Alert me when this user comes online" feature, you might add a listener to
the model, and when the model changes, you check if the status of a specific
user has changed from offline to online, rather than polling the model at
regular intervals.

Another hint: listeners are usually designed so that you can notify the
listener of the exact changed that occured. I personally find this difficult
to grasp, so what I usually do is just tell the listeners "*everything* has
changed". This is less optimal than telling the listener "This specific node
has changed", for example, but it'll get rid of bugs due to not specifying
the correct changes.

Once you get it to work by simply saying everything has changed, then
you can go back and try to make the change-notification code more and more
specific.
Yet it still isn't working...Any interest in seeing my code?

Yes, it'll probably speed up the debugging process, especially if you
trim your code down to an SSCCE.

http://mindprod.com/jgloss/sscce.html

- Oliver
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

Forum statistics

Threads
473,982
Messages
2,570,186
Members
46,739
Latest member
Clint8040

Latest Threads

Top