17.3 The JTree Class


Now that you've seen all the tree models and some of the default implementations, let's look at the visual representation we can give them. The JTree class can build up trees out of several different objects, including a TreeModel. JTree extends directly from JComponent and represents the visual side of any valid tree structure.

As another example of hierarchical data, let's look at a tree that displays XML documents. (We'll leave the details of XML to Brett McLaughlin and his excellent Java and XML book. Of course, as our own Bob Eckstein also wrote the XML Pocket Reference, we'll include a shameless plug for that, too.) Here's an entirely contrived XML document that contains several layers of data:

<?xml version="1.0"?> <simple>  <level1 attr="value" a2="v2">    This is arbitrary data...    <emptytag1 />    <et2 a1="v1"/>    <level2 more="attributes">      <input type="text" name="test"/>    </level2>  </level1>  <!-- one more level to test...--><test/>  <one>   <two>    <three>     <four/>     <five/><fiveA/>    </three>    <six/>    <seven>     <eight/>    </seven>   </two>   <nine/>  </one>  <multi><line>test</line></multi> </simple>

Figure 17-6 shows the representation of this document in a JTree.

Figure 17-6. A JTree built by parsing an XML document
figs/swng2.1706.gif

In this example, we treat XML tags with children as nodes and tags without children as leaves. Any tag with actual data (not counting the attributes more on those later) shows that data in its label. We create a simple inner class to store the tags as they are generated by the XML parser. The other inner class, XMLTreeHandler, fills in the tree model based on the parser's events.

Here's the source code for VSX.java, our Very Simple XML example:

// VSX.java import javax.swing.*; import javax.swing.tree.*; import java.util.*; import java.io.*; import org.xml.sax.*; import org.xml.sax.helpers.*; import javax.xml.parsers.*; public class VSX {   public TreeModel parse(String filename) {     SAXParserFactory factory = SAXParserFactory.newInstance( );     XMLTreeHandler handler = new XMLTreeHandler( );     try {       // Parse the input.       SAXParser saxParser = factory.newSAXParser( );       saxParser.parse( new File(filename), handler);     }     catch (Exception e) {       System.err.println("File Read Error: " + e);       e.printStackTrace( );       return new DefaultTreeModel(new DefaultMutableTreeNode("error"));     }     return new DefaultTreeModel(handler.getRoot( ));   }   public static class XMLTreeHandler extends DefaultHandler {     private DefaultMutableTreeNode root, currentNode;       public DefaultMutableTreeNode getRoot( ) {       return root;     }     // SAX parser handler methods     public void startElement(String namespaceURI, String lName, String qName,                              Attributes attrs) throws SAXException {       String eName = lName; // Element name       if ("".equals(eName)) eName = qName;       Tag t = new Tag(eName, attrs);       DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(t);       if (currentNode == null) {         root = newNode;       }       else {         // Must not be the root node         currentNode.add(newNode);       }       currentNode = newNode;     }     public void endElement(String namespaceURI, String sName, String qName)     throws SAXException {       currentNode = (DefaultMutableTreeNode)currentNode.getParent( );     }     public void characters(char buf[], int offset, int len) throws SAXException {       String s = new String(buf, offset, len).trim( );       ((Tag)currentNode.getUserObject( )).addData(s);     }   }   public static class Tag {     private String name;     private String data;     private Attributes attr;     public Tag(String n, Attributes a) { name = n; attr = a; }     public String getName( ) { return name; }     public Attributes getAttributes( ) { return attr; }     public void setData(String d) { data = d; }     public String getData( ) { return data; }     public void addData(String d) {       if (data == null) {         setData(d);       }       else {         data += d;       }     }     public String getAttributesAsString( ) {       StringBuffer buf = new StringBuffer(256);       for (int i = 0; i < attr.getLength( ); i++) {         buf.append(attr.getQName(i));         buf.append("=\"");         buf.append(attr.getValue(i));         buf.append("\"");       }       return buf.toString( );     }     public String toString( ) {       String a = getAttributesAsString( );       return name + ": " + a + (data == null ? "" :" (" + data + ")");     }   }   public static void main(String args[]) {     if (args.length != 1) {       System.err.println("Usage is: java VSX testfile.xml");       System.exit(1);     }     JFrame frame = new JFrame("VSX Test");     VSX parser = new VSX( );     JTree tree = new JTree(parser.parse(args[0]));     frame.getContentPane( ).add(new JScrollPane(tree));     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);     frame.setSize(300,400);     frame.setVisible(true);   } }

The parse( ) method does most of the work in this example. The events from the SAXParser are used to determine the structure of the tree. Once the document is parsed, that model is used to build the JTree object that we display inside a JScrollPane.

17.3.1 Properties

The JTree class contains properties (shown in Table 17-3) for manually displaying and editing tree cells if you need this control. The editable property specifies whether cells can be edited (i.e., modified by users). The toggleClickCount property allows you to specify how many clicks are required to start editing a tree node. The cellEditor, cellRenderer, and invokesStopCellEditing properties affect the components used to display and manipulate trees. Setting invokesStopCellEditing to true forces changes to a cell to be saved if editing is interrupted.

Table 17-3. JTree properties

Property

Data type

get

is

set

Default value

accessibleContexto

AccessibleContext

·

   

JTree.AccessibleJTree( )

anchorSelectionPath1.3

TreePath

·

 

·

null

cellEditorb

TreeCellEditor

·

 

·

null

cellRendererb

TreeCellRenderer

·

 

·

null

collapsed1.4 ,i , o, *

boolean

 

·

   

dragEnabled1.4

boolean

·

 

·

false

editableb, o

boolean

 

·

·

false

editing

boolean

 

·

 

false

editingPath

TreePath

·

   

null

expanded1.4 ,i ,o, *

boolean

 

·

   

expandsSelectedPaths

boolean

·

 

·

true

fixedRowHeight

boolean

 

·

 

true

invokesStopCellEditingb

boolean

·

 

·

false

largeModelb

boolean

 

·

·

false

lastSelectedPathComponent

Object

·

   

null

leadSelectionPath

TreePath

·

 

·

null

leadSelectionRow

int

·

   

-1

maxSelectionRow

int

·

   

From selection model

minSelectionRow

int

·

   

From selection model

modelb

TreeModel

·

 

·

null

opaqueb, o

boolean

 

·

·

true

preferredScrollableViewportSize

Dimension

·

   

From L&F

rootVisibleb

boolean

 

·

·

true

rowCount

int

·

     

rowHeightb, +

int

·

 

·

16

rowSelectedi

boolean

 

·

   

scrollableTracksViewportHeight

boolean

·

   

false

scrollableTracksViewportWidth

boolean

·

   

false

scrollsOnExpandb

boolean

·

 

·

true

selectionEmpty

boolean

 

·

 

From selection model

selectionModelb

TreeSelectionModel

·

 

·

DefaultTreeSelec-tionModel( )

selectionPath

TreePath

·

 

·

From selection model

selectionPaths

TreePath[]

·

 

·

From selection model

selectionRows

int[]

·

 

·

From selection model

showsRootHandlesb

boolean

·

 

·

false

toggleClickCount1.3

int

·

   

2

treeExpansionListeners1.4

TreeExpansionList-ener[]

·

   

Empty array

treeSelectionListeners1.4

TreeSelectionList-ener[]

·

   

Empty array

treeWillExpandListeners1.4

TreeWillExpand-Listener[]

·

   

Empty array

UIb, o

TreeUI

·

 

·

From L&F

UIClassIDo

String

·

   

"TreeUI"

visibleRowCountb

int

·

 

·

20

1.3since 1.3, 1.4since 1.4, bbound, iindexed, ooverridden

*Indexed by int and TreePath.

+A value of -1 indicates variable row heights.

See also properties of the JComponent class (Table 3-6).

The display of the tree itself inside a scrollpane is managed by the preferredScrollableViewportSize, scrollableTracksViewportHeight, and scrollable-TracksViewportWidth properties. The preferred viewport size specifies the desired size of the viewport showing the tree. Both of the tracking properties are false to indicate that changing the size of the viewport containing the tree does not affect the calculated width or height of the tree. They can be overridden for specialized behavior. For example, if you placed your tree in a JScrollPane, and the width of that pane were suddenly changed so that a given node might not be visible without scrolling, you could turn on tooltips for the long node and supply a string that contained the entire path for that node. The tooltip pop up would show the whole path without scrolling, regardless of the viewport size.

Several properties give you access to the state of selections for a tree. Properties such as anchorSelectionPath, leadSelectionPath, leadSelectionRow, minSelectionRow, and maxSelectionRow indicate the location of various selected entries. An anchor path is the first entry the user clicked (which may or may not be the same as the min/max selections). You can determine whether a programmatic selection change visually expands the children of a node with the expandsSelectedPaths property. The lastSelectedPath property tracks the most recently selected path.

If you need more control over selections, you can use the selectionModel property. You can modify the current model or even install your own custom model. Most of the time, however, you can just use JTree properties like selectionEmpty , selectionPath, selectionPaths, and selectionRows to let you know exactly which (if any) nodes in the tree are selected.

The JTree class also provides properties that allow you to control the tree's appearance regardless of the display and editing mechanisms used. Many aspects of the tree's appearance are based on the concept of a "row," which is a single item currently displayed in the tree. You can control the row height and root display style with the rowHeight , fixedRowHeight, rootVisible, and showsRootHandles properties. fixedRowHeight specifies that all rows must have the same height; if it is false, row heights may vary. rootVisible is true if the tree's root is displayed; if it is false, the root is omitted. Its initial value depends on which constructor you call. If scrollsOnExpand is true, expanding any node automatically scrolls the tree so that as many of the node's children as possible are visible. showsRootHandles determines whether the one-touch expand/collapse control (or "handle") appears for the root node.

Another interesting property of trees is largeModel . Some UI managers pay attention to this property and alter their behavior if it is set to true, presumably to increase the efficiency of updates and model events. The size of the tree that merits using this property depends largely on your application, but if you're wondering if your tree could benefit from a large model, try turning the property on and playing with your tree to see if you notice a performance gain.

Several indexed properties give you a quick view of the state of the tree. The rowSelected property tells you whether a particular row is selected. The expanded and collapsed properties tell you whether a row is, well, expanded or collapsed.

SDK 1.4 introduced access to the event listeners attached to the tree through the treeExpansionListeners , treeSelectionListeners, and treeWillExpandListeners properties.

Many methods of the JTree class should be considered accessors for properties we haven't listed in Table 17-3. This omission is intentional (though not undebated). We felt it would be clearer if these methods were discussed with similar methods that don't fit the "property" patterns.

17.3.2 Events

The JTree class adds support for the expansion and selection events shown in Table 17-4. We will look at these events in greater detail (with examples) in Section 17.6 later in this chapter.

Table 17-4. JTree events

Event

Description

TreeExpansionEvent

A tree node has been (or will be) expanded or collapsed.

TreeSelectionEvent

A row (path) has been selected or, if more than one row can be selected, a row has been added or removed from the current selection.

The TreeEvents class in the code for this chapter reports all the events generated by the JTree object. These events are supported by the following methods:

public void addTreeExpansionListener(TreeExpansionListener l)
public void removeTreeExpansionListener(TreeExpansionListener l)

Add or remove listeners interested in receiving tree expansion events.

public void addTreeWillExpandListener (TreeWillExpandListener tel)
public void removeTreeWillExpandListener (TreeWillExpandListener tel)

Add or remove listeners interested in receiving "tree will expand" events. Note that no "TreeWillExpandEvent" class exists. The methods of the TreeWillExpandListener interface (discussed later in this chapter) use TreeExpansionEvent objects.

public void addTreeSelectionListener(TreeSelectionListener l)
public void removeTreeSelectionListener(TreeSelectionListener l)

Add or remove listeners interested in receiving tree selection events.

public void fireTreeCollapsed(TreePath collapsedPath)
public void fireTreeExpanded(TreePath expandedPath)

Notify any registered TreeExpansionListener objects that a path has collapsed or expanded. The collapsedPath and expandedPath arguments are used to construct a new TreeExpansionEvent with this JTree as the source.

public void fireTreeWillExpand(TreePath path) throws ExpandVetoException
public void fireTreeWillCollapse(TreePath path) throws ExpandVetoException

Notify registered listeners that a tree node is about to expand or collapse. The path argument constructs a new TreeExpansionEvent object sent to the listeners. The ExpandVetoException class is discussed later in Section 17.6 of this chapter.

protected void fireValueChanged(TreeSelectionEvent selectionEvent)

Notify registered TreeSelectionListener objects that a selection event has occurred. Whenever a listener registers with this JTree, an event redirector is set up to grab the selection events coming from the tree selection model and pass them to the listener with this JTree as the source. You do not need to worry about the selection model to attach a listener. You attach the listener to the JTree itself, and the redirector does the work.

JTree also generates property change events whenever any of its bound properties are modified.

17.3.3 Constants

The constants provided with the JTree class are used for reporting the names of bound properties in property change events and are listed in Table 17-5.

Table 17-5. JTree string constants for property change events

ANCHOR_SELECTION_PATH_PROPERTY1.3

ROOT_VISIBLE_PROPERTY

CELL_EDITOR_PROPERTY

ROW_HEIGHT_PROPERTY

CELL_RENDERER_PROPERTY

SCROLLS_ON_EXPAND_PROPERTY

EDITABLE_PROPERTY

SELECTION_MODEL_PROPERTY

EXPANDS_SELECTED_PATHS_PROPERTY1.3

SHOWS_ROOT_HANDLES_PROPERTY

INVOKES_STOP_CELL_EDITING_PROPERTY

TOGGLE_CLICK_COUNT_PROPERTY1.3

LARGE_MODEL_PROPERTY

TREE_MODEL_PROPERTY

LEAD_SELECTION_PATH_PROPERTY1.3

VISIBLE_ROW_COUNT_PROPERTY

1.3since 1.3

17.3.4 Constructors

public JTree( )

Create a tree using a DefaultTreeModel object as its base. You should probably use one of the other constructors to get an interesting tree. The default tree is populated with some meaningless sample content.

public JTree(TreeNode root)
public JTree(TreeNode root, boolean asksAllowsChildren)

Build new trees using the node root as the root of the tree. These constructors also use the DefaultTreeModel as their model.

public JTree(TreeModel model)

Build a tree using the model provided. The model argument contains the root of the tree.

public JTree(Object value[])
public JTree(Vector value)
public JTree(Hashtable value)

Build a DefaultTreeModel object and use the inner class JTree.DynamicUtilTreeNode to populate the tree using the value argument as children. If any element in value is itself an Object[], a Vector, or a Hashtable, a node is built for that element, and its contents become children of the node. This recursive process continues until all elements and their contents are explored.

The last constructor is great for simple data structures that you want to display as a tree. Figure 17-7 shows the tree that results when you display a hashtable.

Figure 17-7. JTrees built from a hashtable and a DefaultTreeModel in Mac and Metal L&Fs
figs/swng2.1707.gif

Even though this tree is larger than the tree in Figure 17-1, it takes less code to set it up:

// ObjectTree.java // import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.tree.*; import java.util.*; public class ObjectTree extends JFrame {   JTree tree;   String[][] sampleData = {     {"Amy"}, {"Brandon", "Bailey"},     {"Jodi"}, {"Trent", "Garrett", "Paige", "Dylan"},     {"Donn"}, {"Nancy", "Donald", "Phyllis", "John", "Pat"},      {"Ron"}, {"Linda", "Mark", "Lois", "Marvin"}   };   public ObjectTree( ) {     super("Hashtable Test");     setSize(400, 300);     setDefaultCloseOperation(EXIT_ON_CLOSE);   }   public void init( ) {     Hashtable h = new Hashtable( );     // Build up the hashtable using every other entry in the String[][] as a key,     // followed by a String[] "value."     for (int i = 0; i < sampleData.length; i+=2) {       h.put(sampleData[i][0], sampleData[i + 1]);     }     tree = new JTree(h);     getContentPane( ).add(tree, BorderLayout.CENTER);   }   public static void main(String args[]) {     ObjectTree tt = new ObjectTree( );     tt.init( );     tt.setVisible(true);   } }

17.3.5 Selection Methods

One of the primary functions the JTree class provides is programmer access to the selection status of the tree. (Most of these functions work with a selection model discussed in "Tree Selections.") We'll say more about this later, but selections may be based on either rows or paths. A row is a displayed element in a tree; you refer to a row by its index. A path is a list of nodes from the root to the selected node.

addSelectionInterval(int row1, int row2)

Add the paths between row1 and row2 to the current selection. It uses getPathBetweenRows( ) to collect the list of paths to add.

public void addSelectionPath(TreePath path)
public void addSelectionPaths(TreePath paths[])
public void addSelectionRow(int row)
public void addSelectionRows(int rows[])

Add to the current selection on the tree. If you supply an array of paths or integers, each of the paths or rows indicated is selected. If any path or row is not currently visible, it is made visible.

public void clearSelection( )

Clear the current selection completely.

public boolean isPathSelected(TreePath path)

Return true if the given path is in the current selection. It is the TreePath equivalent of the get method for the rowSelected property.

public void removeSelectionInterval(int row1, int row2)

Remove the paths between row1 and row2 from the current selection. It uses getPathBetweenRows( ) to collect the list of paths to deselect.

public void removeSelectionPath(TreePath path)
public void removeSelectionPaths(TreePath paths[])
public void removeSelectionRow(int row)
public void removeSelectionRows(int rows[])

Remove pieces of the current selection dictated by the rows or paths provided as arguments. If a specified path or row is not in the current selection, it is ignored, and any remaining rows or paths are deselected.

public void setSelectionInterval(int row1, int row2)

Set the current selection to represent the paths between row1 and row2. It uses getPathBetweenRows( ) to collect the list of paths to select.

public void setSelectionPath(TreePath path)
public void setSelectionPaths(TreePath paths[])
public void setSelectionRow(int row)
public void setSelectionRows(int rows[])

Set the current selection on the tree. If you supply an array of paths or integers, each of the indicated paths or rows is selected. If any path or row is not currently visible, it is made visible.

17.3.6 Expansion Methods

For any entry in your tree, you can check to see if it is currently expanded or collapsed. A node is considered expanded if the nodes in its path are also expanded. (This applies to leaves as well.) You can also programmatically control the collapsing and expanding of parts of your tree. All of the following methods accept either a TreePath or a row (int) argument.

public void collapsePath(TreePath path)
public void collapseRow(int row)

Collapse the given path or row if needed. (In the case of the path argument, the last component of the path is collapsed.) Once collapsed, it tries to make the path visible as well.

public void expandPath(TreePath path)
public void expandRow(int row)

Expand the given path or row if needed. Once expanded, it tries to make the path visible as well.

public boolean isCollapsed(int row)
public boolean isCollapsed(TreePath path)

Return true if any node in the given path or row is not currently expanded. If every node is expanded, these methods return false.

public boolean isExpanded(int row)
public boolean isExpanded(TreePath path)

Return true if the given path or row is currently fully expanded. If any nodes in the path are not expanded, these methods return false.

public boolean hasBeenExpanded(TreePath path)

Return true if the path has ever been expanded.

17.3.7 Path and Row Methods

public TreePath getClosestPathForLocation(int x, int y)
public int getClosestRowForLocation(int x, int y)

Return the path or row closest to a given location (x,y) in the component, relative to its upper-left corner. These methods return null only if nothing is visible. If you need to be sure that the point (x,y) is actually inside the bounds for the path or row returned, you need to check that yourself. The getPathForLocation( ) and getRowForLocation( ) methods do a basic check and return null if the point falls outside the closest row, if this is all you need.

public Rectangle getPathBounds(TreePath path)

Return the Rectangle object that encompasses the specified path, if that path is not currently visible. The scrollPathToVisible( ) method calls this to show a particular path on the screen. If the path is already visible, this method returns null.

public TreePath getPathForLocation(int x, int y)

A more restricted version of getClosestPathForLocation( ). If x or y ends up outside the bounds of the path returned by the closest path call, this method returns null.

public TreePath getPathForRow(int row)

Return the path associated with the specified row. If row is an invalid value (less than zero or greater than the number of rows in the current tree) or is not currently visible, this method returns null.

public Rectangle getRowBounds(int row)

This method functions like getPathBounds( ) for the given row.

public int getRowForLocation(int x, int y)

A more restricted version of getClosestRowForLocation( ). If x or y is outside the bounds of the row returned by the closest row call, this method returns -1.

public int getRowForPath(TreePath path)

Return the row number for the last component in path. If any part of path is not visible, or if path is null, this method returns -1.

public boolean isVisible(TreePath path)

Return true if the given path is currently visible. Recall that a visible path is any path you can see in the tree without expanding a parent node not necessarily one that is onscreen at the moment.

public void makeVisible(TreePath path)

Make path visible if it is not already visible.

public void scrollPathToVisible(TreePath path)
public void scrollRowToVisible(int row)

If the tree is in a scrollpane, scroll the given path or row to make it appear in the pane. A path is expanded up to its last component, if need be, to make it visible. (By definition, rows are always visible.) The tree must be in a scrollable environment (like a JScrollPane or JViewport) for this to work.

17.3.8 Editing Methods

public void cancelEditing( )

Cancel editing of a tree cell. If no cell is being edited, this method has no effect.

public TreeNode getEditingPath( )

Return the path to the element in the tree currently being edited. If the tree is not being edited, this method returns null.

public boolean isEditing( )

Return true if the current selection is being edited.

public boolean isPathEditable(TreePath path)

Return the value of the editable property for a given path. If it returns true, the path can be edited. The UI manager calls this method before editing a node so that a subclass of JTree can override it and say yes or no based on some appropriate criteria. (For example, you could allow editing of leaves but not editing of folders.)

public void startEditingAtPath(TreePath path)

Try to start editing the last element in path. This might fail if the cell editor will not edit that element.

public boolean stopEditing( )

Stop the tree from being edited. If the tree is not being edited, this method has no effect. It returns true if the tree was being edited and the cell editor stopped successfully. It returns false otherwise for example, if the tree was not being edited or the editor could not be stopped.

17.3.9 JTree Inner Classes

protected class JTree.AccessibleJTree

This class represents the accessible implementation for JTree.

public static class JTree.DynamicUtilTreeNode

Various constructors of the JTree class use this inner class to build tree nodes out of arrays, vectors, and hashtables.

protected static class JTree.EmptySelectionModel

As its name implies, this inner class provides an implementation of the TreeSelectionModel interface (by extending DefaultTreeSelectionModel) that does not allow any selections.

protected class JTree.TreeModelHandler

Manage the expandedState cache by listening to expansion and modification events coming from the model.

protected class JTree.TreeSelectionRedirector

This class contains methods for redirecting the source of events. Typically, this is done when the tree model generates an event, but the JTree object associated with that model needs to be listed as the source of the event.



Java Swing
Graphic Java 2: Mastering the Jfc, By Geary, 3Rd Edition, Volume 2: Swing
ISBN: 0130796670
EAN: 2147483647
Year: 2001
Pages: 289
Authors: David Geary

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net