一個簡單的CheckBox Tree實(shí)現(xiàn)
CheckBox Tree是一個十分常用的UI組件,它能使用戶方便地按特定規(guī)則勾選樹中的節(jié)點(diǎn)。本文實(shí)現(xiàn)了一種簡單的Checking規(guī)則:當(dāng)勾選了某節(jié)點(diǎn)后,該節(jié)點(diǎn)的所有下級節(jié)點(diǎn)全部被勾選;當(dāng)取消勾選某節(jié)點(diǎn)后,該節(jié)點(diǎn)的所有下級節(jié)點(diǎn)全部被取消勾選。(2009.08.05最后更新)實(shí)現(xiàn)CheckBox Tree的常用方法,就是使用JCheckBox作為JTree的TreeCellRendrer,并且需要實(shí)現(xiàn)特定的Checking規(guī)則來勾選/取消勾選CheckBox。
1. 樹節(jié)點(diǎn)
DefaultMutableTreeNode是最常用的TreeNode實(shí)現(xiàn),此處我們將擴(kuò)展這一實(shí)現(xiàn)--CheckBoxTreeNode,增加一個屬性isChecked,用于標(biāo)識該節(jié)點(diǎn)是否要被勾選上。該類的完整代碼如下所示:
public class CheckBoxTreeNode extends DefaultMutableTreeNode {
private static final long serialVersionUID = 3195314943599939279L;
private boolean isChecked = false;
public CheckBoxTreeNode(Object userObject) {
super(userObject);
}
public boolean isChecked() {
return isChecked;
}
public void setChecked(boolean isChecked) {
this.isChecked = isChecked;
}
}
private static final long serialVersionUID = 3195314943599939279L;
private boolean isChecked = false;
public CheckBoxTreeNode(Object userObject) {
super(userObject);
}
public boolean isChecked() {
return isChecked;
}
public void setChecked(boolean isChecked) {
this.isChecked = isChecked;
}
}
2. 渲染器
如本文開頭所述,我們將使用JCheckBox作為樹節(jié)點(diǎn)展現(xiàn)形式的渲染器,同時確定對節(jié)點(diǎn)進(jìn)行勾選或取消勾選的規(guī)則。CheckBoxTreeCellRenderer本身即是一個JCheckBox,那么在實(shí)現(xiàn)getTreeCellRendererComponent方法時,只簡單地返回它自己的實(shí)例即可,而對于勾選或取消勾選的條件,則由CheckBoxTreeNode中的isChecked屬性來確定,完整的代碼如下所示:
public class CheckBoxTreeCellRenderer extends JCheckBox implements TreeCellRenderer {
private static final long serialVersionUID = -6432020851855339311L;
public CheckBoxTreeCellRenderer() {
setOpaque(false);
}
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean selected, boolean expanded, boolean leaf, int row,
boolean hasFocus) {
CheckBoxTreeNode node = ((CheckBoxTreeNode) value); // 獲取樹節(jié)點(diǎn)對象。
setText(node.toString()); // 設(shè)置CheckBox所展示的文本。
// 當(dāng)樹節(jié)點(diǎn)被設(shè)置為勾選時,則該節(jié)點(diǎn)對應(yīng)的CheckBox被勾選上;否則,取消勾選。
if (node.isChecked()) {
setSelected(true);
setForeground(Color.BLUE);
} else {
setSelected(false);
setForeground(tree.getForeground());
}
return this;
}
}
private static final long serialVersionUID = -6432020851855339311L;
public CheckBoxTreeCellRenderer() {
setOpaque(false);
}
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean selected, boolean expanded, boolean leaf, int row,
boolean hasFocus) {
CheckBoxTreeNode node = ((CheckBoxTreeNode) value); // 獲取樹節(jié)點(diǎn)對象。
setText(node.toString()); // 設(shè)置CheckBox所展示的文本。
// 當(dāng)樹節(jié)點(diǎn)被設(shè)置為勾選時,則該節(jié)點(diǎn)對應(yīng)的CheckBox被勾選上;否則,取消勾選。
if (node.isChecked()) {
setSelected(true);
setForeground(Color.BLUE);
} else {
setSelected(false);
setForeground(tree.getForeground());
}
return this;
}
}
3. 樹
此處對JTree進(jìn)行擴(kuò)展,創(chuàng)建CheckBoxTree,該類只是為JTree添加了一個MouseListener,以偵聽鼠標(biāo)選中樹節(jié)點(diǎn)后,如何設(shè)置勾選標(biāo)記,并重繪樹。
public class CheckBoxTree extends JTree {
private static final long serialVersionUID = -217950037507321241L;
public CheckBoxTree(TreeModel newModel) {
super(newModel);
addCheckingListener();
}
private void addCheckingListener() {
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
int row = getRowForLocation(e.getX(), e.getY());
TreePath treePath = getPathForRow(row);
if (treePath == null) {
return;
}
CheckBoxTreeNode node = ((CheckBoxTreeNode) treePath.getLastPathComponent());
boolean checking = !node.isChecked(); // 如果該節(jié)點(diǎn)已被勾選上,則此次將取消勾選;反之,亦反。
checkNode(node, checking);
repaint(); // 重繪整棵樹。
}
// 遞歸地勾選或取消勾選指定節(jié)點(diǎn)及其所有下級節(jié)點(diǎn)的CheckBox。
private void checkNode(CheckBoxTreeNode node, boolean checking) {
node.setChecked(checking);
if (!node.isLeaf()) {
Enumeration<CheckBoxTreeNode> children = node.children();
while (children.hasMoreElements()) {
checkNode(children.nextElement(), checking);
}
}
}
});
}
}
上述程序有兩個關(guān)鍵點(diǎn):1. 設(shè)置當(dāng)前節(jié)點(diǎn)及其子節(jié)點(diǎn)的勾選標(biāo)記--checkNode;2. 重繪樹--repaint。調(diào)用repaint方法對樹進(jìn)行繪制時,方法TreeCellRenderer.getTreeCellRendererComponent就會被調(diào)用,此時程序就會根據(jù)checkNode方法設(shè)定的isChecked來勾選或取消勾選對應(yīng)的樹節(jié)點(diǎn)CheckBox。簡言之,就是先設(shè)置樹型數(shù)據(jù)中的勾選標(biāo)記,然后渲染器再根據(jù)這些標(biāo)記來渲染CheckBox。private static final long serialVersionUID = -217950037507321241L;
public CheckBoxTree(TreeModel newModel) {
super(newModel);
addCheckingListener();
}
private void addCheckingListener() {
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
int row = getRowForLocation(e.getX(), e.getY());
TreePath treePath = getPathForRow(row);
if (treePath == null) {
return;
}
CheckBoxTreeNode node = ((CheckBoxTreeNode) treePath.getLastPathComponent());
boolean checking = !node.isChecked(); // 如果該節(jié)點(diǎn)已被勾選上,則此次將取消勾選;反之,亦反。
checkNode(node, checking);
repaint(); // 重繪整棵樹。
}
// 遞歸地勾選或取消勾選指定節(jié)點(diǎn)及其所有下級節(jié)點(diǎn)的CheckBox。
private void checkNode(CheckBoxTreeNode node, boolean checking) {
node.setChecked(checking);
if (!node.isLeaf()) {
Enumeration<CheckBoxTreeNode> children = node.children();
while (children.hasMoreElements()) {
checkNode(children.nextElement(), checking);
}
}
}
});
}
}