![]() ![]() |
![]() |
![]() ![]() ![]() ![]() |
![]() |
Swing's JTree object is a user-interface component that presents arbitrary hierarchical data in an outline format. The classes used to create and manage JTree components are:
The TreeModel interface is defined as follows:
public interface TreeModel { public Object getRoot(); public Object getChild(Object parent, int index); public int getChildCount(Object parent); public boolean isLeaf(Object node); public void reload(); public void reload(Object node); public void addTreeModelListener(TreeModelListener x); public void removeTreeModelListener(TreeModelListener x); public void valueForPathChanged(JTreePath path, Object newValue); } public interface TreeModelListener extends java.util.EventListener { void treeNodesChanged(TreeModelEvent e); void treeNodesInserted(TreeModelEvent e); void treeNodesRemoved(TreeModelEvent e); void treeStructureChanged(TreeModelEvent e); } public class TreeModelEvent extends java.util.EventObject { TreeModelEvent(TreeModel source, Object[] path, int[] childIndices); TreeModelEvent(TreeModel source, JTreePath path, int[] childIndices); public Object[] getPath(); public JTreePath getTreePath(); public int[] getChildIndices(); }
The model defined in the preceding interface provides a tree structure for a JTree component (or some other kind of hierarchy control) to display. The model can decide the data type of each node; for example, a model that encapsulates a file system might use instances of java.io.File as its nodes. A JTree communicates with the model and with clients of the JTree using the nodes as common currency (for example, asking the JTree for its selection returns a set of these nodes).
It is important to note that a TreeModel encapsulates an entire tree, not just its root node. This convention has at least two advantages: It lets the implementer use any object that he or she chooses as a node class, and it simplifies sparse implementations of trees.
The TreeModel provides a reference to the root of a tree and provides information about the rest of the tree structure relatively -- that is, it can return the children or a child count of a given node, and it can report whether or not a given node is a leaf. The children (if any) of each node must be implemented in a defined order. Many models can implement the isLeaf() method to return true if the node has no children, but other models may separate the two concepts -- for instance, a directory in a file system is not a leaf but might not have any children.
When a value has been changed, usually as the result of a UI gesture, valueForPathChanged() is invoked. At this point, it is up to the model to decide whether the value should (in fact) be changed -- and, if so, to create the appropriate event and notify all listeners.
A TreeModel uses a finer-grained set of event messages than a typical Swing model. A single event indicating that something about the tree has changed is of little use if the tree is large or complex; a tree component listening for such an event would need to traverse the tree to figure out what had changed. Instead, the TreeModel can send its listeners four different messages that identify more precisely what has changed:
JTree updates the interface accordingly when it receives any of the above events. Thus, by creating the appropriate events and passing them to the listeners, the tree updates itself. If you use JTreeModel as your TreeModel (this is created for you if you create a JTree with a TreeNode) you must message it with one of the following when you alter the TreeNodes: reload(), reload(TreeNode), nodesWereInserted(TreeNode, int[]), nodesWereRemoved(TreeNode, int[]), nodeChanged(TreeNode), or nodesChanged(TreeNode, int[]). Calling one of these methods creates the appropriate event and message the listeners.
JTree uses the TreeSelectionModel which is used to represent a set of JTreePath instances. TreeSelectionModel can also maintain an index associated with each JTreePath instance. Mapping a JTreePath to an index is accomplished by way of the RowMapper interface. The RowMapper interface consists of one method: getRowForPath(). TreeSelectionModel does not need a RowMapper to manage the set of JTreePaths -- but without one all of the row-based methods, getRowForPath behaves as if nothing is selected. When you use the TreeSelectionModel provided by JTree, a RowMapper is set in such a way that that you can ask the TreeSelectionModel that is returned from getSelectionModel() for row-based information. If you want to alter the selection of JTree, it is perfectly legal to manipulate the selection by way of the TreeSelectionModel. The UI updates itself accordingly. The only caveat is that if you add a path to the TreeSelectionModel and any of the items in the path aren't expanded, the JTree will not attempt to expand it for you. For this reason, you should manipulate the selection through the methods provided by JTree.
public interface TreeSelectionModel { void setSelectionPath(JTreePath path); void setSelectionPaths(JTreePath[] paths); void addSelectionPath(JTreePath path); void addSelectionPaths(JTreePath[] paths); void removeSelectionPath(JTreePath path); void removeSelectionPaths(JTreePath[] paths); void clearSelection(); JTreePath getSelectionPath(); JTreePath[] getSelectionPaths(); int getSelectionCount(); boolean isPathSelected(JTreePath path); boolean isSelectionEmpty(); void setRowMapper(RowMapper newMapper); RowMapper getRowMapper(); int[] getSelectionRows(); int getMinSelectionRow(); int getMaxSelectionRow(); boolean isRowSelected(int row) void resetRowSelection(); void addTreeSelectionListener(TreeSelectionListener x); void removeTreeSelectionListener(TreeSelectionListener x); }
TreeSelectionModel provides three basic types of methods to operate on the set of JTreePath instances it maintains. There are two methods for setting the set of path (setSelectionPath() and setSelectionPaths()), two for adding to the set (addSelectionPath() and addSelectionPaths()), and two for removing from the set (removeSelectionPath() and removeSelectionPaths()). You can empty the set by calling clearSelection(). The set of paths that are selected can be determined by calling getSelectionPath(), which returns the first selected path, and getSelectionPaths(), which returns the complete set. The getSelectionCount() method returns the number of paths in the current set.
A path can be tested for inclusion in the set by calling isPathSelected(), and the empty selection set can tested by using isSelectionEmpty(). If an instance implementing RowMapper is provided, the selected rows can be determined by calling getSelectionRows(), the first selected row can be determined by calling getMinSelectionRow(), and the last selected row can be determined by calling getMaxSelectionRow(). You can check to see if a particular row is in the set by calling isRowSelected(). The resetRowSelection() method does not normally need to be called, but is used to update the rows that are selected by messaging the RowMapper for each path in the selection set.
The JTree class provides cover methods for TreeModel and TreeSelectionModel.
In pursuit of speed and a light weight, a JTree renders each of its nodes using a single cell renderer supplied by the client -- an approach similar to that used by JListBox). If none is provided, the JTree uses a simple default renderer that draws its value's response toString() (see the section titled "The TreeNode API").
Tree cell renderers must be components, and they must implement the TreeCellRenderer interface, which is defined as follows:
public interface TreeCellRenderer { void configureTreeCellRenderer(Object value, boolean selected, boolean focused); Component getTreeCellComponent(); }
In this interface, the value parameter is the node that is being drawn. The selected flag causes the renderer to draw a cell differently if it is selected, while the focused flag specifies that the Tree has focus. It is up to the renderer to represent the selected state, usually by drawing a background color.
Editing is accomplished by way of the TreeCellEditor interface. TreeCellEditor extends CellEditor with the method configureTreeCellEditor(Object value, boolean isSelected, boolean expanded, boolean leaf). A default implementation of TreeCellEditor is provided for the basic look and feel. If you wish to allow editing you need to message JTree with setEditable(true). The sequence of events in editing is:
After this sequence of events, editing can be stopped in a number of ways. Any sequence of events that would cause another item to be selected in the tree causes the editing to be canceled. You can accomplish the same thing by invoking cancelEditing() on the TreeCellEditor. Editing is stopped if the tree receives an editingStopped event, which is created by way of invoking stopEditing() on the tree or on the editor. If editing has stopped, the TreeModel is messaged with the path of the value that was edited and the new value. If the model decides to commit the change, it should then update its internal state and create a nodeChanged event. The tree receives this information and update itself accordingly.
At the current stage of development, features that remain to be added to JTree and its associated classes include the following:
The TreeNode API is defined as follows:
public class TreeNode extends Object implements Cloneable, Serializable { public TreeNode(); public TreeNode(Object userObject); public TreeNode(Object userObject, boolean allowsChildren); public void insertChildAt(TreeNode newChild, int childIndex); public void removeChildAt(int childIndex); public TreeNode getParent(); public TreeNode getChildAt(int index); public int getChildCount(); public int getIndex(TreeNode aChild) public TreeNode[] getPath(); public Object[] getUserObjectPath(); public Enumeration children(); public void setAllowsChildren(boolean allows); public boolean getAllowsChildren(); public void setUserObject(); public Object getUserObject(); // Includes convenience methods for adding, removing, replacing nodes // Includes methods for querying about ancestors and descendants // Includes methods for querying about the tree depth, level, and root public Enumeration preorderEnumeration(); public Enumeration postorderEnumeration(); public Enumeration breadthFirstEnumeration(); public Enumeration depthFirstEnumeration(); public Enumeration pathFromAncestorEnumeration(); // Includes methods for querying about children, siblings, and leaves
At the current stage of TreeNode's development, the following notes apply:
TreeNode root = new TreeNode("root"); TreeNode child = new TreeNode("Letters"); root.add(child); child.add(new TreeNode("Able")); child.add(new TreeNode("Baker")); child.add(new TreeNode("Charlie")); JTree tree = new JTree(root); // Put it somewhere the user can see it // Time passes, we want to add something to the tree child = new TreeNode("Numbers"); root.add(child); child.add(new TreeNode("One")); child.add(new TreeNode("Two")); child.add(new TreeNode("Three")); tree.getModel().reload(child);
Version 0.4. Last modified 09/04/97.
Copyright © 1995-97 Sun
Microsystems, Inc. All Rights Reserved.