JCheckBoxTree 初始状态问题

Posted

技术标签:

【中文标题】JCheckBoxTree 初始状态问题【英文标题】:JCheckBoxTree initial state issue 【发布时间】:2020-03-27 20:15:13 【问题描述】:

我在使用一个自定义的JTree,实现了拖放,单元格渲染器是一个Jcheckbox

问题是我的复选框状态与我的 jtree 选择状态不同,因为我正在更新单元格渲染器中的复选框选择状态

@Override
public TreeCellRenderer getCellRenderer() 
    return (tree, value, selected, expanded, leaf, row, hasFocus) -> 

        selected = GridManager.getInstance().getDisplayableSources().containsKey(value+"");
        final String key = value + "";
        final JCheckBoxMenuItem jCheckBoxMenuItem = new JCheckBoxMenuItem(key);
        jCheckBoxMenuItem.setSelected(selected);
        return jCheckBoxMenuItem;
    ;

我很清楚,selected lambda 参数反映了单击事件触发(触发)的某种内部选择状态,这解释了我的拖放错误行为

那么如何更新 JTree 选择以便我可以像这样盲目地使用我的渲染器

@Override
public TreeCellRenderer getCellRenderer() 
    return (tree, value, selected, expanded, leaf, row, hasFocus) -> 
        final String key = value + "";
        final JCheckBoxMenuItem jCheckBoxMenuItem = new JCheckBoxMenuItem(key);
        jCheckBoxMenuItem.setSelected(selected);
        return jCheckBoxMenuItem;
    ;

这是我的完整实现

import java.util.HashMap;

import javax.swing.DropMode;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreePath;

import com.wanasis.vlc.control.GridManager;
import com.wanasis.vlc.domain.SourceNode;

public class JCheckBoxDragableTree extends JTree 

    /**
     * 
     */
    private static final long serialVersionUID = -7768978192373533316L;
    private HashMap<String, SourceNode> configurableSources;
    private static HashMap<String, String> displaySources = new HashMap<>();

    public JCheckBoxDragableTree(HashMap<String, SourceNode> configurableSources,
            HashMap<String, String> displaySources) 

        this.configurableSources = configurableSources;
        this.displaySources = displaySources;
        final DefaultTreeModel dtm = new DefaultTreeModel(new DefaultMutableTreeNode());

        final DefaultMutableTreeNode root = (DefaultMutableTreeNode) dtm.getRoot();
        configurableSources.values().stream().filter(s -> 
            return s.getParent().isEmpty();
        ).forEachOrdered(child -> 
            dtm.insertNodeInto(new DefaultMutableTreeNode(child.getId()), root, root.getChildCount());
        );
        setModel(dtm);

        setDragEnabled(true);
        setDropMode(DropMode.ON);
        setTransferHandler(new TreeTransferHandler());
        setSelectionModel(new SomativeSelectionModel());
        addTreeSelectionListener(new TreeSelectionListener() 

            @Override
            public void valueChanged(TreeSelectionEvent e) 



                String key = ""+e.getPath().getLastPathComponent();
                DefaultMutableTreeNode dmtn = (DefaultMutableTreeNode) e.getPath().getLastPathComponent();
                System.out.println(dmtn.getUserObject());
                System.out.println(key);
                GridManager gm = GridManager.getInstance();
                boolean isDisplayed = gm.getDisplayableSources().containsKey(key);
                if (isDisplayed) 
                    System.out.println("hiding selected element");
                    gm.getDisplayableSources().remove(key);
                    System.out.println(gm.getDisplayableSources().keySet());
                 else 
                    System.out.println("showing selected element");
                    gm.getDisplayableSources().put(key, gm.getConfigurableSources().get(key).getSource());

                    System.out.println(gm.getDisplayableSources().keySet());
                
                gm.prepareDisplay();

            
        );

    

    @Override
    public void setSelectionPath(TreePath path) 

        addSelectionPath(path);

        return;
    

    @Override
    public void setSelectionPaths(TreePath[] paths) 

        addSelectionPaths(paths);

        return;
    

    @Override
    public void setSelectionRow(int row) 

        addSelectionRow(row);

        return;
    

    @Override
    public void setSelectionRows(int[] rows) 

        addSelectionRows(rows);

        return;
    

    @Override
    public TreeCellRenderer getCellRenderer() 
        return (tree, value, selected, expanded, leaf, row, hasFocus) -> 

            selected = GridManager.getInstance().getDisplayableSources().containsKey(value+"");
            final String key = value + "";
            final JCheckBoxMenuItem jCheckBoxMenuItem = new JCheckBoxMenuItem(key);
            jCheckBoxMenuItem.setSelected(selected);
            return jCheckBoxMenuItem;
        ;
    

【问题讨论】:

【参考方案1】:

在我的例子中,我更新了我的所有代码,下面的 jtree 是拖放的,带有复选框作为单元格渲染器

我还在 D&D 和复选框选择事件中附加了一些事件逻辑,您所要做的就是根据需要更新它

希望这会有所帮助

import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.Insets;
import java.awt.Point;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragGestureRecognizer;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetContext;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.util.EventObject;
import java.util.HashMap;

import javax.swing.AbstractCellEditor;
import javax.swing.Icon;
import javax.swing.JCheckBox;
import javax.swing.JTree;
import javax.swing.UIManager;
import javax.swing.border.LineBorder;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeCellEditor;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;

import com.wanasis.vlc.control.GridManager;
import com.wanasis.vlc.domain.SourceNode;

public class JCheckBoxDragableTree extends JTree 

    /**
     * 
     */
    private static final long serialVersionUID = -776897819237333316L;
    private HashMap<String, SourceNode> configurableSources;
    private static HashMap<String, String> displaySources = new HashMap<>();

    private static DefaultMutableTreeNode root = new DefaultMutableTreeNode("wanaDVR");

    TreeDragSource ds;

    TreeDropTarget dt;

    public JCheckBoxDragableTree(HashMap<String, SourceNode> configurableSources, HashMap<String, String> displaySources) 
        super(root);
        System.out.println("JCheckBoxDragableTree");
        setupTreeElements(configurableSources);

        this.configurableSources = configurableSources;
        this.displaySources = displaySources;

        setCellRenderer(new CheckBoxTreeNodeRenderer());
        setCellEditor(new CheckBoxTreeNodeEditor(this));
        setEditable(true);
        ds = new TreeDragSource(this, DnDConstants.ACTION_COPY_OR_MOVE);
        dt = new TreeDropTarget(this);

        addTreeSelectionListener(new TreeSelectionListener() 

            @Override
            public void valueChanged(TreeSelectionEvent e) 

                GridManager gm = GridManager.getInstance();
                String key = "" + e.getPath().getLastPathComponent();
                DefaultMutableTreeNode dmtn = (DefaultMutableTreeNode) e.getPath().getLastPathComponent();
                System.out.println(dmtn.getUserObject());
                System.out.println(key);


            
        );
    

    public void setupTreeElements(HashMap<String, SourceNode> configurableSources) 
        final HashMap<String, DefaultMutableTreeNode> parents = new HashMap<>();
        final HashMap<String, SourceNode> reste = new HashMap<>();

        configurableSources.values().stream().filter(s -> 
            return s.getParent().isEmpty()||s.getParent().trim().toLowerCase().equals("wanadvr");
        ).forEachOrdered(child -> 
            String key = child.getId();
            DefaultMutableTreeNode newChild = new DefaultMutableTreeNode(key);
            parents.put(key, newChild);
            root.add(newChild);
        );
        configurableSources.values().stream().filter(s -> 
            return !s.getParent().isEmpty() && !s.getParent().trim().toLowerCase().equals("wanadvr");
        ).forEachOrdered(child -> 
            String parentKey = child.getParent();
            String key = child.getId();
            DefaultMutableTreeNode newChild = new DefaultMutableTreeNode(key);
            if (parents.keySet().contains(parentKey)) 
                parents.get(parentKey).add(newChild);
                parents.put(key, newChild);
             else 
                reste.put(key, child);
            
        );
        while (reste.size() > 0) 
            reste.values().stream().forEachOrdered(child -> 
                String parentKey = child.getParent();
                String key = child.getId();
                DefaultMutableTreeNode newChild = new DefaultMutableTreeNode(key);
                if (parents.keySet().contains(parentKey)) 
                    parents.get(parentKey).add(newChild);
                    parents.put(key, newChild);
                    reste.remove(key);
                
            );
        
    


class TreeNodeCheckBox extends JCheckBox 

    public TreeNodeCheckBox() 
        this("", false);
    

    public TreeNodeCheckBox(final String text, final boolean selected) 
        this(text, null, selected);
    

    public TreeNodeCheckBox(final String text, final Icon icon, final boolean selected) 
        super(text, icon, selected);
        setMargin(new Insets(1, 1, 1, 1));

        System.out.println(text);
        System.out.println(isSelected());
        GridManager instance = GridManager.getInstance();
        if (isSelected()) 
            instance.getDisplayableSources().put(text, instance.getConfigurableSources().get(text).getSource());

        else 
            instance.getDisplayableSources().remove(text);
        
        instance.prepareDisplay();
    


class CheckBoxTreeNodeRenderer implements TreeCellRenderer 
    Color selectionBorderColor, selectionForeground, selectionBackground, textForeground, textBackground;
    private TreeNodeCheckBox treeNodeCheckBox = new TreeNodeCheckBox();
    private DefaultTreeCellRenderer defaultRenderer = new DefaultTreeCellRenderer();

    public CheckBoxTreeNodeRenderer() 
        Font fontValue;
        fontValue = UIManager.getFont("Tree.font");
        if (fontValue != null) 
            treeNodeCheckBox.setFont(fontValue);
        
        Boolean booleanValue = (Boolean) UIManager.get("Tree.drawsFocusBorderAroundIcon");
        treeNodeCheckBox.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");
    

    protected TreeNodeCheckBox getCheckBoxRenderer() 
        return treeNodeCheckBox;
    

    public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded,
            boolean leaf, int row, boolean hasFocus) 
        Component component;
        if (leaf)          
            String stringValue = tree.convertValueToText(value, selected, expanded, leaf, row, false);

            treeNodeCheckBox.setText(stringValue);
            treeNodeCheckBox.setSelected(GridManager.getInstance().getDisplayableSources().keySet().contains(stringValue));
            treeNodeCheckBox.setEnabled(tree.isEnabled());
            if (selected) 
                treeNodeCheckBox.setBorder(new LineBorder(selectionBorderColor));
                treeNodeCheckBox.setForeground(selectionForeground);
                treeNodeCheckBox.setBackground(selectionBackground);
             else 
                treeNodeCheckBox.setBorder(null);
                treeNodeCheckBox.setForeground(textForeground);
                treeNodeCheckBox.setBackground(textBackground);
            
            if ((value != null) && (value instanceof DefaultMutableTreeNode)) 
                Object userObject = ((DefaultMutableTreeNode) value).getUserObject();
                if (userObject instanceof TreeNodeCheckBox) 
                    TreeNodeCheckBox node = (TreeNodeCheckBox) userObject;
                    treeNodeCheckBox.setText(node.getText());
                    treeNodeCheckBox.setSelected(node.isSelected());
                
            
            component = treeNodeCheckBox;
         else 
            component = defaultRenderer.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row,
                    hasFocus);
        
        return component;
    


class CheckBoxTreeNodeEditor extends AbstractCellEditor implements TreeCellEditor 
    CheckBoxTreeNodeRenderer renderer = new CheckBoxTreeNodeRenderer();
    JTree tree;

    public CheckBoxTreeNodeEditor(JTree tree) 
        this.tree = tree;
    

    public Object getCellEditorValue() 
        TreeNodeCheckBox checkBox = renderer.getCheckBoxRenderer();
        TreeNodeCheckBox checkBoxNode = new TreeNodeCheckBox(checkBox.getText(), checkBox.isSelected());
        return checkBoxNode;
    

    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 treeNode = (DefaultMutableTreeNode) node;
                    editable = treeNode.isLeaf();
                
            
        
        return editable;
    

    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);
        if (editor instanceof TreeNodeCheckBox) 
            ((TreeNodeCheckBox) editor).addItemListener(new ItemListener() 
                public void itemStateChanged(ItemEvent itemEvent) 
                    if (stopCellEditing()) 
                        fireEditingStopped();
                    
                
            );
        
        return editor;
    



class TreeDragSource implements DragSourceListener, DragGestureListener 

    DragSource source;

    DragGestureRecognizer recognizer;

    TransferableTreeNode transferable;

    DefaultMutableTreeNode oldNode;

    JTree sourceTree;

    public TreeDragSource(JTree tree, int actions) 
        sourceTree = tree;
        source = new DragSource();
        recognizer = source.createDefaultDragGestureRecognizer(sourceTree, actions, this);
    

    /*
     * Drag Gesture Handler
     */
    public void dragGestureRecognized(DragGestureEvent dge) 
        TreePath path = sourceTree.getSelectionPath();
        if ((path == null) || (path.getPathCount() <= 1)) 
            // We can't move the root node or an empty selection
            return;
        
        oldNode = (DefaultMutableTreeNode) path.getLastPathComponent();
        transferable = new TransferableTreeNode(path);
        // source.startDrag(dge, DragSource.DefaultMoveNoDrop, transferable, this);

        // If you support dropping the node anywhere, you should probably
        // start with a valid move cursor:
        source.startDrag(dge, DragSource.DefaultMoveDrop, transferable, this);
    

    /*
     * Drag Event Handlers
     */
    public void dragEnter(DragSourceDragEvent dsde) 
    

    public void dragExit(DragSourceEvent dse) 
    

    public void dragOver(DragSourceDragEvent dsde) 
    

    public void dropActionChanged(DragSourceDragEvent dsde) 
        System.out.println("Action: " + dsde.getDropAction());
        System.out.println("Target Action: " + dsde.getTargetActions());
        System.out.println("User Action: " + dsde.getUserAction());
    

    public void dragDropEnd(DragSourceDropEvent dsde) 
        /*
         * to support move or copy, we have to check which occurred:
         */
        System.out.println("Drop Action: " + dsde.getDropAction());
        if (dsde.getDropSuccess() && (dsde.getDropAction() == DnDConstants.ACTION_MOVE)) 
            ((DefaultTreeModel) sourceTree.getModel()).removeNodeFromParent(oldNode);
        

        /*
         * to support move only... if (dsde.getDropSuccess()) 
         * ((DefaultTreeModel)sourceTree.getModel()).removeNodeFromParent(oldNode); 
         */
    


// TreeDropTarget.java
// A quick DropTarget that's looking for drops from draggable JTrees.
//

class TreeDropTarget implements DropTargetListener 

    DropTarget target;

    JTree targetTree;

    public TreeDropTarget(JTree tree) 
        targetTree = tree;
        target = new DropTarget(targetTree, this);
    

    /*
     * Drop Event Handlers
     */
    private TreeNode getNodeForEvent(DropTargetDragEvent dtde) 
        Point p = dtde.getLocation();
        DropTargetContext dtc = dtde.getDropTargetContext();
        JTree tree = (JTree) dtc.getComponent();
        TreePath path = tree.getClosestPathForLocation(p.x, p.y);
        return (TreeNode) path.getLastPathComponent();
    

    public void dragEnter(DropTargetDragEvent dtde) 
        TreeNode node = getNodeForEvent(dtde);
        if (node.isLeaf()) 
            dtde.rejectDrag();
         else 
            // start by supporting move operations
            // dtde.acceptDrag(DnDConstants.ACTION_MOVE);
            dtde.acceptDrag(dtde.getDropAction());
        
    

    public void dragOver(DropTargetDragEvent dtde) 
        TreeNode node = getNodeForEvent(dtde);
        if (node.isLeaf()) 
            dtde.rejectDrag();
         else 
            // start by supporting move operations
            // dtde.acceptDrag(DnDConstants.ACTION_MOVE);
            dtde.acceptDrag(dtde.getDropAction());
        
    

    public void dragExit(DropTargetEvent dte) 
    

    public void dropActionChanged(DropTargetDragEvent dtde) 
    

    public void drop(DropTargetDropEvent dtde) 
        Point pt = dtde.getLocation();
        DropTargetContext dtc = dtde.getDropTargetContext();
        JTree tree = (JTree) dtc.getComponent();
        TreePath parentpath = tree.getClosestPathForLocation(pt.x, pt.y);
        DefaultMutableTreeNode parent = (DefaultMutableTreeNode) parentpath.getLastPathComponent();


        if (parent.isLeaf()) 
            dtde.rejectDrop();
            return;
        

        try 
            Transferable tr = dtde.getTransferable();
            DataFlavor[] flavors = tr.getTransferDataFlavors();
            for (int i = 0; i < flavors.length; i++) 
                if (tr.isDataFlavorSupported(flavors[i])) 
                    dtde.acceptDrop(dtde.getDropAction());
                    TreePath p = (TreePath) tr.getTransferData(flavors[i]);
                    DefaultMutableTreeNode node = (DefaultMutableTreeNode) p.getLastPathComponent();
                    DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
                    model.insertNodeInto(node, parent, 0);
                    dtde.dropComplete(true);
                    System.out.println("dragged node : "+node);
                    System.out.println("dropped in parent : "+parent);
                    GridManager.getInstance().getConfigurableSources().get(""+node).setParent(""+parent);
                    System.out.println(GridManager.getInstance().getConfigurableSources().values());

                    return;
                
            
            dtde.rejectDrop();
         catch (Exception e) 
            e.printStackTrace();
            dtde.rejectDrop();
        
    


// TransferableTreeNode.java
// A Transferable TreePath to be used with Drag & Drop applications.
//

class TransferableTreeNode implements Transferable 

    public static DataFlavor TREE_PATH_FLAVOR = new DataFlavor(TreePath.class, "Tree Path");

    DataFlavor flavors[] =  TREE_PATH_FLAVOR ;

    TreePath path;

    public TransferableTreeNode(TreePath tp) 
        path = tp;
    

    public synchronized DataFlavor[] getTransferDataFlavors() 
        return flavors;
    

    public boolean isDataFlavorSupported(DataFlavor flavor) 
        return (flavor.getRepresentationClass() == TreePath.class);
    

    public synchronized Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException 
        if (isDataFlavorSupported(flavor)) 
            return (Object) path;
         else 
            throw new UnsupportedFlavorException(flavor);
        
    

NB 我的 D&D 事件在成功放入 drop 处理程序 TreeDropTarget 方法 drop 后附加,因为 D&D 使用的是复制 Jtree 模式,因此您可以注入您的构造函数中的事件逻辑。最后,这个 JTree 在一个小的上下文中表现得像我需要的那样,这对于大数据集来说不是一个好主意(很多事件总是在重绘树)

【讨论】:

以上是关于JCheckBoxTree 初始状态问题的主要内容,如果未能解决你的问题,请参考以下文章

在每个页面上将状态重置为初始状态

React Hooks - 将状态设置为初始状态

Reactjs 状态初始化一个值数组

更改初始视图后如何加载状态栏

无法在 Redux 中设置初始状态

Infinispan 初始状态传输超时