In Figure 11.4, you can see that the data is displayed as a tree, but somewhat clumsily. The default tree implementation calls the toString method of the Object class for each node. This probably is not the desired string for a Vector, Hashtable, or array. The toString method in the Vector class is final, so an anonymous extension to the Vector class that overrides the toString method cannot be used. Later in this chapter you will see how the JTree class can be extended and how a custom rendering class can be used to overcome this situation. Even though the basic collections can be used to populate the tree, simply put, they are the wrong data structure for a tree. Fortunately, the JFC defines the TreeModel and TreeNode interfaces that are the correct data structure for a tree. When a collection is used to initialize a tree, a tree model is created by traversing the collection and creating parent nodes for each collection found and leaf nodes for other types of objects. These interfaces and the classes that implement them in the JFC are the subject of the next section.
The JTree class defines three selection modes. SINGLE_TREE_SELECTION mode allows the selection of only one path at a given time. The CONTIGUOUS_TREE_SELECTION mode allows multiple contiguous paths to be simultaneously selected. The DISCONTIGUOUS_TREE_SELECTION mode allows any paths to be simultaneously selected. The DISCONTIGUOUS_TREE_SELECTION mode is the default selection mode for a JTree instance. The selection mode is defined and enforced by the trees selection model discussed in the next section.
The swing.tree Package
The interfaces that define the trees data model and the nodes contained in that model are defined in the javax.swing.tree package. The classes that implement these interfaces and are used internally by the JFC are located in this package also. The classes and interfaces are available for you to use in your applications as well. This section describes the interfaces and classes that are typically encountered when building tree views in the JFC.
TreeModel Interface
The TreeModel interface contained in the swing.tree package defines the data model used by the JTree class (see Listing 11.3). By examining the interface, you can see that the model consists primarily as a reference to the trees root node. The other methods are convenience methods to hide implementation details from the view. For example, the number of children can be determined for any node in the tree by passing the getChildCount method to the node whose child count is desired. How to find the node in the tree data structure is hidden from the caller.
Listing 11.3 The TreeModel Interface
public interface TreeModel
{
/**
* Returns the root of the tree. Returns null only if the tree has
* no nodes.
*
* @return the root of the tree
*/
public Object getRoot();
/**
* Returns the child of <I>parent</I> at index <I>index</I> in the
* parents child array. <I>parent</I> must be a node previously
* obtained from this data source.
*
* @param parent a node in the tree, obtained from this data source
* @return the child of <I>parent</I> at index <I>index</I>
*/
public Object getChild(Object parent, int index);
/**
* Returns the number of children of <I>parent</I>. Returns 0 if the
* node is a leaf or if it has no children. <I>parent</I> must be a
* node previously obtained from this data source.
*
* @param parent a node in the tree, obtained from this data source
* @return the number of children of the node <I>parent</I>
*/
public int getChildCount(Object parent);
/**
* Returns true if <I>node</I> is a leaf. It is possible for this
* method to return false even if <I>node</I> has no children. A
* directory in a file system, for example, may contain no files;
* the node representing the directory is not a leaf, but it also
* has no children.
*
* @param node a node in the tree, obtained from this data source
* @return true if <I>node</I> is a leaf
*/
public boolean isLeaf(Object node);
/**
* Messaged when the user has altered the value for the item
* identified by <I>path</I> to <I>newValue</I>. If <I>newValue</I>
* signifies a truly new value the model should post a
* treeNodesChanged event.
*
* @param path path to the node that the user has altered.
* @param newValue the new value from the TreeCellEditor.
*/
public void valueForPathChanged(TreePath path, Object newValue);
/**
* Returns the index of child in parent.
*/
public int getIndexOfChild(Object parent, Object child);
/**
* Adds a listener for the TreeModelEvent posted after the tree
* changes.
*
* @see #removeTreeModelListener
* @param l the listener to add
*/
void addTreeModelListener(TreeModelListener l);
/**
* Removes a listener previously added with
* <B>addTreeModelListener()</B>.
*
* @see #addTreeModelListener
* @param l the listener to remove
*/
void removeTreeModelListener(TreeModelListener l);
}
The DefaultTreeModel class is a complete implementation of the TreeModel interface. It is used internally by the JTree class as the TreeModel when a TreeModel is not explicitly set. The DefaultTreeModel class requires a TreeNode in its constructor. This node is used as the root of the tree. The swing.tree package defines interfaces for two types of tree nodes, a mutable and immutable variant. These interfaces are described in the next section.
|