本文介绍Java Swing中的JTree模型,介绍如何创建、修改、遍历,设置树。参考Core Java2相关章节及其源代码。
Swing树使用人们所熟悉的文件夹和树叶图来显示分层的数据。树由节点组成,节点可以是文件夹,也可以是树叶。文件夹可以有子节点,除根节点之外的所有节点都只有一个父节点。空的文件夹与树叶的不同之处就在于它允许有子节点。
除父节点和子节点外,树的节点还有一个用户对象(当使用DefaultTreeModel时就会呈现一个用户对象)。用户对象是Object类型,因此它提供了一个将任意对象与节点相关联的办法。
树有一个简单的模型,每一个JTree实例都要维护对绘制器和编辑器的引用,这个绘制器和编辑器被树中所有的节点所使用。表1中列出了swing.tree包中的主要类。
表1 Swing.tree包中的主要类
名 称 实 现
DefaultMutableTreeNode 一个具有一个父节点、(可能)许多子节点和 一个用户对象的可变节点,为相关联的节点提供了访问方法。如果没有任何子节点,这个节点就是树叶。
DefaultTreeModel 一个激发TreeModel Events事件的简单可变的模型。提供对子节点的访问方法,但不是提供对父节点的访问方法。
DefaultTreeCellEditor 绘制器和编辑器的包装器,它把一个“真正”的编辑器组件放在节点图标的旁边。
DefaultTreeCellRenderer 具有字体、颜色和图标访问方法的JLabel扩 展,它提供图标的缺省值。
TreePath 由一个节点到另一个节点的路径。路径中的节点存储在一个数组中。路径用于在选取内容之间进行通信。
1.构建树模型
详细代码见SimpleTree.java
DefaultMutableTreeNode root = new DefaultMutableTreeNode("World");
DefaultMutableTreeNode country = new DefaultMutableTreeNode("USA");
root.add(country);
。。。
JTree tree = new JTree(root);
Container contentPane = getContentPane();
contentPane.add(new JScrollPane(tree));
这些代码来自一个JFrame的构造子中。这里通过JTree来创建一个TreeModel的实例。
2.树结点
树节点由TreeNode接口定义,TreeNode接口被MutableTreeNode接口扩展,而MutableTreeNode接口又由 DefaultMutableTreeNode类来实现。
树结构详细类图
2.1 TreeNode接口
TreeNode接口定义了(固定)树节点的实质,表2总结了TreeNode相关方法。
表2TreeNode接口
public abstract Enumeration children()
public abstract TreeNode getParent()
public abstract TreeNode getChildAt(int)
public abstract int getChildCount()
public abstract int getIndex(TreeNode)
public abstract abstract boolean getAllowsChildren()
public abstract boolean isLeaf()
上面列出的前两组方法是对一个节点的父节点和子节点的访问方法。访问一个节点的子节点,可以通过枚举子节点的父节点来实现,也可以通过索引来访问子节点。另外,还定义了获取节点索引的方法和获取一个节点包含的子节点数目的方法。
上面列出的最后两个方法用来确定一个节点是文件夹,还是树叶。
开发人员很少直接实现TreeNode接口,这是因为Swing在DefaultMutableTreeNode类中提供了TreeNode接口的一个常用的缺省实现。数目众多的树节点扩展了DefaultMutableTreeNode。
2.2 MutableTreeNode接口
MutableTreeNode接口扩展TreeNode,它除了定义指定用户对象的方法外,还定义了修改一个节点的父节点和子节点的方法。表3总结了MutableNode接口。
表3MutableTreeNode接口
public abstract void insert(MutableTreeNode child,int index)
public abstract void remove(int index)
public abstract void remove(MutableTreeNode child)
public abstract void removeFromParent()
public abstract void setParent(MutableTreeNode)
public abstract void setUserObject(Object)
上面列出的第一组方法用来插入和删除子节点,子节点可以通过索引或引用来删除。removeFromParent方法用来将节点从父节点中删除,并更新父节点的子节点数目。
上面列出的最后两个方法用来设置一个节点的父节点和用户对象。需要注意的是,MutableTreeNode继承了getParent方法。
2.3 DefaultMutableTreeNode类
在实际应用中,很少直接实现MutableTreeNode接口,这是因为Swing以DefaultMutableTreeNode类的形式提供了一个合理又强大的MutableTreeNode接口的实现。
3.编辑树
详细代码见TreeEditTest.java。
JTree类使用对象的路径(树路径)来定位要处理的结点。一个树路径从根节点开始,由一个子结点序列构成。
3.1获得结点路径
TreePath类管理着一个Object引用序列。当拥有一个树路径时,通常只需要知道其终端结点,该结点可以通过getLastPathComponent方法得到。
TreePath selectionPath = tree.getSelectionPath();
DefaultMutableTreeNode selectedNode1 =
(DefaultMutableTreeNode) selectionPath .getLastPathComponent();
或者通过下面方法可立即返回给定的结点。
DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) tree
.getLastSelectedPathComponent();
3.2编辑结点
在获得选定的结点后,不能通过如下方法来进行编辑。
selectedNode.add(newNode);//error
因为这样只是改变树模型,而相关的视图却没有被通知到。可以使用DefaultTreeModel类的insertNodeInto方法。
DefaultMutableTreeNode parent = (DefaultMutableTreeNode) selectedNode
.getParent();
DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(
"New");
int selectedIndex = parent.getIndex(selectedNode);
model.insertNodeInto(newNode, parent, selectedIndex + 1);
3.3视图显示
当视图接收到结点结构被改变的通知时,它会更新显示树的视图,但是不会自动展开某个结点以展示新添加的子结点。
可以使用JTree类中的makeVisible方法来实现这个目的。makeVisible方法通过一个树路径让某个结点变成可视的。
TreeNode[] nodes = model.getPathToRoot(newNode);
TreePath path = new TreePath(nodes);
tree.makeVisible(path);
如果树是放在一个滚动面板里面,则调用。
TreeNode[] nodes = model.getPathToRoot(newNode);
TreePath path = new TreePath(nodes);
tree.scrollPathToVisible(path);
4.查找树
详细代码见ClassTree.java。
有时候需要遍历所有子结点来找到相匹配的结点。DefaultMutableTreeNode类提供广度优先和深度优先遍历方法,分别是breadthFirstEnumeration方法和depthFirstEnumeration方法。
public DefaultMutableTreeNode findUserObject(Object obj) {
// find the node containing a user object
Enumeration e = root.breadthFirstEnumeration();
while (e.hasMoreElements()) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) e
.nextElement();
if (node.getUserObject().equals(obj))
return node;
}
return null;
}
5.绘制结点
详细代码见ClassTree.java。
可以使用下面方式来制定树构件结点的外观。
使用DefaultTreeCellRenderer改变图标、字体以及背景颜色。这些设置针对所有结点。
tree = new JTree(model);
...
DefaultTreeCellRenderer renderer=new DefaultTreeCellRenderer();
renderer.setLeafIcon(new ImageIcon(“1.gif“))
renderer.setClosedIcon(new ImageIcon(“2.gif“));
renderer.setOpenIcon(new ImageIcon(“3.gif“));
tree.setCellRenderder(renderer);
可以通过实现TreeCellRenderer接口中的
Component getTreeCellRendererComponent(JTree tree, Object value,boolean selected, boolean expanded, boolean leaf, int row,boolean hasFocus)
方法,来针对特定的结点修改。该方法返回的是this,换句话说,就是一个标签。(DefaultTreeCellRenderer继承自JLabel类。)
下面的代码,通过反射API得到被处理的类,如果该类是静态类,则将显示斜体。
class ClassNameTreeCellRenderer extends DefaultTreeCellRenderer {
private Font plainFont = null;
private Font italicFont = null;
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row,
boolean hasFocus) {
super.getTreeCellRendererComponent(tree, value, selected, expanded,leaf, row, hasFocus);
// get the user object
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
Class c = (Class) node.getUserObject();
// the first time, derive italic font from plain font
if (plainFont == null) {
plainFont = getFont();
// the tree cell renderer is sometimes called with a label that has
// a null font
if (plainFont != null)
italicFont = plainFont.deriveFont(Font.ITALIC);
}
// set font to italic if the class is abstract, plain otherwise
if ((c.getModifiers() & Modifier.ABSTRACT) == 0)
setFont(plainFont);
else
setFont(italicFont);
return this;
}
本文出自 “子 孑” 博客[url]http://zhangjunhd.blog.51c...[/url]
Swing树使用人们所熟悉的文件夹和树叶图来显示分层的数据。树由节点组成,节点可以是文件夹,也可以是树叶。文件夹可以有子节点,除根节点之外的所有节点都只有一个父节点。空的文件夹与树叶的不同之处就在于它允许有子节点。
除父节点和子节点外,树的节点还有一个用户对象(当使用DefaultTreeModel时就会呈现一个用户对象)。用户对象是Object类型,因此它提供了一个将任意对象与节点相关联的办法。
树有一个简单的模型,每一个JTree实例都要维护对绘制器和编辑器的引用,这个绘制器和编辑器被树中所有的节点所使用。表1中列出了swing.tree包中的主要类。
表1 Swing.tree包中的主要类
名 称 实 现
DefaultMutableTreeNode 一个具有一个父节点、(可能)许多子节点和 一个用户对象的可变节点,为相关联的节点提供了访问方法。如果没有任何子节点,这个节点就是树叶。
DefaultTreeModel 一个激发TreeModel Events事件的简单可变的模型。提供对子节点的访问方法,但不是提供对父节点的访问方法。
DefaultTreeCellEditor 绘制器和编辑器的包装器,它把一个“真正”的编辑器组件放在节点图标的旁边。
DefaultTreeCellRenderer 具有字体、颜色和图标访问方法的JLabel扩 展,它提供图标的缺省值。
TreePath 由一个节点到另一个节点的路径。路径中的节点存储在一个数组中。路径用于在选取内容之间进行通信。
1.构建树模型
详细代码见SimpleTree.java
DefaultMutableTreeNode root = new DefaultMutableTreeNode("World");
DefaultMutableTreeNode country = new DefaultMutableTreeNode("USA");
root.add(country);
。。。
JTree tree = new JTree(root);
Container contentPane = getContentPane();
contentPane.add(new JScrollPane(tree));
这些代码来自一个JFrame的构造子中。这里通过JTree来创建一个TreeModel的实例。
2.树结点
树节点由TreeNode接口定义,TreeNode接口被MutableTreeNode接口扩展,而MutableTreeNode接口又由 DefaultMutableTreeNode类来实现。
树结构详细类图
2.1 TreeNode接口
TreeNode接口定义了(固定)树节点的实质,表2总结了TreeNode相关方法。
表2TreeNode接口
public abstract Enumeration children()
public abstract TreeNode getParent()
public abstract TreeNode getChildAt(int)
public abstract int getChildCount()
public abstract int getIndex(TreeNode)
public abstract abstract boolean getAllowsChildren()
public abstract boolean isLeaf()
上面列出的前两组方法是对一个节点的父节点和子节点的访问方法。访问一个节点的子节点,可以通过枚举子节点的父节点来实现,也可以通过索引来访问子节点。另外,还定义了获取节点索引的方法和获取一个节点包含的子节点数目的方法。
上面列出的最后两个方法用来确定一个节点是文件夹,还是树叶。
开发人员很少直接实现TreeNode接口,这是因为Swing在DefaultMutableTreeNode类中提供了TreeNode接口的一个常用的缺省实现。数目众多的树节点扩展了DefaultMutableTreeNode。
2.2 MutableTreeNode接口
MutableTreeNode接口扩展TreeNode,它除了定义指定用户对象的方法外,还定义了修改一个节点的父节点和子节点的方法。表3总结了MutableNode接口。
表3MutableTreeNode接口
public abstract void insert(MutableTreeNode child,int index)
public abstract void remove(int index)
public abstract void remove(MutableTreeNode child)
public abstract void removeFromParent()
public abstract void setParent(MutableTreeNode)
public abstract void setUserObject(Object)
上面列出的第一组方法用来插入和删除子节点,子节点可以通过索引或引用来删除。removeFromParent方法用来将节点从父节点中删除,并更新父节点的子节点数目。
上面列出的最后两个方法用来设置一个节点的父节点和用户对象。需要注意的是,MutableTreeNode继承了getParent方法。
2.3 DefaultMutableTreeNode类
在实际应用中,很少直接实现MutableTreeNode接口,这是因为Swing以DefaultMutableTreeNode类的形式提供了一个合理又强大的MutableTreeNode接口的实现。
3.编辑树
详细代码见TreeEditTest.java。
JTree类使用对象的路径(树路径)来定位要处理的结点。一个树路径从根节点开始,由一个子结点序列构成。
3.1获得结点路径
TreePath类管理着一个Object引用序列。当拥有一个树路径时,通常只需要知道其终端结点,该结点可以通过getLastPathComponent方法得到。
TreePath selectionPath = tree.getSelectionPath();
DefaultMutableTreeNode selectedNode1 =
(DefaultMutableTreeNode) selectionPath .getLastPathComponent();
或者通过下面方法可立即返回给定的结点。
DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) tree
.getLastSelectedPathComponent();
3.2编辑结点
在获得选定的结点后,不能通过如下方法来进行编辑。
selectedNode.add(newNode);//error
因为这样只是改变树模型,而相关的视图却没有被通知到。可以使用DefaultTreeModel类的insertNodeInto方法。
DefaultMutableTreeNode parent = (DefaultMutableTreeNode) selectedNode
.getParent();
DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(
"New");
int selectedIndex = parent.getIndex(selectedNode);
model.insertNodeInto(newNode, parent, selectedIndex + 1);
3.3视图显示
当视图接收到结点结构被改变的通知时,它会更新显示树的视图,但是不会自动展开某个结点以展示新添加的子结点。
可以使用JTree类中的makeVisible方法来实现这个目的。makeVisible方法通过一个树路径让某个结点变成可视的。
TreeNode[] nodes = model.getPathToRoot(newNode);
TreePath path = new TreePath(nodes);
tree.makeVisible(path);
如果树是放在一个滚动面板里面,则调用。
TreeNode[] nodes = model.getPathToRoot(newNode);
TreePath path = new TreePath(nodes);
tree.scrollPathToVisible(path);
4.查找树
详细代码见ClassTree.java。
有时候需要遍历所有子结点来找到相匹配的结点。DefaultMutableTreeNode类提供广度优先和深度优先遍历方法,分别是breadthFirstEnumeration方法和depthFirstEnumeration方法。
public DefaultMutableTreeNode findUserObject(Object obj) {
// find the node containing a user object
Enumeration e = root.breadthFirstEnumeration();
while (e.hasMoreElements()) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) e
.nextElement();
if (node.getUserObject().equals(obj))
return node;
}
return null;
}
5.绘制结点
详细代码见ClassTree.java。
可以使用下面方式来制定树构件结点的外观。
使用DefaultTreeCellRenderer改变图标、字体以及背景颜色。这些设置针对所有结点。
tree = new JTree(model);
...
DefaultTreeCellRenderer renderer=new DefaultTreeCellRenderer();
renderer.setLeafIcon(new ImageIcon(“1.gif“))
renderer.setClosedIcon(new ImageIcon(“2.gif“));
renderer.setOpenIcon(new ImageIcon(“3.gif“));
tree.setCellRenderder(renderer);
可以通过实现TreeCellRenderer接口中的
Component getTreeCellRendererComponent(JTree tree, Object value,boolean selected, boolean expanded, boolean leaf, int row,boolean hasFocus)
方法,来针对特定的结点修改。该方法返回的是this,换句话说,就是一个标签。(DefaultTreeCellRenderer继承自JLabel类。)
下面的代码,通过反射API得到被处理的类,如果该类是静态类,则将显示斜体。
class ClassNameTreeCellRenderer extends DefaultTreeCellRenderer {
private Font plainFont = null;
private Font italicFont = null;
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row,
boolean hasFocus) {
super.getTreeCellRendererComponent(tree, value, selected, expanded,leaf, row, hasFocus);
// get the user object
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
Class c = (Class) node.getUserObject();
// the first time, derive italic font from plain font
if (plainFont == null) {
plainFont = getFont();
// the tree cell renderer is sometimes called with a label that has
// a null font
if (plainFont != null)
italicFont = plainFont.deriveFont(Font.ITALIC);
}
// set font to italic if the class is abstract, plain otherwise
if ((c.getModifiers() & Modifier.ABSTRACT) == 0)
setFont(plainFont);
else
setFont(italicFont);
return this;
}
本文出自 “子 孑” 博客[url]http://zhangjunhd.blog.51c...[/url]