使用 JCheckBox 节点进行 JTree 渲染
Posted
技术标签:
【中文标题】使用 JCheckBox 节点进行 JTree 渲染【英文标题】:JTree rendering with JCheckBox nodes 【发布时间】:2011-01-04 17:47:43 【问题描述】:我正在尝试修改标准 Swing JTree 以混合使用和不使用复选框的节点。这是一个例子:
当我尝试选中/取消选中其中一个复选框(本示例中的“用户 01”节点)时,树会丢失节点:
我的代码是对这个例子的改编:http://forums.sun.com/thread.jspa?threadID=5321084&start=13。
而不是像这样在 DefaultMutableTreeNode 中嵌入 JCheckBox:
new DefaultMutableTreeNode(new CheckBoxNode("Accessibility", true));
我认为创建一个派生自 DefaultMutableTreeNode(我称之为 JTreeNode)的模型节点更有意义。此类自动将 DefaultMutableTreeNode 的 UserObject 设置为 JCheckBox。 TreeCellRenderer 使用类的 ShowCheckBox 属性来确定是使用 JCheckBox 还是 DefaultTreeCellRenderer。 JTreeNode 是这样使用的:
JTreeNode user01 = new JTreeNode("User 01");
user01.setShowCheckBox(true);
user01.setSelected(true);
我认为问题出在实现 TreeCellEditor 的类上,特别是在 getCellEditorValue() 或 getTreeCellEditorComponent() 方法中。我怀疑这个问题与 getCellEditorValue() 返回 DefaultMutableTreeNode 的派生而不是更简单的模型实例有关。
public Object getCellEditorValue()
JCheckBox checkBox = renderer.getCheckBoxRenderer();
JTreeNode node = new JTreeNode(checkBox.getText());
node.setShowCheckBox(true);
node.setSelected(checkBox.isSelected());
return node;
public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row)
Component editor = renderer.getTreeCellRendererComponent(tree, value, true, 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;
这里是 TreeCellRender 的 getTreeCellRendererComponent() 方法:
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus)
Component component;
//if object being passed for rendering is a JTreeNode that should show a JCheckBox, attempt to render it so
if (((JTreeNode) value).getShowCheckBox())
String stringValue = tree.convertValueToText(value, selected, expanded, leaf, row, false);
//set default JCheckBox rendering
checkBoxRenderer.setText(stringValue);
checkBoxRenderer.setSelected(false); //not checked
checkBoxRenderer.setEnabled(tree.isEnabled());
if (selected)
//removed colorization
//checkBoxRenderer.setForeground(selectionForeground);
//checkBoxRenderer.setBackground(selectionBackground);
else
checkBoxRenderer.setForeground(textForeground);
checkBoxRenderer.setBackground(textBackground);
//DefaultMutableTreeNode
if ((value != null) && (value instanceof JTreeNode))
//userObject should be a JTreeNode instance
//DefaultMutableTreeNode
//Object userObject = ((JTreeNode) value).getUserObject();
//if it is
//if (userObject instanceof JTreeNode)
//cast as a JTreeNode
//JTreeNode node = (JTreeNode) userObject;
JTreeNode node = (JTreeNode) value;
//set JCheckBox settings to match JTreeNode's settings
checkBoxRenderer.setText(node.getText());
checkBoxRenderer.setSelected(node.isSelected());
//
component = checkBoxRenderer;
//if not, render the default
else
component = defaultRenderer.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
return component;
非常感谢任何想法。
【问题讨论】:
这是基于截图的纯粹猜测,但可能是渲染问题。您可以在节点消失后尝试使用 SwingUtilities.updateComponentTreeUI。如果这有帮助,那么树只是没有正确重绘。 来吧伙计们,为什么没有人支持这个问题? 26 次浏览,你们中没有一个人认为这家伙投入了创造一个好问题的工作?嘘,你们都是骗子。 /咆哮 【参考方案1】:我能够解决问题。
我创建了一个模型类(TreeNodeModel)来保存相关的节点数据:key、value、hasCheckBox、isSelected:
public class TreeNodeModel
int key;
String value;
boolean isSelected=false;
boolean hasCheckBox=false;
public TreeNodeModel()
public TreeNodeModel(int key, String value)
this.key=key;
this.value = value;
public TreeNodeModel(int key, String value, boolean hasCheckBox)
this.key=key;
this.value = value;
this.hasCheckBox = hasCheckBox;
public TreeNodeModel(int key, String value, boolean hasCheckBox, boolean isSelected)
this.key=key;
this.value = value;
this.hasCheckBox=hasCheckBox;
this.isSelected = isSelected;
public boolean isSelected()
return this.isSelected;
public void setSelected(boolean newValue)
this.isSelected = newValue;
public boolean hasCheckBox()
return this.hasCheckBox;
public void setCheckBox(boolean newValue)
this.hasCheckBox=newValue;
public int getKey()
return this.key;
public void setKey(int newValue)
this.key = newValue;
public String getValue()
return this.value;
public void setValue(String newValue)
this.value = newValue;
@Override
public String toString()
return getClass().getName() + "[" + this.value + "/" + this.isSelected + "]";
// return this.text;
我创建了 TreeCellEditor 接口的实现:
public class TreeNodeEditor extends AbstractCellEditor implements TreeCellEditor
private JTree tree;
private TreeNodeModel editedModel = null;
TreeNodeRenderer renderer = new TreeNodeRenderer();
public TreeNodeEditor(JTree tree)
this.tree=tree;
@Override
public boolean isCellEditable(EventObject event)
boolean editable=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 editedNode = (DefaultMutableTreeNode) node;
TreeNodeModel model = (TreeNodeModel) editedNode.getUserObject();
editable = model.hasCheckBox;
//if (node)
//if (path)
//if (MouseEvent)
return editable;
public Object getCellEditorValue()
JCheckBox checkbox = renderer.getCheckBoxRenderer();
TreeNodeModel model = new TreeNodeModel(editedModel.getKey(), checkbox.getText(), true, checkbox.isSelected());
return model;
public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row)
if (((DefaultMutableTreeNode) value).getUserObject() instanceof TreeNodeModel)
editedModel = (TreeNodeModel) ((DefaultMutableTreeNode) value).getUserObject();
Component editor = renderer.getTreeCellRendererComponent(tree, value, true, 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;
关键是在 getTreeCellEditorComponent() 方法中捕获模型并在 getCellEditorValue() 方法中使用它的 Key。
构建树很容易:
DefaultMutableTreeNode server = new DefaultMutableTreeNode(new TreeNodeModel(0,"Server 01", false));
DefaultMutableTreeNode userFolder = new DefaultMutableTreeNode(new TreeNodeModel(1, "User Folders", false));
server.add(userFolder);
DefaultMutableTreeNode user01 = new DefaultMutableTreeNode(new TreeNodeModel(2, "User 01", true, true));
userFolder.add(user01);
DefaultMutableTreeNode clientA = new DefaultMutableTreeNode(new TreeNodeModel(3, "Client A", true, true));
user01.add(clientA);
DefaultMutableTreeNode clientB = new DefaultMutableTreeNode(new TreeNodeModel(4, "Client B", true, true));
user01.add(clientB);
DefaultMutableTreeNode publicFolder = new DefaultMutableTreeNode(new TreeNodeModel(5, "Public Folders", false));
server.add(publicFolder);
DefaultMutableTreeNode clientC = new DefaultMutableTreeNode(new TreeNodeModel(6, "Client C", true));
publicFolder.add(clientC);
Tree_Nodes.setCellRenderer(new TreeNodeRenderer());
Tree_Nodes.setCellEditor(new TreeNodeEditor(Tree_Nodes));
Tree_Nodes.setModel(new DefaultTreeModel(server);
最后,确定检查哪些节点是检查模型的节点集合(通过按钮)的问题:
private Map<Integer, String> checked = new HashMap<Integer, String>();
private void Button_CheckedActionPerformed(java.awt.event.ActionEvent evt)
DefaultTableModel tableModel = ((DefaultTableModel) Table_Nodes.getModel());
tableModel.getDataVector().removeAllElements();
tableModel.fireTableDataChanged();
checked.clear();
DefaultTreeModel treeModel = (DefaultTreeModel) Tree_Nodes.getModel();
DefaultMutableTreeNode root = (DefaultMutableTreeNode) treeModel.getRoot();
getChildNodes(root);
for (Iterator it=checked.entrySet().iterator(); it.hasNext(); )
Map.Entry entry = (Map.Entry)it.next();
tableModel.addRow(new Object[] entry.getKey(), entry.getValue());
Button_Checked.requestFocus();
private void getChildNodes(DefaultMutableTreeNode parentNode)
for (Enumeration e=parentNode.children(); e.hasMoreElements();)
DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) e.nextElement();
TreeNodeModel model = (TreeNodeModel) childNode.getUserObject();
if (model.hasCheckBox && model.isSelected())
checked.put(model.getKey(), model.getValue());
//recurse
if (childNode.getChildCount()>0) getChildNodes(childNode);
【讨论】:
+1 用于添加屏幕截图,因为我尽可能这样做(我想 +2 使用 OSX) 我想通过让 JCheckBox 向 JTree 中的适当机制发出信号来改进这一点,以便能够使用 JTree.getSelectionPaths() 枚举选定的节点。 你已经创建了 TreeNodeRenderer() 那么它不工作的那个类在哪里呢。【参考方案2】:以下是一些导致我出现渲染问题的“陷阱”:
TreeCellEditor 可能不共享传递给 JTree.setCellRenderer()
的 TreeCellRenderer 实例。如果TreeCellEditor.getTreeCellEditorComponent()
返回与树的TreeCellRenderer.getTreeCellRendererComponent()
相同的实例,则最终会出现渲染问题。您将尝试编辑一个单元格,但渲染器针对 5 个不相关的单元格运行。当编辑器尝试检索 JCheckBox 的状态时,它将从最后呈现的单元格(而不是最后编辑的单元格)中获取值,这显然是错误的。
使用TreeCellEditor.getCellEditorValue()
修改单元格的值,而不是直接在复选框上添加鼠标侦听器。仅当值被保存时才会调用此方法。如果您立即对鼠标事件采取行动,则该值不会在 CellEditor.cancelCellEditing()
上回滚。
【讨论】:
以上是关于使用 JCheckBox 节点进行 JTree 渲染的主要内容,如果未能解决你的问题,请参考以下文章