JTree是Swing里的一种树形结构,感觉有些场合能起到意想不到的作用。
树结点新选中的事件
说是”新选中”而不是”选中”或”单击”,因为监听器实际上是在看选中者的某个值有没有改变,连续的选中不会触发这件事,单击也仅仅是”选中”的一条不必要的途径,这些都不会产生TreeSelectionEvent对象。
简述
选中事件的监听比较方便,不能对每个结点注册监听器,只能对整棵树注册一个实现了TreeSelectionListener接口的监听器,然后实现其valueChanged()方法,在方法中用JTree对象的getLastSelectedPathComponent()方法可以获取当前选择的第一个结点中的最后一个路径组件。
一般要考虑的仅仅是叶子结点,所以可以把这个组件强制转换成叶子结点的数据类型,如DefaultMutableTreeNode标准树结点,然后使用isLeaf()方法可以判定它是不是叶子结点。
在经过这样的筛选后,已经确定它是叶子结点了,还需要知道这个结点到底是哪个结点。如果结点之间的名称各不相同的话,可以使用其toString()方法去判断这个结点对应的字符串,这有些特别,因为之前学过的组件明明是使用事件对象的getActionCommand()方法然后用equals()作字符串的比较的。
打开DefaultMutableTreeNode源码看一下,其构造器中传入的实际上是一个Object对象:
/**
* Creates a tree node with no parent, no children, but which allows
* children, and initializes it with the specified user object.
*
* @param userObject an Object provided by the user that constitutes
* the node's data
*/
public DefaultMutableTreeNode(Object userObject) {
this(userObject, true);
}
然后看一下它的toString()方法,实际就是在做这个构造时传入的Object对象的toString(),当然对象不存在时候要返回一个空串:
/**
* Returns the result of sending <code>toString()</code> to this node's
* user object, or the empty string if the node has no user object.
*
* @see #getUserObject
*/
public String toString() {
if (userObject == null) {
return "";
} else {
return userObject.toString();
}
}
程序
import javax.swing.JFrame;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
//实现TreeSelectionListener接口以成为树的选中事件的监听器
class TestJTree extends JFrame implements TreeSelectionListener {
JTree jt;// 为了对valueChanged()方法可见,在这里声明
TestJTree() {
super("JTree的使用");
// DefaultMutableTreeNode是树结构中通用的结点
DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root Node");
DefaultMutableTreeNode nodeA = new DefaultMutableTreeNode("A Node");
// 用结点之间add的方法来实现嵌套(建立树结点的父子关系)
root.add(nodeA);
root.add(new DefaultMutableTreeNode("B Node"));// 可以用匿名对象
DefaultMutableTreeNode nodeC = new DefaultMutableTreeNode("C Node");// 也可以创建了再add
nodeA.add(nodeC);
nodeA.add(new DefaultMutableTreeNode("D Node"));
// 用实现了TreeModel接口的DefaultTreeModel类来指定树的根结点
DefaultTreeModel dtm = new DefaultTreeModel(root);
// 用这个根结点就可以去建立JTree树了
jt = new JTree(dtm);
jt.addTreeSelectionListener(this);// 为这棵树注册监听器
this.setBounds(700, 150, 250, 400);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.add(jt);
this.setVisible(true);
}
@Override
public void valueChanged(TreeSelectionEvent e) {
if (e.getSource() == jt) // 如果是要相应的那棵树
{
// 获取当前选择的第一个结点中的最后一个路径组件
DefaultMutableTreeNode dmt = (DefaultMutableTreeNode) jt.getLastSelectedPathComponent();
// 如果是叶子结点
if (dmt.isLeaf()) {
String str = dmt.toString();// 叶子结点的字符串
// 判断
if (str.equals("B Node")) {
System.out.println("B Node");
} else if (str.equals("C Node")) {
System.out.println("C Node");
} else if (str.equals("D Node")) {
System.out.println("D Node");
}
}
}
}
}
public class Main {
public static void main(String[] args) {
TestJTree tj = new TestJTree();
}
}
运行结果
(新选中叶结点时会打印相应的字符串)
C Node
D Node
B Node
D Node
树结点双击的事件
因为不能为树中的结点注册监听器,双击的事件也是为JTree对象注册的监听器。
简述
查了好久,发现JTree对象有一个非常实用的的方法getPathForLocation()能够根据指定的坐标点来获取点击的那个结点在树中的逻辑路径TreePath对象。有了这个方法就不需要去手工判断点击的位置处是哪个对象了。
获得了TreePath路径对象后,就可以使用它的getLastPathComponent()获取这棵树上这条路径上的最后一个组件了,还是强制转换成结点类型然后做操作就可以了。
实际使用时,双击事件也往往只需要去捕获叶子结点的,只要和前面一样做一个isLeaf()判断就可以了,我这里没写。
需要注意的是,对某些空白处双击时,还是属于在树上触发了双击事件,然而getPathForLocation()肯定获取不到路径了,返回的是一个null,在后面对这个对象的操作都必须包含在判断非null中才行,否则对null取成员方法显然是错误的。
程序
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
class TestJTree extends JFrame {
TestJTree() {
super("JTree的使用");
// DefaultMutableTreeNode是树结构中通用的结点
DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root Node");
DefaultMutableTreeNode nodeA = new DefaultMutableTreeNode("A Node");
// 用结点之间add的方法来实现嵌套(建立树结点的父子关系)
root.add(nodeA);
root.add(new DefaultMutableTreeNode("B Node"));// 可以用匿名对象
DefaultMutableTreeNode nodeC = new DefaultMutableTreeNode("C Node");// 也可以创建了再add
nodeA.add(nodeC);
nodeA.add(new DefaultMutableTreeNode("D Node"));
// 用实现了TreeModel接口的DefaultTreeModel类来指定树的根结点
DefaultTreeModel dtm = new DefaultTreeModel(root);
// 用这个根结点就可以去建立JTree树了
JTree jt = new JTree(dtm);
// 为这棵树注册监听器,用匿名的适配器覆写鼠标点击方法
jt.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
// 如果在这棵树上点击了2次,即双击
if (e.getSource() == jt && e.getClickCount() == 2) {
// 按照鼠标点击的坐标点获取路径
TreePath selPath = jt.getPathForLocation(e.getX(), e.getY());
if (selPath != null)// 谨防空指针异常!双击空白处是会这样
{
System.out.println(selPath);// 输出路径看一下
// 获取这个路径上的最后一个组件,也就是双击的地方
DefaultMutableTreeNode node = (DefaultMutableTreeNode) selPath.getLastPathComponent();
System.out.println(node.toString());// 输出这个组件toString()的字符串看一下
}
}
}
});
this.setBounds(700, 150, 250, 400);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.add(jt);
this.setVisible(true);
}
}
public class Main {
public static void main(String[] args) {
TestJTree tj = new TestJTree();
}
}
运行结果
[Root Node, A Node, C Node]
C Node
[Root Node]
Root Node
[Root Node]
Root Node
[Root Node, B Node]
B Node
[Root Node, A Node]
A Node
[Root Node, A Node, D Node]
D Node