【问题标题】:CheckBoxNodeTree example - how to correctly implement itemlistener in CheckBoxNodeEditor?CheckBoxNodeTree 示例 - 如何在 CheckBoxNodeEditor 中正确实现 itemlistener?
【发布时间】:2013-10-18 00:17:49
【问题描述】:

我正在使用这个实现来创建我的 JTree: https://github.com/imagej/imagej/blob/ee3c360949/ui/swing/util/src/test/java/imagej/util/swing/tree/CheckBoxTreeSample.java

我试图寻找在 CheckBoxNodeEditor 中创建 ItemListener 的正确方法,并自己尝试了一些东西,但到目前为止还没有工作。我已经为 JTree 和 TreeSelectionListener 实现了一个 MouseListener,那么我需要这个 itemlistener 吗?现在我的应用程序在没有 itemlistener 的情况下工作,但我担心我以后需要它来做一些事情。

Kleopatra 已经在这篇文章中声明了原始代码的一些错误。 Kleopatra 还说 actionListener 可以解决问题,所以也许我应该检查一下? : Multiple ITEM_STATE_CHANGED events from jcheckbox in jtree

hardwired 在此论坛中的帖子有助于不向编辑添加多个听众:http://www.java-forums.org/awt-swing/10389-jcheckboxes-jtree-nodes.html

因此,通过硬连线的方法,我可以决定我只想向面板添加一个侦听器,但由于每次单击 JTree 时都会创建一个新侦听器,它仍然会触发很多事件?

这里是 CheckBoxNodeEditor 的原始代码(减去一个节省空间的方法和涉及的其他类)。如果您同意,我将从该方法的侦听器中移除并使用我创建的两个侦听器(TreeSelection 和 MouseListener):

class CheckBoxNodeEditor extends AbstractCellEditor implements TreeCellEditor {

  CheckBoxNodeRenderer renderer = new CheckBoxNodeRenderer();
  public static int clickCount = 0;

  ChangeEvent changeEvent = null;

  JTree tree;

  public CheckBoxNodeEditor(JTree tree) {
    this.tree = tree;
  }

  public Object getCellEditorValue() {
    ...
  }

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

      System.out.println("getTreeCellEditorComponent method called!");

      System.out.println(String.valueOf(clickCount++));

    Component editor = renderer.getTreeCellRendererComponent(tree, value,
        true, expanded, leaf, row, true);

    // editor always selected / focused
    ItemListener itemListener = new ItemListener() {
      public void itemStateChanged(ItemEvent itemEvent) {

我实际上删除了接下来的三行:

JCheckBox item = (JCheckBox)itemEvent.getItem();
System.out.println(itemEvent.paramString());
System.out.println("item "+item.getText()+" state Changed!");
          if (stopCellEditing()) {
              fireEditingStopped();
          }
      }
    };
    if (editor instanceof JCheckBox) {
      ((JCheckBox) editor).addItemListener(itemListener);
    }

    return editor;
  }
}

好的,我看到 ItemListener 控制复选框。所以每个复选框实际上都被分配了它自己的监听器。如何为所有复选框/节点添加一个?

【问题讨论】:

    标签: java swing jtree jcheckbox


    【解决方案1】:
    • 将所有内容放在一起,toString 用于覆盖 DefaultXxxXxx 中用于非向量数组类型的一些方法,可以删除

    import java.awt.Color;
    import java.awt.Component;
    import java.awt.EventQueue;
    import java.awt.Font;
    import java.awt.event.ItemEvent;
    import java.awt.event.ItemListener;
    import java.awt.event.MouseEvent;
    import java.util.EventObject;
    import java.util.Vector;
    
    import javax.swing.AbstractCellEditor;
    import javax.swing.JCheckBox;
    import javax.swing.JFrame;
    import javax.swing.JScrollPane;
    import javax.swing.JTree;
    import javax.swing.UIManager;
    import javax.swing.event.ChangeEvent;
    import javax.swing.tree.DefaultMutableTreeNode;
    import javax.swing.tree.DefaultTreeCellRenderer;
    import javax.swing.tree.TreeCellEditor;
    import javax.swing.tree.TreeCellRenderer;
    import javax.swing.tree.TreePath;
    
    public class JCheckBoxJTreeEditor {
    private JFrame frame = new JFrame("CheckBox Tree");
        public JCheckBoxJTreeEditor() {        
            CheckBoxNode accessibilityOptions[] = {
                new CheckBoxNode(
                "Move system caret with focus/selection changes", false),
                new CheckBoxNode("Always expand alt text for images", true)};
            CheckBoxNode browsingOptions[] = {
                new CheckBoxNode("Notify when downloads complete", true),
                new CheckBoxNode("Disable script debugging", true),
                new CheckBoxNode("Use AutoComplete", true),
                new CheckBoxNode("Browse in a new process", false)};
            Vector accessVector = new NamedVector("Accessibility", accessibilityOptions);
            Vector browseVector = new NamedVector("Browsing", browsingOptions);
            Object rootNodes[] = {accessVector, browseVector};
            Vector rootVector = new NamedVector("Root", rootNodes);
            JTree tree = new JTree(rootVector);
            CheckBoxNodeRenderer renderer = new CheckBoxNodeRenderer();
            tree.setCellRenderer(renderer);
            tree.setCellEditor(new CheckBoxNodeEditor(tree));
            tree.setEditable(true);
            JScrollPane scrollPane = new JScrollPane(tree);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add(scrollPane);
            frame.setSize(400, 250);
            frame.setVisible(true);
        }
    
        public static void main(String args[]) {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    new JCheckBoxJTreeEditor();
                }
            });
        }
    }
    
    class CheckBoxNodeRenderer implements TreeCellRenderer {
    
        private JCheckBox leafRenderer = new JCheckBox();
        private DefaultTreeCellRenderer nonLeafRenderer = new DefaultTreeCellRenderer();
        private Color selectionBorderColor, selectionForeground, selectionBackground,
                textForeground, textBackground;
    
        protected JCheckBox getLeafRenderer() {
            return leafRenderer;
        }
    
        public CheckBoxNodeRenderer() {
            Font fontValue;
            fontValue = UIManager.getFont("Tree.font");
            if (fontValue != null) {
                leafRenderer.setFont(fontValue);
            }
            Boolean booleanValue = (Boolean) UIManager.get("Tree.drawsFocusBorderAroundIcon");
            leafRenderer.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");
        }
    
        @Override
        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.setText(stringValue);
                leafRenderer.setSelected(false);
                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.setText(node.getText());
                        leafRenderer.setSelected(node.isSelected());
                    }
                }
                returnValue = leafRenderer;
            } else {
                returnValue = nonLeafRenderer.getTreeCellRendererComponent(tree, value,
                        selected, expanded, leaf, row, hasFocus);
            }
            return returnValue;
        }
    }
    
    class CheckBoxNodeEditor extends AbstractCellEditor implements TreeCellEditor {
    
        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.getLeafRenderer();
            CheckBoxNode checkBoxNode = new CheckBoxNode(checkbox.getText(), checkbox.isSelected());
            return checkBoxNode;
        }
    
        @Override
        public boolean isCellEditable(EventObject event) {
            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 = ((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, true,
                    expanded, leaf, row, true);
            ItemListener itemListener = new ItemListener() {
                @Override
                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 + "]";
        }
    }
    
    class NamedVector extends Vector {
    
        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 + "]";
        }
    }
    

    【讨论】:

    • 但这就是我不希望的样子?现在创建监听器的方式我得到了太多的事件?
    • XxxXxxEditor 是临时的 JComponents by default 只有在编辑开始到停止时才存在,那么当前时间只能有一个 XxxXxxEditor,没有太多事件,也没有来自 ItemListener
    • 但是点击三个不同的节点后,每次点击会发送9或10个事件。我不明白这怎么可能是正确的。请详细说明! :)
    • 这是正确的,看起来也像数字或事件,请问您期待什么,我看不出有更好更干净的方式来使用已实现的方法和通知器(在 API 中)
    • 我希望点击一个节点时,会发送 1 个事件。现在的方式是,第一次点击发送 1 个事件。第二次单击发送 2 个事件,依此类推。要重现此行为,请考虑我有 1 个根和两个孩子。点击 child 1, child2, child 1, child2... 重现了我提到的内容。这必须是因为每次我单击都会创建一个新的侦听器。正如硬连线所提到的,我可以通过说每个节点只有一个侦听器来减少这种情况。不过,如果我有 10 个节点,那么我将有 10 个侦听器,但我想的是所有节点只有 1 个侦听器
    猜你喜欢
    • 1970-01-01
    • 2012-09-09
    • 1970-01-01
    • 2015-01-22
    • 1970-01-01
    • 2020-08-07
    • 2020-10-11
    • 2021-11-03
    • 2021-12-13
    相关资源
    最近更新 更多