对于我一直在从事的项目,我必须创建一个JTree,每行都带有一个JToggleButton,实际上每行上都有两个。因此,如您在MCVE中所见,为了使这项工作有效,我编写了一个自定义DefaultTreeCellRenderer和一个自定义AbstractCellEditor

但是,编辑是否选择JToggleBox存在问题。最初,当您选择要更改的第一个值时,它可以正常工作。而且,如果您停留在JTree的该行上,并且在JToggleButton上没有单击其他任何地方,则可以正常工作。如果单击其他位置,则设置的值将丢失。

单击其他位置时,如何保留JToggleButton的设置值?

MCVE

import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.util.EventObject;

import javax.swing.AbstractCellEditor;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToggleButton;
import javax.swing.JTree;
import javax.swing.event.ChangeEvent;
import javax.swing.plaf.metal.MetalToggleButtonUI;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeCellEditor;
import javax.swing.tree.TreePath;

@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 {
        @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);
        }
    }

    private class CheckBoxNodeEditor extends AbstractCellEditor implements TreeCellEditor {
        FileNameRenderer renderer = new FileNameRenderer();
        ChangeEvent changeEvent = null;
        JTree t;
        TreePath path;

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

        public Object getCellEditorValue() {
            Holder checkBoxNode = new Holder(new JLabel((((DefaultMutableTreeNode) path.getLastPathComponent()).getUserObject().toString())));
            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 JToggleButton) {
                ((JToggleButton) editor).addItemListener(itemListener);
            }

            return editor;
        }
    }

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

        private void addComponents(Component c) {
            GridBagConstraints gBC = new GridBagConstraints();

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

            gBC.insets = new Insets(0, 0, 0, 0);
            add(new DefaultChkBx(), gBC);
            add(new FavouriteChkBx(), 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);
        }
    }
}

最佳答案

在我看来,您的问题出在“模型”上:您在为节点使用普通的DefaultMutableTreeNode对象,该类无法保存最重要的数据-按钮的状态。

而是考虑


使用从DefaultMutableTreeNode扩展的类,该类具有两个布尔值,渲染器将使用这些值告诉按钮显示什么状态。
或通过将包含文本以及两个布尔字段(JToggleButton用于显示其状态的字段)的对象传递到DefaultMutableTreeNode中,将DefaultMutableTreeNode用作“包装器”。

08-26 12:01