树是一种使用非常广泛的数据结构,计算机中的目录和文件就是以树结构形式来管理的。在Swing中,使用JTree来创建树结构,其主要构造方法如下:
- JTree():使用默认数据模型创建一棵树。
- JTree(Object[] value):使用指定数组中的每个元素元素作为子节点,创建一棵不显示根节点的树。
- JTree(TreeModel newModel):使用指定的数据模型创建一棵显示根节点的树。
- JTree(TreeNode root):创建一棵带有指定根节点的树。
与表格类似,树的设计也采用了MVC模式,其中,JTree对象并没有包含实际的数据,它只是提供了数据的一个视图,对应的数据存储在树的数据模型中。在Swing中,树的数据模型采用TreeModel接口实现类的对象来管理。在实际应用中,一般使用DefaultTreeModel对象来管理树的数据模型。DefaultTreeModel类是TreeModel接口的一个实现类,它提供了TreeModel接口的默认实现。
DefaultTreeModel类的构造方法如下:
- DefaultTreeModel(TreeNode root):创建一个指定根节点的数据模型。
- DefaultTreeModel(TreeNode root, boolean asksAllowsChildren):创建一个指定根节点的数据模型,如果asksAllowsChildren的取值为true,则节点的isLeaf()方法将被忽略,所有节点都被认为可以有子节点,如果其取值为false,则节点的isLeaf()方法将决定节点是否也可以有子节点。
树中的每一行包含一个数据项,称为树的节点(node),每棵树只有一个根节点(root node),其它所有节点都是根节点的子孙节点。一个节点可以拥有子节点也可以不拥有子节点,我们称那些可有拥有子节点(不管当前是否有子节点)的节点称为分支节点(branch node),而不能拥有子节点的节点称为叶子节点(leaf node)。在Swing中,树的节点采用TreeNode接口实现类的对象来管理。在实际应用中,一般使用DefaultMutableTreeNode类的对象来创建树的节点,该类是TreeNode接口的子接口MutableTreeNode的实现类。
DefaultMutableTreeNode类的构造方法如下:
- DefaultMutableTreeNode():创建一个没有父节点和子节点,但允许有子节点的树节点。
- DefaultMutableTreeNode(Object userObject):创建一个没有父节点和子节点,但允许有子节点的树节点,并使用指定的用户对象对其进行初始化。
- DefaultMutableTreeNode(Object userObject,boolean allowsChildren): 创建一个没有父节点和子节点的树节点,使用指定的用户对象对其进行初始化,可以设置是否允许有子节点。
下面的代码演示了树的创建一般过程,其所创建的树结构如图1所示。
//创建树的根节点
DefaultMutableTreeNode root=new DefaultMutableTreeNode("专业");
//创建树的节点
DefaultMutableTreeNode node1=new DefaultMutableTreeNode("理学");
DefaultMutableTreeNode node2=new DefaultMutableTreeNode("工学");
DefaultMutableTreeNode node3=new DefaultMutableTreeNode("管理学");
DefaultMutableTreeNode node21=new DefaultMutableTreeNode("电子信息类");
DefaultMutableTreeNode node22=new DefaultMutableTreeNode("计算机类");
//将node21和node22作为子节点添加到node2中
node2.add(node21);node2.add(node22);
//将node1、node2和node3作为子节点添加到根节点中
root.add(node1);root.add(node2);root.add(node3);
//创建树的数据模型
DefaultTreeModel treeModel=new DefaultTreeModel(root);
//创建树
JTree tree=new JTree(treeModel);
图1. 树结构
DefaultMutableTreeNode是构造树结构的核心类,其提供的常用方法如下:
- void add(MutableTreeNode newchild):从newchild的父节点(如果它具有父节点)中将其删除,并添加到此节点的子节点数组的末尾。
- void insert(MutableTreeNode newChild,int childIndex):从newChild的父节点中将其删除,并加入到此节点的子节点数组中的指定位置。
- void remove(MutableTreeNode aChild):从此节点的子节点数组中删除aChild,并将其父节点设置为null。
- void remove(int childIndex):从节点的子节点数组中移除指定索引处的子节点,并将其父节点设置为null。
- int getChildCount():返回此节点的子节点数目。
- TreeNode getChildAt(int index):返回此节点的子节点数组中指定索引处的子节点。
- int getIndex(TreeNode aChild):返回此节点的子节点数组中指定子节点的索引。
- int getDepth():返回以此节点为根的深度,即从此节点到叶子的最长距离。
- boolean isLeaf():如果此节点没有子节点,则返回true,否则,返回false。
- void setAllowsChildren():设置此节点是否允许拥有子节点。
- boolean isRoot():如果此节点是树的根节点,则返回true,否则,返回false。
- TreeNode getParent():返回此节点的父节点,如果没有父节点则返回null。
- Int getLevel():返回此节点的层级数,即从根节点到此节点的距离。
- TreeNode[] getPath():返回从根节点到此节点的路径。
- void setUserObject(Object userObject):设置此节点的用户对象。
- Object getUserObject():获取此节点的用户对象。
- Object[] getUserObjectPath():获取从根节点到此节点的用户对象序列。
JTree类提供了注册树选择事件监听器方法,语法格式如下:
public void addTreeSelectionListener(TreeSelectionListener tsl)
当用户点击树中某一节点时,将会触发TreeSelectionEvent事件,其所对应的事件监听器接口为TreeSelectionListener,该接口只包含一个方法void valueChanged(TreeSelectionEvent e),在该方法中,可以添加点击树节点所执行的业务逻辑代码。
在树的创建过程中,可以利用JTree类所提供的getLastSelectedPathComponent()方法可以获取用户当前所选择的节点,该方法的返回值为Object类型,可以将其强制转换为DefaultMutableTreeNode类型。
当树的结构发生变化时,可以利用DefaultTreeModel类所提供的reload()方法重新加载树结构的数据,使得树的显示与数据的变化保持同步。reload()方法有两种形式:
- void reload():重新加载整个树结构。
- void reload(TreeNode node):重新加载指定节点及其子节点的树结构。
在前面创建树的代码中,用户对象为字符串,而在实际应用中,一般需要自定义用户对象类。为了能够在树节点上显示用户对象类中相应属性的名称,则需要重写该类的toString()方法。例如,下面的代码定义了一个用户对象类Profession,并重写toString()方法,返回name属性的名称。
class Profession{
String code; //专业代码
String name; //专业名称
public Profession(String code,String name) {
this.code=code;
this.name=name;
}
//重写方法
public String toString() {
return name;
}
}
【例1】下面的程序演示了如何创建一棵树,主要功能包括增加、插入、修改和删除树节点,以及点击树节点显示其用户对象路径信息。
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
public class JTreeDemo extends JFrame{
JTree tree; //树
DefaultTreeModel treeModel;//树的数据模型
DefaultMutableTreeNode selectedNode;//当前被选择的树节点
JButton addButton=new JButton("增加");
JButton insertButton=new JButton("插入");
JButton updateButton=new JButton("修改");
JButton deleteButton=new JButton("删除");
JTextField textField=new JTextField();
public JTreeDemo() {
JFrame frame=this;
this.setTitle("树的示例");
this.setSize(400, 400);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
Container contentPane=this.getContentPane();
//创建树的根节点
DefaultMutableTreeNode root=new DefaultMutableTreeNode(
new Profession("00","专业"));
treeModel=new DefaultTreeModel(root);//创建树的数据模型
tree=new JTree(treeModel);//创建树
JPanel panel1=new JPanel();
panel1.setLayout(new FlowLayout(FlowLayout.LEFT));
panel1.add(addButton);
panel1.add(insertButton);
panel1.add(updateButton);
panel1.add(deleteButton);
contentPane.add(panel1,BorderLayout.NORTH);
contentPane.add(new JScrollPane(tree),BorderLayout.CENTER);
contentPane.add(textField,BorderLayout.SOUTH);
//注册树的选择事件监听器
tree.addTreeSelectionListener(new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent e) {
//获取当前被选择的节点
selectedNode=(DefaultMutableTreeNode)tree.getLastSelectedPathComponent();
if(selectedNode!=null) {
//获取当前被选择节点的用户对象路径
Object userObjects[]=selectedNode.getUserObjectPath();
String str=userObjects[0].toString();
for(int i=1;i<userObjects.length;i++) {
String str1="->"+userObjects[i].toString();
str+=str1;
}
textField.setText(str);
}
}
});
//增加按钮注册动作事件监听器
addButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//获取当前被选择的树节点
selectedNode=(DefaultMutableTreeNode)tree.getLastSelectedPathComponent();
if(selectedNode!=null) {
new TreeNodeDialog(frame,"增加节点",selectedNode);
}
}
});
//修改按钮注册动作事件监听器
updateButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
selectedNode=(DefaultMutableTreeNode)tree.getLastSelectedPathComponent();
if(selectedNode!=null) {
new TreeNodeDialog(frame,"修改节点",selectedNode);
}
}
});
//插入按钮注册动作事件监听器
insertButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
selectedNode=(DefaultMutableTreeNode)tree.getLastSelectedPathComponent();
if(selectedNode!=null) {
new TreeNodeDialog(frame,"插入节点",selectedNode);
}
}
});
//删除按钮注册动作事件监听器
deleteButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
selectedNode=(DefaultMutableTreeNode)tree.getLastSelectedPathComponent();
if(selectedNode==root) return;//不能删除根节点
if(selectedNode!=root && selectedNode!=null) {
int select=JOptionPane.showConfirmDialog(null,
"是否确定删除?", "删除节点",JOptionPane.YES_NO_OPTION);
if(select==JOptionPane.YES_OPTION) {
//获取当前被选择节点的父节点
DefaultMutableTreeNode parent=
(DefaultMutableTreeNode)selectedNode.getParent();
//将当前被选择节点从其父节点中删除
parent.remove(selectedNode);
treeModel.reload(parent);//重新加载父节点
}
}
}
});
this.setVisible(true);
}
//增加和修改树的节点对话框(内部类)
class TreeNodeDialog extends JDialog{
JTextField codeField=new JTextField(10);
JTextField nameField=new JTextField(10);
JButton saveButton=new JButton("保存");
Profession userObject;
DefaultMutableTreeNode parentNode;
public TreeNodeDialog(JFrame owner,String title,
DefaultMutableTreeNode selectedNode) {
super(owner,true);
this.setTitle(title);
setSize(300,150);
setLocationRelativeTo(null);
setResizable(false);
JPanel panel1=new JPanel();
panel1.setLayout(new GridLayout(2,1));
JPanel panel11=new JPanel();
JPanel panel12=new JPanel();
panel11.add(new JLabel("专业代码:"));panel11.add(codeField);
panel12.add(new JLabel("专业名称:"));panel12.add(nameField);
panel1.add(panel11);panel1.add(panel12);
JPanel panel2=new JPanel();
panel2.add(saveButton);
add(panel1,BorderLayout.CENTER);
add(panel2,BorderLayout.SOUTH);
switch(title) {
case "增加节点":
case "插入节点":
codeField.setText("");
nameField.setText("");
break;
case "修改节点":
userObject=(Profession)selectedNode.getUserObject();
codeField.setText(userObject.code);
nameField.setText(userObject.name);
break;
}
//保存按钮注册动作事件监听器
saveButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(codeField.getText()!="" && nameField.getText()!="") {
switch(title) {
case "增加节点":
//创建新的用户对象
userObject=new Profession(codeField.getText(),
nameField.getText());
//创建一个新的节点加入到当前被选择节点的子节点数组中
selectedNode.add(new DefaultMutableTreeNode(userObject));
break;
case "插入节点":
//创建新的用户对象
userObject=new Profession(codeField.getText(),
nameField.getText());
//获取当前被选择节点的父节点
parentNode=(DefaultMutableTreeNode)selectedNode.getParent();
//获取当前被选择节点在其父节点数组中的索引
int i=parentNode.getIndex(selectedNode);
//创建一个新的节点插入到当前被选择节点的后面
parentNode.insert(new
DefaultMutableTreeNode(userObject), i+1);
//重新加载parentNode节点
JTreeDemo.this.treeModel.reload(parentNode);
break;
case "修改节点":
//获取当前被选择节点的用户对象
userObject=(Profession)selectedNode.getUserObject();
userObject.code=codeField.getText();
userObject.name=nameField.getText();
break;
}
//更新加载selectedNode节点
JTreeDemo.this.treeModel.reload(selectedNode);
}
}
});
setVisible(true);
}
}
public static void main(String[] args) {
JTreeDemo frame=new JTreeDemo();
}
}
运行上述程序,执行结果如图2(a)所示,初始树中只有一个根节点。当用户选择某一个节点并单击增加按钮后,会弹出增加节点对话框,如图2(b)所示,当用户输入专业代码和专业名称并点击保存按钮后,会在当前被选择节点中增加一个新的子节点。当用户选择某一个节点并单击插入按钮后,会弹出插入插入节点对话框,如图2(c),当用户输入专业代码和专业名称并点击保存按钮后,会在当前被选择节点的后面增加一个新的兄弟子节点。当用户选择某一个节点并单击修改按钮后,会弹出修改节点对话框,如图2(d)所示,用户在该界面中可以修改当前被选择节点的信息。当用户选择某一个节点并单击删除按钮后,会弹出一个确认对话框,询问用户是否删除被选择的节点,如果用户点击“是(Y)”按钮,则会在树中删除以当前被选择节点为根的子树。当用户点击某一结点时,在窗口下方的文本框中会显示从根节点到该节点的名称路径信息。
(a) | (b) |
(c) | (d) |
图2. 树的使用示例