12.10.5 扩展DefaultTreeCellRenderer改变节点外观

12.10.5 扩展DefaultTreeCellRenderer改变节点外观

DefaultTreeCellRenderer实现类实现了TreeCellRenderer接口,该接口里只有一个负责绘制JTree节点的
getTreeCellRendererComponent()方法:

方法 描述
Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) Sets the value of the current tree cell to value.

如果读者还记得前面介绍的绘制JList的列表项外观的内容,应该对该方法非常熟悉,与Listcellrenderer接口类似的是,getTreeCellRendererComponent()方法返回一个Component对象,该对象就是JTree的节点组件。
DefaultTreeCellRenderer类继承了JLabel,实现getTreeCellRendererComponent方法时返回this,即返回一个特殊的JLabel对象。如果需要根据节点内容来改变节点的外观,则可以再次扩展DefaultTreeCellRenderer类,并再次重写它提供的getTreeCellRendererComponent方法。

程序 数据库对象导航树

下面程序模拟了一个数据库对象导航树,程序可以根据节点的类型来绘制节点的图标。在本程序中为了给每个节点指定节点类型,程序不再使用String作为节点数据,而是使用NodeData来封装节点数据,并重写了NodeDatatoString方法。

使用Object类型的对象来创建TreeNode对象时,DefaultTreeCellRenderer默认使用该对象的toString方法返回的字符串作为该节点的标签。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import java.awt.*;
import javax.swing.*;
import javax.swing.tree.*;

public class ExtendsDefaultTreeCellRenderer {
JFrame jf = new JFrame("根据节点类型定义图标");
JTree tree;
// 定义几个初始节点
DefaultMutableTreeNode root = new DefaultMutableTreeNode(new NodeData(DBObjectType.ROOT, "数据库导航"));
DefaultMutableTreeNode salaryDb = new DefaultMutableTreeNode(new NodeData(DBObjectType.DATABASE, "公司工资数据库"));
DefaultMutableTreeNode customerDb = new DefaultMutableTreeNode(new NodeData(DBObjectType.DATABASE, "公司客户数据库"));
// 定义salaryDb的两个子节点
DefaultMutableTreeNode employee = new DefaultMutableTreeNode(new NodeData(DBObjectType.TABLE, "员工表"));
DefaultMutableTreeNode attend = new DefaultMutableTreeNode(new NodeData(DBObjectType.TABLE, "考勤表"));
// 定义customerDb的一个子节点
DefaultMutableTreeNode contact = new DefaultMutableTreeNode(new NodeData(DBObjectType.TABLE, "联系方式表"));
// 定义employee的三个子节点
DefaultMutableTreeNode id = new DefaultMutableTreeNode(new NodeData(DBObjectType.INDEX, "员工ID"));
DefaultMutableTreeNode name = new DefaultMutableTreeNode(new NodeData(DBObjectType.COLUMN, "姓名"));
DefaultMutableTreeNode gender = new DefaultMutableTreeNode(new NodeData(DBObjectType.COLUMN, "性别"));

public void init() {
// 通过add()方法建立树节点之间的父子关系
root.add(salaryDb);
root.add(customerDb);
salaryDb.add(employee);
salaryDb.add(attend);
customerDb.add(contact);
employee.add(id);
employee.add(name);
employee.add(gender);
// 以根节点创建树
tree = new JTree(root);
// 设置该JTree使用自定义的节点绘制器
tree.setCellRenderer(new MyRenderer());
// 设置是否显示根节点的“展开/折叠”图标,默认是false
tree.setShowsRootHandles(true);
// 设置节点是否可见,默认是true
tree.setRootVisible(true);
try {
// 设置使用Windows风格外观
UIManager.setLookAndFeel("com.sun.java.swing.plaf." + "windows.WindowsLookAndFeel");
} catch (Exception ex) {
}
// 更新JTree的UI外观
SwingUtilities.updateComponentTreeUI(tree);
jf.add(new JScrollPane(tree));
jf.pack();
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
}

public static void main(String[] args) {
new ExtendsDefaultTreeCellRenderer().init();
}
}

// 定义一个NodeData类,用于封装节点数据
class NodeData {
public int nodeType;
public String nodeData;

public NodeData(int nodeType, String nodeData) {
this.nodeType = nodeType;
this.nodeData = nodeData;
}

public String toString() {
return nodeData;
}
}

// 定义一个接口,该接口里包含数据库对象类型的常量
interface DBObjectType {
int ROOT = 0;
int DATABASE = 1;
int TABLE = 2;
int COLUMN = 3;
int INDEX = 4;
}

class MyRenderer extends DefaultTreeCellRenderer {
// 初始化5个图标
ImageIcon rootIcon = new ImageIcon("icon/root.gif");
ImageIcon databaseIcon = new ImageIcon("icon/database.gif");
ImageIcon tableIcon = new ImageIcon("icon/table.gif");
ImageIcon columnIcon = new ImageIcon("icon/column.gif");
ImageIcon indexIcon = new ImageIcon("icon/index.gif");

public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf,
int row, boolean hasFocus) {
// 执行父类默认的节点绘制操作
super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
NodeData data = (NodeData) node.getUserObject();
// 根据数据节点里的nodeType数据决定节点图标
ImageIcon icon = null;
switch (data.nodeType) {
case DBObjectType.ROOT:
icon = rootIcon;
break;
case DBObjectType.DATABASE:
icon = databaseIcon;
break;
case DBObjectType.TABLE:
icon = tableIcon;
break;
case DBObjectType.COLUMN:
icon = columnIcon;
break;
case DBObjectType.INDEX:
icon = indexIcon;
break;
}
// 改变图标
this.setIcon(icon);
return this;
}
}

上面的程序中码强制JTree使用自定义的节点绘制器:MyRenderer,该节点绘制器继承了DefaultTreeCellRenderer类,并重写了getTreeCellRendererComponent()方法。该节点绘制器重写该节点时根据节点的nodeType属性改变其图标。运行上面程序,会看到如图12.45所示的效果。

从图12.45中可以看出,JTree中表示节点展开、折叠的图标已经改为了“+”和“-”,这是因为本程序强制JTree使用了Windows风格。