CheckBox Tree是一個十分常用的UI組件,它能使用戶方便地按特定規則勾選樹中的節點。本文實現了一種簡單的Checking規則:當勾選了某節點後,該節點的所有下級節點全部被勾選;當取消勾選某節點後,該節點的所有下級節點全部被取消勾選。(2009.08.05最後更新)
實現CheckBox Tree的常用方法,就是使用JCheckBox作為JTree的TreeCellRendrer,並且需要實現特定的Checking規則來勾選/取消勾選CheckBox。
1. 樹節點
DefaultMutableTreeNode是最常用的TreeNode實現,此處我們將擴展這一實現--CheckBoxTreeNode,增加一個屬性isChecked,用於標識該節點是否要被勾選上。該類的完整代碼如下所示:
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;
}
}
2. 渲染器
如本文開頭所述,我們將使用JCheckBox作為樹節點展現形式的渲染器,同時確定對節點進行勾選或取消勾選的規則。CheckBoxTreeCellRenderer本身即是一個JCheckBox,那麼在實現getTreeCellRendererComponent方法時,只簡單地返回它自己的實例即可,而對於勾選或取消勾選的條件,則由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); // 獲取樹節點對象。
setText(node.toString()); // 設置CheckBox所展示的文本。
// 當樹節點被設置為勾選時,則該節點對應的CheckBox被勾選上;否則,取消勾選。
if (node.isChecked()) {
setSelected(true);
setForeground(Color.BLUE);
} else {
setSelected(false);
setForeground(tree.getForeground());
}
return this;
}
}
3. 樹
此處對JTree進行擴展,創建CheckBoxTree,該類只是為JTree添加了一個MouseListener,以偵聽鼠標選中樹節點後,如何設置勾選標記,並重繪樹。
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(); // 如果該節點已被勾選上,則此次將取消勾選;反之,亦反。
checkNode(node, checking);
repaint(); // 重繪整棵樹。
}
// 遞歸地勾選或取消勾選指定節點及其所有下級節點的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);
}
}
}
});
}
}
上述程序有兩個關鍵點:
1. 設置當前節點及其子節點的勾選標記--checkNode;
2. 重繪樹--repaint。調用repaint方法對樹進行繪制時,方法TreeCellRenderer.getTreeCellRendererComponent就會被調用,此時程序就會根據checkNode方法設定的isChecked來勾選或取消勾選對應的樹節點CheckBox。簡言之,就是先設置樹型數據中的勾選標記,然後渲染器再根據這些標記來渲染CheckBox。