一、简单的树
先来看看什么是树:
就跟文件目录一样是一级一级的。下面是上图的代码:
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import java.awt.*;
public class jTree extends JFrame {
public jTree(){
Container c = getContentPane();
DefaultMutableTreeNode root = new DefaultMutableTreeNode("根节点");//创建一个具有指定标签的节点
DefaultMutableTreeNode fi = new DefaultMutableTreeNode("一级子节点A");
root.add(fi);//将子节点添加到根节点
DefaultMutableTreeNode two = new DefaultMutableTreeNode("二级子节点",false);//不允许有子节点
fi.add(two);//将二级子节点添加到一级子节点
root.add(new DefaultMutableTreeNode("一级子节点B"));
JTree jt = new JTree(root);//根据root根节点创建树
c.add(jt,BorderLayout.WEST);
DefaultTreeModel model = new DefaultTreeModel(root);//根据root根节点创建树模型并判断节点是否是叶子节点(默认方式判断)
JTree jt2 = new JTree(model);//根据树模型创建树
c.add(jt2,BorderLayout.CENTER);
DefaultTreeModel model2 = new DefaultTreeModel(root,true);//根据root根节点并判断节点是否为叶子节点(指定方式判断)
/**
* 默认判断是否是叶子节点:如果该节点没有子节点,就为叶子节点。
*指定方式方式判断是否是叶子节点:节点是否允许有子节点,如果不允许则为叶子节点,如果允许,就算没有子节点也不是叶子节点
*/
JTree jt3 = new JTree(model2);
c.add(jt3,BorderLayout.EAST);
setVisible(true);
setSize(300,500);
setDefaultCloseOperation(3);
}
public static void main(String[] args) {
new jTree();
}
}
二、处理选中节点事件
树的节点允许为被选中和取消选中状态,通过捕获树节点的选择事件,可以处理相应的操作。树的选择模式有3种,通过TreeSelectionModel类的对象可以设置树的选择模式。可以通过JTree类的getSelectionModel()方法获得TreeSelectionModel类的对象,然后通过TreeSelectionModel类的setSelection(int mode)方法设置选择模式。
SINGLE_TREE_SELECTION 1 只允许选中一个
CONTIGUOUS_TREE_SELECTION 2 允许连续选中多个
DISCONTIGUOUS_TREE_SELECTION 4 允许任意选中多个,为树的默认模式
当选中树节点和取消树节点的选中状态时,将发出TreeSelectionEvent事件,通过实现TreeSelectionListener接口可以捕获该事件。TreeSelectionListener接口的具体定义如下:
public interface TreeSelectionListener extends EventListener { void valueChanged(TreeSelectionEvent e); }
当捕获发出TreeSelectionEvent事件时,void valueChanged(TreeSelectionEvent e)方法将被触发执行,此时通过JTree类的getSelectionPaths()方法可以获得所有被选中节点的路径,该方法将返回一个TreePath类型的数组;通过getSelectionPath()将获得选中节点中索引值最小的节点的路径(TreePath类的对象),也可以理解为选中节点中离根节点最近的节点的路径。在获得选中节点的路径之前,可以通过JTree类的isSelectionEmpty()方法查看是否存在被选中的节点,如果返回false,表示存在,通过getSelectionCount()方法可以获得被选中节点的数量。
TreePath类表示树节点的路径,即通过该类可以获得子节点所属的父节点,以及父节点所属的上级节点,直到数的根节点。TreePath类的常用方法如下:
下面是示例代码:
方法 说明 getPath() 以Object数组的形式返回该路径中所有节点的对象,在数组中的顺序按照从根节点到子节点的顺序 getLastPathComponent() 获得路径中最后一个节点的对象 getParentPath() 获得路径中除了最后一个节点的路径 pathByAddingChild(Object child) 获得向路径中添加指定节点后的路径 getPathCount() 获得路径中包含节点的数量 getPathComponent(int element) 获得路径中指定索引位置的节点对象
import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import java.awt.*;
public class jTree3 extends JFrame {
public jTree3(){
Container c = getContentPane();
setTitle("处理选中节点事件");
//创建根节点
DefaultMutableTreeNode root = new DefaultMutableTreeNode("本地磁盘(D:)");
//添加子节点
DefaultMutableTreeNode tu = new DefaultMutableTreeNode("图片");
root.add(tu);
root.add(new DefaultMutableTreeNode("音乐"));
tu.add(new DefaultMutableTreeNode("千山"));
DefaultMutableTreeNode shan = new DefaultMutableTreeNode("凤凰山");
tu.add(shan);
shan.add(new DefaultMutableTreeNode("图1", false));
shan.add(new DefaultMutableTreeNode("图2", false));
//根据树模型创建树
DefaultTreeModel model = new DefaultTreeModel(root,true);
JTree tree = new JTree(model);
c.add(tree);
//获得树的选择模式
TreeSelectionModel treeSelectionModel = tree.getSelectionModel();
//设置树的选择模式为连选
treeSelectionModel.setSelectionMode(TreeSelectionModel.CONTIGUOUS_TREE_SELECTION);
//给树添加监听事件
tree.addTreeSelectionListener(new TreeSelectionListener() {
@Override
public void valueChanged(TreeSelectionEvent e) {
//判断是否存在选中节点
if (!tree.isSelectionEmpty()){
//获得所有被选中节点的路径
TreePath[] paths = tree.getSelectionPaths();
for (int i = 0; i < paths.length; i++) {
//获得选中节点的路径
TreePath path = paths[i];
//返回该路径中的所有节点的对象
Object[] oPath = path.getPath();
for (int j = 0; j < oPath.length; j++) {
//获得节点
DefaultMutableTreeNode node = (DefaultMutableTreeNode) oPath[j];
String s = node.getUserObject()+(j==(oPath.length-1)?"":"-->");
System.out.print(s);//输出节点标签
}
System.out.println();
}
System.out.println();
}
}
});
setVisible(true);
setSize(500,500);
setDefaultCloseOperation(3);
}
public static void main(String[] args) {
new jTree3();
}
}
三、遍历树节点
有时候需要对树进行遍历,也就是遍历树中的部分或全部节点,以便查找某一节点,或者是对树中的结点执行某一操作。DefaultMutableTreeNode类提供了两组相对的遍历方式:前序遍历和后序遍历,广度优先遍历和深度优先遍历。
通过DefaultMutableTreeNode类的childrem()方法,可以得到仅包含该节点子节点的枚举对象,以便快速遍历节点的子节点。在DefaultMutableTreeNode类中还提供了一些常用方法,如下
getLevel()//获得该节点相对于根节点的级别值,如根节点的子节点的级别值为1
getDepth()//获得以此节点为根节点的树的深度,如果该节点没有子节点,则深度为0
getParent()//获得该节点的父节点对象
getChildCount()//获得该节点拥有字节点的个数
getFirstChild()//获得该节点的第一个子节点对象
getSiblingCount()//获得该节点拥有的兄弟节点的个数
getNextSibling()//获得该节点的后一个兄弟节点
getPreviousSibling()//获得该节点的前一个兄弟节点
getPath()//获得该节点的路径
isRoot()//判断该节点是否为根节点
isLeaf()//判断该节点是否为叶子节点
下面是通过按钮控选择遍历方式的示例代码:
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Enumeration;
public class jTree2 extends JFrame {
private static final long serialVersionUID = 1L;
private DefaultMutableTreeNode root;
public static void main(String args[]) {
jTree2 frame = new jTree2();
frame.setVisible(true);
}
public jTree2() {
super();
setTitle("遍历树节点");
setBounds(100, 100, 290, 260);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//创建根节点
root = new DefaultMutableTreeNode("ROOT");
//创建一级节点
DefaultMutableTreeNode A = new DefaultMutableTreeNode("A");
//添加二级节点
A.add(new DefaultMutableTreeNode("AA"));
A.add(new DefaultMutableTreeNode("AB"));
root.add(A);
DefaultMutableTreeNode B = new DefaultMutableTreeNode("B");
root.add(B);
DefaultMutableTreeNode C = new DefaultMutableTreeNode("C");
C.add(new DefaultMutableTreeNode("CA"));
DefaultMutableTreeNode CB = new DefaultMutableTreeNode("CB");
CB.add(new DefaultMutableTreeNode("CBA"));
CB.add(new DefaultMutableTreeNode("CBB"));
C.add(CB);
C.add(new DefaultMutableTreeNode("CC"));
root.add(C);
//创建树
JTree tree = new JTree(root);
getContentPane().add(tree, BorderLayout.CENTER);
final JPanel panel = new JPanel();
panel.setLayout(new GridLayout(0, 1));
getContentPane().add(panel, BorderLayout.EAST);
final JButton b1 = new JButton("按前序遍历节点");
b1.addActionListener(new ButtonActionListener("按前序遍历"));
panel.add(b1);
final JButton b2 = new JButton("按后序遍历节点");
b2.addActionListener(new ButtonActionListener("按后序遍历"));
panel.add(b2);
final JButton b3 = new JButton("以广度优先遍历");
b3.addActionListener(new ButtonActionListener("以广度优先遍历"));
panel.add(b3);
final JButton b4 = new JButton("以深度优先遍历");
b4.addActionListener(new ButtonActionListener("以深度优先遍历"));
panel.add(b4);
final JButton b5 = new JButton("遍历直属子节点");
b5.addActionListener(new ButtonActionListener("遍历子节点"));
panel.add(b5);
}
private class ButtonActionListener implements ActionListener {
private String mode;
public ButtonActionListener(String mode) {
this.mode = mode;
}
public void actionPerformed(ActionEvent e) {
Enumeration<?> enumeration;// 声明节点枚举对象
System.out.println(mode);
if (mode.equals("按前序遍历"))
// 按前序遍历所有树节点
enumeration = root.preorderEnumeration();
else if (mode.equals("按后序遍历"))
// 按后序遍历所有树节点
enumeration = root.postorderEnumeration();
else if (mode.equals("以广度优先遍历"))
// 以广度优先遍历所有树节点
enumeration = root.breadthFirstEnumeration();
else if (mode.equals("以深度优先遍历"))
// 以深度优先遍历所有树节点
enumeration = root.depthFirstEnumeration();
else
enumeration = root.children(); // 遍历该节点的子节点
while (enumeration.hasMoreElements()) {// 遍历节点枚举对象
DefaultMutableTreeNode node;// 获得节点
node = (DefaultMutableTreeNode) enumeration.nextElement();
// 根据节点级别输出占位符
for (int l = 0; l < node.getLevel(); l++) {
System.out.print("----");
}
System.out.println(node.getUserObject());// 输出节点标签
}
System.out.println();
}
}
}
这样看遍历顺序还是很直观的
四、定制树
在使用树时,经常需要对树进行一系列的设置,例如,对节点图标的设置,对节点间连线的设置,以及对树展开情况的设置等,以便实现实际需要的视觉效果。(借用一下最开始的树节点代码)
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.util.Enumeration;
public class jTree extends JFrame {
public static void main(String[] args) {
jTree jTree = new jTree();
jTree.setVisible(true);
}
public jTree(){
super();
setTitle("定制树");
setBounds(100, 100, 320, 260);
DefaultMutableTreeNode root = new DefaultMutableTreeNode("根节点");//创建一个具有指定标签的节点
DefaultMutableTreeNode fi = new DefaultMutableTreeNode("一级子节点A");
root.add(fi);//将子节点添加到根节点
DefaultMutableTreeNode two = new DefaultMutableTreeNode("二级子节点",false);//不允许有子节点
fi.add(two);//将二级子节点添加到一级子节点
root.add(new DefaultMutableTreeNode("一级子节点B"));
JTree jt = new JTree(root);//根据root根节点创建树
jt.setRootVisible(false);//不显示树的根节点
jt.setRowHeight(50);//设置树节点的行高为50
jt.setFont(new Font("宋体", Font.BOLD, 14));//设置树节点的字体
getContentPane().add(jt,BorderLayout.WEST);
DefaultTreeModel model = new DefaultTreeModel(root);//根据root根节点创建树模型并判断节点是否是叶子节点(默认方式判断)
JTree jt2 = new JTree(model);//根据树模型创建树
jt2.putClientProperty("JTree.lineStyle","None");//设置节点之间没有连线
getContentPane().add(jt2,BorderLayout.CENTER);
DefaultTreeModel model2 = new DefaultTreeModel(root,true);//根据root根节点并判断节点是否为叶子节点(指定方式判断)
/**
* 默认判断是否是叶子节点:如果该节点没有子节点,就为叶子节点。
*指定方式方式判断是否是叶子节点:节点是否允许有子节点,如果不允许则为叶子节点,如果允许,就算没有子节点也不是叶子节点
*/
JTree jt3 = new JTree(model2);
DefaultTreeCellRenderer treeCellRenderer;//获得树节点的绘制对象
treeCellRenderer = (DefaultTreeCellRenderer) jt3.getCellRenderer();
treeCellRenderer.setLeafIcon(null);//设置叶子节点不使用图标
treeCellRenderer.setClosedIcon(null);//设置节点折叠时不采用图标
treeCellRenderer.setOpenIcon(null);//设置节点展开时不采用图标
//运行时所有节点展开功能的实现代码
Enumeration<?> enumeration;
enumeration = root.preorderEnumeration();//按前序遍历所有树节点
while (enumeration.hasMoreElements()){
DefaultMutableTreeNode node;
node = (DefaultMutableTreeNode) enumeration.nextElement();
if (!node.isLeaf()){//判断是否为叶子节点
//创建该节点的路径
TreePath path = new TreePath(node.getPath());
jt3.expandPath(path);//展开路径
}
}
getContentPane().add(jt3,BorderLayout.EAST);
setDefaultCloseOperation(3);
}
}