编辑中返回意外值 - JTree

Unexpected value returned in edit - JTree

在下面的代码中有一个例子 JTree 每行有两个 JToggleButtons 和一个 JLabel 都包含在 Holder (subclass of JPanel).

JTree 上的一片叶子可能如下所示。

并且当 JToggleButtons 之一被点击时,被点击的切换按钮应该改变颜色。但是,输出非常不同,如下所示。

为什么会出现这种情况,我该如何解决这个问题?

注意:这个return值可以在public component getTreeCellEditorComponent(...)

中找到
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.io.*;
import java.util.*;

import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
import javax.swing.tree.*;
import javax.swing.table.*;
import javax.swing.plaf.basic.*;
import javax.swing.plaf.metal.*;

@SuppressWarnings("serial")
public class DirectoryExplorer extends JFrame {
    private DirectoryExplorer() {
        super("Directory Explorer");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(new GridLayout(1, 1));
        createPanel();
        setSize(800,600);
        setVisible(true);
    }

    private void createPanel() {
        DefaultMutableTreeNode colors = new DefaultMutableTreeNode("Colours");
        colors.add(new DefaultMutableTreeNode("Red"));
        colors.add(new DefaultMutableTreeNode("Green"));
        colors.add(new DefaultMutableTreeNode("Blue"));

        DefaultMutableTreeNode falvors = new DefaultMutableTreeNode("Flavours");
        falvors.add(new DefaultMutableTreeNode("Toffee"));
        falvors.add(new DefaultMutableTreeNode("Fudge"));
        falvors.add(new DefaultMutableTreeNode("Chocolate"));

        DefaultMutableTreeNode root = new DefaultMutableTreeNode("root");
        root.add(colors);
        root.add(falvors);
        root.add(new DefaultMutableTreeNode("Leafy"));

        JPanel panel = new JPanel(new GridLayout(1, 1));
        JTree tree = new JTree(root);

        FileNameRenderer fileRender = new FileNameRenderer();
        tree.setCellRenderer(fileRender);

        tree.setCellEditor(new CheckBoxNodeEditor(tree));
        tree.setEditable(true);

        tree.setShowsRootHandles(true);

        panel.add(new JScrollPane(tree));
        getContentPane().add(panel);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(() -> new DirectoryExplorer());
    }

    private class FileNameRenderer extends DefaultTreeCellRenderer {
        private DefaultChkBx def = new DefaultChkBx();
        private FavouriteChkBx fav = new FavouriteChkBx();

        protected DefaultChkBx getDefBut() {
            return def;
        }

        protected FavouriteChkBx getFavBut() {
            return fav;
        }

        @Override
        public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
            JComponent c = (JComponent) super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
            return new Holder((JLabel) c, def, fav);
        }

        public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus, boolean defSel, boolean favSel) {
            JComponent c = (JComponent) super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
            def.setSelected(defSel);
            fav.setSelected(favSel);
            return new Holder((JLabel) c, def, fav);
        }
    }

    private class CheckBoxNodeEditor extends AbstractCellEditor implements TreeCellEditor {
        FileNameRenderer renderer = new FileNameRenderer();
        ChangeEvent changeEvent = null;
        JTree t;
        TreePath path;
        DefaultChkBx def = renderer.getDefBut();
        FavouriteChkBx fav = renderer.getFavBut();

        public CheckBoxNodeEditor(JTree tree) {
            t = tree;
        }

        public Object getCellEditorValue() {
            def = renderer.getDefBut();
            fav = renderer.getFavBut();

            def.setSelected(def.isSelected());
            fav.setSelected(fav.isSelected());

            Holder checkBoxNode = new Holder(new JLabel((((DefaultMutableTreeNode) path.getLastPathComponent()).getUserObject().toString())), def, fav);
            return checkBoxNode;
        }

        public boolean isCellEditable(EventObject event) {
            if(event instanceof MouseEvent) {
                MouseEvent mouseEvent = (MouseEvent) event;
                path = t.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();
                        return (userObject instanceof String);
                    }
                }
            }
            return false;
        }

        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() {
                public void itemStateChanged(ItemEvent itemEvent) {
                    if (stopCellEditing())
                        fireEditingStopped();
                }
            };

            if (editor instanceof Holder) {
                for(Component c : ((Holder) editor).getComponents()) {
                    if(c instanceof DefaultChkBx)
                        ((DefaultChkBx) c).addItemListener(itemListener);
                    else if(c instanceof FavouriteChkBx)
                        ((FavouriteChkBx) c).addItemListener(itemListener);
                }
            }

            return editor;
        }
    }

    private class Holder extends JPanel {
        public Holder(Component c, DefaultChkBx def, FavouriteChkBx fav) {
            setLayout(new GridBagLayout());
            setBackground(Color.BLACK);
            setOpaque(false);
            addComponents(c, def, fav);
        }

        private void addComponents(Component c, DefaultChkBx def, FavouriteChkBx fav) {
            GridBagConstraints gBC = new GridBagConstraints();

            gBC.insets = new Insets(0, 0, 0, 5);
            add(c, gBC);

            gBC.insets = new Insets(0, 0, 0, 0);
            add(def, gBC);
            add(fav, gBC);
        }
    }

    private class DefaultChkBx extends JToggleButton {
        public DefaultChkBx() {
            setUI(new MetalToggleButtonUI() {
                @Override
                protected Color getSelectColor() {
                    return new Color(242, 0, 255);
                }
            });
            setBorder(null);
            setForeground(Color.GRAY);
            setText("Default");
            setFocusPainted(false);
        }
    }

    private class FavouriteChkBx extends JToggleButton {
        public FavouriteChkBx() {
            setUI(new MetalToggleButtonUI() {
                @Override
                protected Color getSelectColor() {
                    return Color.RED;
                }
            });
            setBorder(null);
            setForeground(Color.GRAY);
            setText("Favourite");
            setFocusPainted(false);
        }
    }
}

CheckBoxNodeEditor 中,您的 getCellEditorValue() 应该 return 编辑对象的值(通常是叶中显示的 String 对象)。

相反,您的方法 return 是 Holder 的一个实例。 toString() 方法在此对象上调用并由树显示(这是您看到的 "DirectoryExplorer$Holder[....." 显示)。

作为开始,您可以通过此方法return 一个简单的字符串(如 "Leafy")。