简体   繁体   English

JTree - TreeCellEditor 实现更改节点对象类

[英]JTree - TreeCellEditor Implemetation changing the Node Object Class

I'm trying to build a JTree with TestObjectCheckBoxNode leafs where each leaf holds a TestObject, everything appears to work except when I check the box in the leaf it is changing the node from a TestObjectCheckBoxNode to the CheckBoxNode superclass.我正在尝试使用 TestObjectCheckBoxNode 叶子构建一个 JTree,其中每个叶子都包含一个 TestObject,一切似乎都可以正常工作,除非我选中叶子中的框,它将节点从 TestObjectCheckBoxNode 更改为 CheckBoxNode 超类。 I know this is happening in the TreeCellEditor implementation, specifically CheckBoxNodeEditor:getCellEditorValue(), because it is creating the updated TreeCellRenderer from the UI of that node.我知道这发生在 TreeCellEditor 实现中,特别是 CheckBoxNodeEditor:getCellEditorValue(),因为它正在从该节点的 UI 创建更新的 TreeCellRenderer。

CheckBoxNode checkBoxNode = 
  new CheckBoxNode(checkBoxPanel.label.getText(),
                   checkBoxPanel.checkBox.isSelected());
return checkBoxNode;

I'm at a complete loss for how would I do this in a way where I have access to the TestObject for the selected node in CheckBoxNodeEditor, so I could do something like this:我完全不知道如何以可以访问 CheckBoxNodeEditor 中所选节点的 TestObject 的方式执行此操作,因此我可以执行以下操作:

TestObjectCheckBoxNode testObjectCheckBoxNode = 
  new TestObjectCheckBoxNode(testObject,
                             checkBoxPanel.checkBox.isSelected());
return testObjectCheckBoxNode;

Here is the complete code below:下面是完整的代码:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.awt.font.TextAttribute;
import java.util.Collections;
import java.util.Enumeration;
import java.util.EventObject;
import java.util.Vector;

import javax.swing.AbstractCellEditor;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeCellEditor;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;

public class JCheckBoxTreeTest 
{

  private TestObjectCheckBoxTree tree;

  public static void main(String... s)
  {
    new JCheckBoxTreeTest();
  }

  public JCheckBoxTreeTest() 
  {
    JFrame frame = new JFrame("JCheckBoxTreeTest Tree");


    Vector rootVector = new Category("Root", new Object[]
        {              
            new Category("POI", 
                new TestObjectCheckBoxNode[] {
                    new TestObjectCheckBoxNode(new TestObject("TestObject 1"),true),
                    new TestObjectCheckBoxNode(new TestObject("TestObject 2"),true),
            }),
        });




    tree = new TestObjectCheckBoxTree(rootVector);

    tree.addTreeSelectionListener(new TreeSelectionListener() 
    {
      public void valueChanged(TreeSelectionEvent e) 
      {
        DefaultMutableTreeNode node = (DefaultMutableTreeNode)tree.getLastSelectedPathComponent();

        if (node == null) return;

        Object userObject = ((DefaultMutableTreeNode) node).getUserObject();

        System.err.println("node: " + node.toString());
        System.err.println("userObject: " + userObject.toString());

      }
    });

    tree.expandAll();

    JScrollPane scrollPane = new JScrollPane(tree);
    frame.getContentPane().add(scrollPane, BorderLayout.NORTH);
    frame.setSize(360, 600);
    frame.setVisible(true);
  }



  class TestObject
  {
    String name;  
    public TestObject(String inStr)
    {
      name = inStr;
    }
  }

  public class CheckBoxNode 
  {
    String text;

    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; }

    public String toString() { return getClass().getName() + "[" + text + "/" + selected + "]"; }
  }

  public class TestObjectCheckBoxNode extends CheckBoxNode
  {
    TestObject testObject;

    public TestObjectCheckBoxNode(TestObject testObject, boolean selected) 
    {
      super(testObject.name, selected);
      this.testObject = testObject;
    }  
  }




  public class CheckBoxTree extends JTree
  {    
    public CheckBoxTree(Vector rootVector)
    {     
      super(rootVector);      

      CheckBoxNodeRenderer renderer = new CheckBoxNodeRenderer();
      setCellRenderer(renderer);
      setCellEditor(new CheckBoxNodeEditor());
      setEditable(true);
    }   

    public void expandAll()
    {      
      expandAll(this, new TreePath(((DefaultMutableTreeNode)this.treeModel.getRoot()).getPath()), true);
    }


    private void expandAll(JTree tree, TreePath path, boolean expand) {
      TreeNode node = (TreeNode) path.getLastPathComponent();

      if (node.getChildCount() >= 0) {
        Enumeration<? extends TreeNode> enumeration = node.children();
        while (enumeration.hasMoreElements()) {
          TreeNode treeNode = enumeration.nextElement();
          TreePath treePath = path.pathByAddingChild(treeNode);

          expandAll(tree, treePath, expand);
        }
      }

      if (expand) {
        tree.expandPath(path);
      } else {
        tree.collapsePath(path);
      }
    }
  }

  class CheckBoxPanel extends JPanel
  {
    public JCheckBox checkBox;
    public JLabel label;

    public CheckBoxPanel() 
    { 
      super();

      checkBox = new JCheckBox();
      label = new JLabel();

      checkBox.setBorder(new EmptyBorder(0, 0, 0, 0));

      add(checkBox);
      add(label);
    }
  }

  class CheckBoxNodeRenderer implements TreeCellRenderer 
  {
    private CheckBoxPanel leafRenderer = new CheckBoxPanel();

    private DefaultTreeCellRenderer nonLeafRenderer = new DefaultTreeCellRenderer();

    Color selectionBorderColor, selectionForeground, selectionBackground,
    textForeground, textBackground;

    protected CheckBoxPanel getLeafRenderer() {
      return leafRenderer;
    }

    public CheckBoxNodeRenderer() {
      Font fontValue;
      fontValue = UIManager.getFont("Tree.font");
      if (fontValue != null)
      {
        leafRenderer.checkBox.setFont(fontValue);
        leafRenderer.label.setFont(fontValue);

        //set the nonLeaf text to bold
        nonLeafRenderer.setFont(fontValue.deriveFont(Collections.singletonMap(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD)));
      }


      Boolean booleanValue = (Boolean) UIManager.get("Tree.drawsFocusBorderAroundIcon");
      leafRenderer.checkBox.setFocusPainted((booleanValue != null) && (booleanValue.booleanValue()));

      selectionBorderColor = UIManager.getColor("Tree.selectionBorderColor");
      selectionForeground = UIManager.getColor("Tree.selectionForeground");
      selectionBackground = UIManager.getColor("Tree.selectionBackground");
      textForeground = UIManager.getColor("Tree.textForeground");
      textBackground = UIManager.getColor("Tree.textBackground");
    }

    public Component getTreeCellRendererComponent(JTree tree, Object value,
        boolean selected, boolean expanded, boolean leaf, int row,
        boolean hasFocus) 
    {

      Component returnValue;
      if (leaf) 
      {

        String stringValue = tree.convertValueToText(value, selected,
            expanded, leaf, row, false);

        leafRenderer.checkBox.setSelected(false);
        leafRenderer.label.setText(stringValue);

        leafRenderer.setEnabled(tree.isEnabled());

        if (selected) {
          leafRenderer.setForeground(selectionForeground);
          leafRenderer.setBackground(selectionBackground);
        } else {
          leafRenderer.setForeground(textForeground);
          leafRenderer.setBackground(textBackground);
        }

        if ((value != null) && (value instanceof DefaultMutableTreeNode)) {
          Object userObject = ((DefaultMutableTreeNode) value)
              .getUserObject();
          if (userObject instanceof CheckBoxNode) {
            CheckBoxNode node = (CheckBoxNode) userObject;                                        
            leafRenderer.checkBox.setSelected(node.isSelected());
            leafRenderer.label.setText(node.getText());
          }
        }
        returnValue = leafRenderer;
      } 
      else 
      {
        returnValue = nonLeafRenderer.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
      }
      return returnValue;
    }
  }

  class CheckBoxNodeEditor extends AbstractCellEditor implements TreeCellEditor {

    CheckBoxNodeRenderer renderer = new CheckBoxNodeRenderer();
      TestObject testObject;
      
    ChangeEvent changeEvent = null;

    public Object getCellEditorValue() 
    {
      CheckBoxPanel checkBoxPanel = renderer.getLeafRenderer();
      
          if (testObject != null) 
          { 
            return new TestObjectCheckBoxNode(testObject, checkBoxPanel.checkBox.isSelected());
          }
          else 
          { 
            return new CheckBoxNode(checkBoxPanel.label.getText(), checkBoxPanel.checkBox.isSelected()); 
          }
    }

    public boolean isCellEditable(EventObject event) {
      boolean returnValue = false;

      if (event instanceof MouseEvent) {
        MouseEvent mouseEvent = (MouseEvent) event;

        JTree tree = (JTree)event.getSource();

        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 = ((treeNode.isLeaf()) && (userObject instanceof CheckBoxNode));
          }
        }
      }
      return returnValue;
    }

    public Component getTreeCellEditorComponent(JTree tree, Object value,
        boolean selected, boolean expanded, boolean leaf, int row) {
      
      Object userObject = ((DefaultMutableTreeNode)value).getUserObject();
                 
          if (userObject instanceof TestObjectCheckBoxNode) 
          {            
              testObject = ((TestObjectCheckBoxNode)userObject).testObject;             
          }
          else 
          {
              testObject = null;
          }
          
      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 CheckBoxPanel) 
      {
        ((CheckBoxPanel) editor).checkBox.addItemListener(itemListener);
      }

      return editor;
    }
  }



  class Category extends Vector 
  {
    String name;

    public Category(String name) 
    {
      this.name = name;
    }

    public Category(String name, Object elements[]) 
    {
      this.name = name;
      for (int i = 0, n = elements.length; i < n; i++) 
      {
        add(elements[i]);
      }
    }

    public String toString() 
    {
      return "[" + name + "]";
    }
  }



  class TestObjectCheckBoxTree extends CheckBoxTree
  {  
    public TestObjectCheckBoxTree(Vector rootVector)
    {     
      super(rootVector);
    }    
  }  
}

When editing the tree node Swing calls CheckBoxNodeEditor.getTreeCellEditorComponent() with the current node as the value parameter.在编辑树节点时,Swing 调用CheckBoxNodeEditor.getTreeCellEditorComponent()并将当前节点作为value参数。 Later when you stop editing the node it calls CheckBoxNodeEditor.getCellEditorValue() .稍后当您停止编辑节点时,它会调用CheckBoxNodeEditor.getCellEditorValue()

What you need to do: if the value in getTreeCellEditorComponent() is a TestObjectCheckBoxNode then store its testObject field into an instance field of the CheckBoxNodeEditor .您需要做什么:如果getTreeCellEditorComponent()中的valueTestObjectCheckBoxNode ,则将其testObject字段存储到CheckBoxNodeEditor的实例字段中。

Later when getCellEditorValue() is called you can either return a TestObjectCheckBoxNode if the testObject was stored, otherwise return a ChechBoxNode .稍后当getCellEditorValue()时,如果存储了testObject ,则可以返回TestObjectCheckBoxNode ,否则返回ChechBoxNode

The code could look like this (shortened) example:代码可能看起来像这样(缩短的)示例:

class CheckBoxNodeEditor extends AbstractCellEditor implements TreeCellEditor {
    CheckBoxNodeRenderer renderer = new CheckBoxNodeRenderer();
    TestObject testObject;

    public Object getCellEditorValue() {
        CheckBoxPanel checkBoxPanel = renderer.getLeafRenderer();
        if (testObject != null) {
            return new TestObjectCheckBoxNode(testObject, checkBoxPanel.checkBox.isSelected());
        } else {
            return new CheckBoxNode(checkBoxPanel.label.getText(), checkBoxPanel.checkBox.isSelected());
        }
    }

    public Component getTreeCellEditorComponent(
        JTree tree, Object value,
        boolean selected, boolean expanded, boolean leaf, int row
    ) {
        Object realValue = ((DefaultMutableTreeNode) value).getUserObject();
        if (realValue instanceof TestObjectCheckBoxNode n) {
            testObject = n.testObject;
        } else {
            testObject = null;
        }
        // remaining code from your getTreeCellEditorComponent method
    }
    // other code removed for brevity
}

If you can't use the new pattern matching instanceof operator (because you don't use Java 16 or later) the if statement at the beginning of getTreeCellEditorComponent would need to be written as如果您不能使用新的模式匹配 instanceof 运算符(因为您不使用 Java 16 或更高版本),则getTreeCellEditorComponent开头的if语句需要编写为

    public Component getTreeCellEditorComponent(
        JTree tree, Object value,
        boolean selected, boolean expanded, boolean leaf, int row
    ) {
        Object realValue = ((DefaultMutableTreeNode) value).getUserObject();
        if (realValue instanceof TestObjectCheckBoxNode) {
            TestObjectCheckBoxNode n = (TestObjectCheckBoxNode) value;
            testObject = n.testObject;
        } else {
            testObject = null;
        }
        // remaining code from your getTreeCellEditorComponent method
    }

Why is the value in getTreeCellEditorComponent a DefaultMutableTreeNode ?为什么getTreeCellEditorComponent中的valueDefaultMutableTreeNode

The explanation is that JTree exclusively works with TreeNode instances.解释是JTree专门用于TreeNode实例。 If you create a JTree with a Vector , a Hashtable or an Object[] it wraps the values that these contain in JTree.DynamicUtilTreeNode instances and sets your objects as userObject property on them.如果您使用VectorHashtableObject[]创建JTree ,它将包装这些包含在JTree.DynamicUtilTreeNode实例中的值,并将您的对象设置为它们的userObject属性。

The JTree.DynamicUtilTreeNode extends from DefaultMutableTreeNode . JTree.DynamicUtilTreeNodeDefaultMutableTreeNode扩展而来。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM