我想创建一个包含给定节点和子节点的树。所需的行为如下:
“当切换节点的复选框时,它的新值(选中/取消选中)应该反映到所有的后代”
以下是完整代码,它没有给出预期的行为:
public class CheckBoxTree {
Map<String, DefaultMutableTreeNode> nodes = new HashMap<String, DefaultMutableTreeNode>();
public static void main(final String args[]) {
final CheckBoxTree cbt = new CheckBoxTree();
DefaultMutableTreeNode root = cbt.addNodeWithoutCheckbox(null, "Select divisions");
DefaultMutableTreeNode nodeSTD1 = cbt.addNodeWithCheckbox(root, "STD1", false);
cbt.nodes.put("STD1", nodeSTD1);
DefaultMutableTreeNode nodeDiv1 = cbt.addNodeWithCheckbox(nodeSTD1, "DIV1", false);
cbt.nodes.put("DIV1", nodeDiv1);
DefaultMutableTreeNode nodeDiv2 = cbt.addNodeWithCheckbox(nodeSTD1, "DIV2", false);
cbt.nodes.put("DIV2", nodeDiv2);
root.add(nodeSTD1);
DefaultMutableTreeNode nodeSTD2 = cbt.addNodeWithCheckbox(root, "STD2", false);
cbt.nodes.put("STD2", nodeSTD2);
DefaultMutableTreeNode nodeDiv3 = cbt.addNodeWithCheckbox(nodeSTD2, "DIV3", false);
cbt.nodes.put("DIV3", nodeDiv3);
DefaultMutableTreeNode nodeDiv4 = cbt.addNodeWithCheckbox(nodeSTD2, "DIV4", false);
cbt.nodes.put("DIV4", nodeDiv4);
root.add(nodeSTD2);
final JTree tree = cbt.createCheckBoxTree(root);
// show the tree onscreen
final JFrame frame = new JFrame("CheckBox Tree");
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
panel.add(tree, BorderLayout.CENTER);
JButton button = new JButton("Submit");
panel.add(button, BorderLayout.SOUTH);
final JScrollPane scrollPane = new JScrollPane(panel);
frame.getContentPane().add(scrollPane, BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 150);
frame.setVisible(true);
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
/*TreePath[] selectionPaths = tree.getSelectionModel().getSelectionPaths();
// TreePath[] selectionPaths = tree.getSelectionPaths();
if (selectionPaths != null) {
System.out.println("Selected paths:");
for (TreePath tp : selectionPaths) {
System.out.println(tp);
}
}*/
System.out.println("Selected paths:");
for (DefaultMutableTreeNode n : cbt.nodes.values()) {
Object obj = n.getUserObject();
if (obj instanceof CheckBoxNode) {
if (((CheckBoxNode) obj).isSelected()) {
System.out.println(n.toString());
}
}
}
}
});
}
private JTree createCheckBoxTree(final DefaultMutableTreeNode root) {
final DefaultTreeModel treeModel = new DefaultTreeModel(root);
final JTree tree = new JTree(treeModel);
final CheckBoxNodeRenderer renderer = new CheckBoxNodeRenderer();
tree.setCellRenderer(renderer);
final CheckBoxNodeEditor editor = new CheckBoxNodeEditor(tree);
tree.setCellEditor(editor);
tree.setEditable(true);
// listen for changes in the model (including check box toggles)
treeModel.addTreeModelListener(new TreeModelListener() {
@Override
public void treeNodesChanged(final TreeModelEvent e) {
System.out.println(System.currentTimeMillis() + ": nodes changed");
DefaultMutableTreeNode node =
(DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
if (node == null) {
return;
}
changeSubTreeSelections(node, null);
}
@Override
public void treeNodesInserted(final TreeModelEvent e) {
System.out.println(System.currentTimeMillis() + ": nodes inserted");
}
@Override
public void treeNodesRemoved(final TreeModelEvent e) {
System.out.println(System.currentTimeMillis() + ": nodes removed");
}
@Override
public void treeStructureChanged(final TreeModelEvent e) {
System.out.println(System.currentTimeMillis() + ": structure changed");
}
});
// listen for changes in the selection
tree.addTreeSelectionListener(new TreeSelectionListener() {
@Override
public void valueChanged(TreeSelectionEvent arg0) {
System.out.println(System.currentTimeMillis() + ": value changed");
/*DefaultMutableTreeNode node =
(DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
if (node == null) {
return;
}
changeSubTreeSelections(node, null);*/
}
});
return tree;
}
protected void changeSubTreeSelections(DefaultMutableTreeNode node, Boolean selected) {
/*change all subtree selection if applicable*/
Object obj = node.getUserObject();
Boolean isSelected = selected;
if (obj instanceof CheckBoxNode) {
CheckBoxNode cbn = (CheckBoxNode) obj;
if (isSelected == null) {
isSelected = cbn.isSelected();
} else {
cbn.setSelected(isSelected);
}
} else {
return;
}
int count = node.getChildCount();
for (int i = 0; i < count; i++) {
DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt(i);
changeSubTreeSelections(child, isSelected);
}
}
public DefaultMutableTreeNode addNodeWithCheckbox(
final DefaultMutableTreeNode parent, final String text, final boolean checked) {
final CheckBoxNode data = new CheckBoxNode(text, checked);
final DefaultMutableTreeNode node = new DefaultMutableTreeNode(data);
if (parent != null) {
parent.add(node);
}
return node;
}
public DefaultMutableTreeNode addNodeWithoutCheckbox(
final DefaultMutableTreeNode parent, final String text) {
final DefaultMutableTreeNode node = new DefaultMutableTreeNode(text);
if (parent != null) {
parent.add(node);
}
return node;
}
/*private static DefaultMutableTreeNode add(
final DefaultMutableTreeNode parent, final String text, final boolean checked) {
final CheckBoxNode data = new CheckBoxNode(text, checked);
final DefaultMutableTreeNode node = new DefaultMutableTreeNode(data);
parent.add(node);
nodes.put(text, node);
return node;
}*/
}
class CheckBoxNodeRenderer implements TreeCellRenderer {
private JCheckBox checkBoxNodeRenderer = new JCheckBox();
private DefaultTreeCellRenderer nonCheckBoxNodeRenderer = new DefaultTreeCellRenderer();
private Color selectionBorderColor, selectionForegroundColor, selectionBackgroundColor,
textForegroundColor, textBackgroundColor;
protected JCheckBox getLeafCheckBox() {
return checkBoxNodeRenderer;
}
public CheckBoxNodeRenderer() {
Font fontValue;
fontValue = UIManager.getFont("Tree.font");
if (fontValue != null) {
checkBoxNodeRenderer.setFont(fontValue);
}
Boolean booleanValue = (Boolean) UIManager.get("Tree.drawsFocusBorderAroundIcon");
checkBoxNodeRenderer.setFocusPainted((booleanValue != null)
&& (booleanValue.booleanValue()));
selectionBorderColor = UIManager.getColor("Tree.selectionBorderColor");
selectionForegroundColor = UIManager.getColor("Tree.selectionForeground");
selectionBackgroundColor = UIManager.getColor("Tree.selectionBackground");
textForegroundColor = UIManager.getColor("Tree.textForeground");
textBackgroundColor = UIManager.getColor("Tree.textBackground");
}
@Override
public Component getTreeCellRendererComponent(
JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row,
boolean hasFocus) {
Component returnValue;
/*check if it is checkbox object*/
boolean isCheckboxObject = false;
if ((value != null) && (value instanceof DefaultMutableTreeNode)) {
Object userObject = ((DefaultMutableTreeNode) value).getUserObject();
if (userObject instanceof CheckBoxNode) {
isCheckboxObject = true;
CheckBoxNode node = (CheckBoxNode) userObject;
checkBoxNodeRenderer.setText(node.getText());
checkBoxNodeRenderer.setSelected(node.isSelected());
}
} else {
String stringValue =
tree.convertValueToText(value, selected, expanded, leaf, row, false);
checkBoxNodeRenderer.setText(stringValue);
checkBoxNodeRenderer.setSelected(false);
}
if (isCheckboxObject) {
checkBoxNodeRenderer.setEnabled(tree.isEnabled());
if (selected) {
checkBoxNodeRenderer.setForeground(selectionForegroundColor);
checkBoxNodeRenderer.setBackground(selectionBackgroundColor);
} else {
checkBoxNodeRenderer.setForeground(textForegroundColor);
checkBoxNodeRenderer.setBackground(textBackgroundColor);
}
returnValue = checkBoxNodeRenderer;
} else {
returnValue =
nonCheckBoxNodeRenderer.getTreeCellRendererComponent(tree, value, selected,
expanded, leaf, row, hasFocus);
}
return returnValue;
}
}
class CheckBoxNodeEditor extends AbstractCellEditor implements TreeCellEditor {
private static final long serialVersionUID = 1L;
private CheckBoxNodeRenderer renderer = new CheckBoxNodeRenderer();
private ChangeEvent changeEvent = null;
private JTree tree;
public CheckBoxNodeEditor(JTree tree) {
this.tree = tree;
}
@Override
public Object getCellEditorValue() {
JCheckBox checkbox = renderer.getLeafCheckBox();
CheckBoxNode checkBoxNode = new CheckBoxNode(checkbox.getText(), checkbox.isSelected());
return checkBoxNode;
}
@Override
public boolean isCellEditable(EventObject event) {
/*uncomment following code to make all nodes editable*/
// return true;
// make all nodes, which are instance of CheckBoxNode, editable
boolean returnValue = false;
if (event instanceof MouseEvent) {
MouseEvent mouseEvent = (MouseEvent) event;
TreePath path = tree.getPathForLocation(mouseEvent.getX(), mouseEvent.getY());
if (path != null) {
Object node = path.getLastPathComponent();
if ((node != null) && (node instanceof DefaultMutableTreeNode)) {
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) node;
Object userObject = treeNode.getUserObject();
returnValue = (userObject instanceof CheckBoxNode);
// returnValue = ((treeNode.isLeaf()) && (userObject
// instanceof CheckBoxNode));
}
}
}
return returnValue;
}
@Override
public Component getTreeCellEditorComponent(
JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row) {
Component editor =
renderer.getTreeCellRendererComponent(tree, value, false, expanded, leaf, row, true);
// editor always selected / focused
ItemListener itemListener = new ItemListener() {
public void itemStateChanged(ItemEvent itemEvent) {
if (stopCellEditing()) {
fireEditingStopped();
}
}
};
if (editor instanceof JCheckBox) {
((JCheckBox) editor).addItemListener(itemListener);
}
return editor;
}
}
class CheckBoxNode {
private String text;
private boolean selected;
public CheckBoxNode(String text, boolean selected) {
this.text = text;
this.selected = selected;
}
public boolean isSelected() {
return selected;
}
public void setSelected(boolean newValue) {
selected = newValue;
}
public String getText() {
return text;
}
public void setText(String newValue) {
text = newValue;
}
/*@Override
public String toString() {
return getClass().getName() + "[" + text + "/" + selected + "]";
}*/
@Override
public String toString() {
return text;
}
}
class NamedVector extends Vector<Object> {
private static final long serialVersionUID = 1L;
private String name;
public NamedVector(String name) {
this.name = name;
}
public NamedVector(String name, Object elements[]) {
this.name = name;
for (int i = 0, n = elements.length; i < n; i++) {
add(elements[i]);
}
}
@Override
public String toString() {
return "[" + name + "]";
}
}
切换任何父节点的复选框在子节点中无法正确反映。
请帮助我了解问题所在。
答案 0 :(得分:1)
添加treeDidChange
以重绘树。
@Override
public void treeNodesChanged(final TreeModelEvent e) {
System.out.println(System.currentTimeMillis() + ": nodes changed");
DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
if (node == null) {
return;
}
changeSubTreeSelections(node, null);
tree.treeDidChange();
}
更重要的是,我发现在函数changeSubTreeSelections
中,此行isSelected = !(cbn.isSelected());
不正确。您不应该使用!
来获得相反的值。
如果选择父节点,也许最好扩展整个后代的路径。