如何将复选框添加到 JTree 节点以管理多选?

Posted

技术标签:

【中文标题】如何将复选框添加到 JTree 节点以管理多选?【英文标题】:How to add checkbox to JTree node to manage multiselection? 【发布时间】:2012-10-03 23:13:59 【问题描述】:

我想构建JTree,它的节点包含复选框+图标+数据和树选择算法。

【问题讨论】:

【参考方案1】:

这是演示如何将复选框添加到 Jtree 节点的完整示例。我将 JTree 与基于文件系统内容的节点一起使用。

我还使用AddCheckBoxToTree.CheckTreeManager 类来管理选择或半选择选项。

使用

public AddCheckBoxToTree.CheckTreeManager getCheckTreeManager() 
    return checkTreeManager;

使用选择树路径的方法。

例如:

// clear all selected path in order 
    TreePath[] paths=getCheckTreeManager().getSelectionModel().getSelectionPaths();
    if(paths != null)
        for(TreePath tp : paths)
            getCheckTreeManager().getSelectionModel().removeSelectionPath(tp);
        
    

我在这里粘贴了所有执行此操作的代码:

package com.demo.tree.checkbox;


import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.File;
import java.util.Vector;

import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.ToolTipManager;
import javax.swing.UIManager;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeExpansionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;



public class FileTreeViewer  extends JFrame 

private static final long serialVersionUID = 1L;
public static final ImageIcon ICON_COMPUTER =  new ImageIcon("");
public static final ImageIcon ICON_DISK =  new ImageIcon("defaults1.png");
public static final ImageIcon ICON_FOLDER =   new ImageIcon("fol_orig.png");
public static final ImageIcon ICON_EXPANDEDFOLDER =  new ImageIcon("folder_open.png");

protected JTree  m_tree;
protected DefaultTreeModel m_model;

AddCheckBoxToTree AddCh = new AddCheckBoxToTree();

private AddCheckBoxToTree.CheckTreeManager checkTreeManager;


protected TreePath m_clickedPath;

public FileTreeViewer()

    super("Demo tree check box");
    setSize(400, 300);

    DefaultMutableTreeNode top = new DefaultMutableTreeNode(
            new IconData(ICON_COMPUTER, null, "Computer"));

    DefaultMutableTreeNode node;
    File[] roots = File.listRoots();
    for (int k=0; k<roots.length; k++)
    
        node = new DefaultMutableTreeNode(new IconData(ICON_DISK, null, new FileNode(roots[k])));
        top.add(node);
        node.add(new DefaultMutableTreeNode( new Boolean(true) ));
    

    m_model = new DefaultTreeModel(top);

    m_tree = new JTree(m_model)
        public String getToolTipText(MouseEvent ev) 
        
            if(ev == null)
                return null;
            TreePath path = m_tree.getPathForLocation(ev.getX(), 
                    ev.getY());
            if (path != null)
            
                FileNode fnode = getFileNode(getTreeNode(path));
                if (fnode==null)
                    return null;
                File f = fnode.getFile();
                return (f==null ? null : f.getPath());
            
            return null;
        
    ;

    ToolTipManager.sharedInstance().registerComponent(m_tree);

    m_tree.putClientProperty("JTree.lineStyle", "Angled");

    TreeCellRenderer renderer = new IconCellRenderer();
    m_tree.setCellRenderer(renderer);

    m_tree.addTreeExpansionListener(new  DirExpansionListener());

    m_tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); 
    m_tree.setShowsRootHandles(true); 
    m_tree.setEditable(false);


    checkTreeManager = AddCh.new CheckTreeManager(m_tree, null);



    JScrollPane s = new JScrollPane();
    s.getViewport().add(m_tree);
    getContentPane().add(s, BorderLayout.CENTER);


    WindowListener wndCloser = new WindowAdapter()
        public void windowClosing(WindowEvent e)
            System.exit(0);
        
    ;

    addWindowListener(wndCloser);

    setVisible(true);


DefaultMutableTreeNode getTreeNode(TreePath path)

    return (DefaultMutableTreeNode)(path.getLastPathComponent());


FileNode getFileNode(DefaultMutableTreeNode node)

    if (node == null)
        return null;
    Object obj = node.getUserObject();
    if (obj instanceof IconData)
        obj = ((IconData)obj).getObject();
    if (obj instanceof FileNode)
        return (FileNode)obj;
    else
        return null;


public AddCheckBoxToTree.CheckTreeManager getCheckTreeManager() 
    return checkTreeManager;


// Make sure expansion is threaded and updating the tree model
// only occurs within the event dispatching thread.
class DirExpansionListener implements TreeExpansionListener

    public void treeExpanded(TreeExpansionEvent event)
    
        final DefaultMutableTreeNode node = getTreeNode(
                event.getPath());
        final FileNode fnode = getFileNode(node);

        Thread runner = new Thread() 
        
            public void run() 
            
                if (fnode != null && fnode.expand(node)) 
                
                    Runnable runnable = new Runnable() 
                    
                        public void run() 
                        
                            m_model.reload(node);
                        
                    ;
                    SwingUtilities.invokeLater(runnable);
                
            
        ;
        runner.start();
    

    public void treeCollapsed(TreeExpansionEvent event) 




public static void main(String argv[]) 

    try 
        UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
     catch (Exception evt) 
    new FileTreeViewer();



class IconCellRenderer extends JLabel implements TreeCellRenderer
protected Color m_textSelectionColor;
protected Color m_textNonSelectionColor;
protected Color m_bkSelectionColor;
protected Color m_bkNonSelectionColor;
protected Color m_borderSelectionColor;

protected boolean m_selected;

public IconCellRenderer()

    super();
    m_textSelectionColor = UIManager.getColor(
            "Tree.selectionForeground");
    m_textNonSelectionColor = UIManager.getColor(
            "Tree.textForeground");
    m_bkSelectionColor = UIManager.getColor(
            "Tree.selectionBackground");
    m_bkNonSelectionColor = UIManager.getColor(
            "Tree.textBackground");
    m_borderSelectionColor = UIManager.getColor(
            "Tree.selectionBorderColor");
    setOpaque(false);


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


    DefaultMutableTreeNode node = 
            (DefaultMutableTreeNode)value;
    Object obj = node.getUserObject();
    setText(obj.toString());

    if (obj instanceof Boolean)
        setText("Retrieving data...");

    if (obj instanceof IconData)
    
        IconData idata = (IconData)obj;
        if (expanded)
            setIcon(idata.getExpandedIcon());
        else
            setIcon(idata.getIcon());
    
    else
        setIcon(null);

    setFont(tree.getFont());
    setForeground(sel ? m_textSelectionColor : 
        m_textNonSelectionColor);
    setBackground(sel ? m_bkSelectionColor : 
        m_bkNonSelectionColor);
    m_selected = sel;
    return this;


public void paintComponent(Graphics g) 

    Color bColor = getBackground();
    Icon icon = getIcon();

    g.setColor(bColor);
    int offset = 0;
    if(icon != null && getText() != null) 
        offset = (icon.getIconWidth() + getIconTextGap());
    g.fillRect(offset, 0, getWidth() - 1 - offset,
            getHeight() - 1);

    if (m_selected) 
    
        g.setColor(m_borderSelectionColor);
        g.drawRect(offset, 0, getWidth()-1-offset, getHeight()-1);
    

    super.paintComponent(g);



class IconData 
protected Icon   m_icon;
protected Icon   m_expandedIcon;
protected Object m_data;

public IconData(Icon icon, Object data)

    m_icon = icon;
    m_expandedIcon = null;
    m_data = data;


public IconData(Icon icon, Icon expandedIcon, Object data)

    m_icon = icon;
    m_expandedIcon = expandedIcon;
    m_data = data;


public Icon getIcon() 
 
    return m_icon;


public Icon getExpandedIcon() 
 
    return m_expandedIcon!=null ? m_expandedIcon : m_icon;


public Object getObject() 
 
    return m_data;


public String toString() 
 
    return m_data.toString();



class FileNode 
protected File m_file;

public FileNode(File file)

    m_file = file;


public File getFile() 
 
    return m_file;


public String toString() 
 
    return m_file.getName().length() > 0 ? m_file.getName() : 
        m_file.getPath();


public boolean expand(DefaultMutableTreeNode parent)
    DefaultMutableTreeNode flag = (DefaultMutableTreeNode)parent.getFirstChild();
    if (flag==null)    // No flag
        return false;
    Object obj = flag.getUserObject();
    if (!(obj instanceof Boolean))
        return false;      // Already expanded

    parent.removeAllChildren();  // Remove Flag

    File[] files = listFiles();
    if (files == null)
        return true;

    Vector<FileNode> v = new Vector<FileNode>();

    for (int k=0; k<files.length; k++)
        File f = files[k];
        if (!(f.isDirectory()))
            continue;

        FileNode newNode = new FileNode(f);

        boolean isAdded = false;
        for (int i=0; i<v.size(); i++)
        
            FileNode nd = (FileNode)v.elementAt(i);
            if (newNode.compareTo(nd) < 0)
            
                v.insertElementAt(newNode, i);
                isAdded = true;
                break;
            
        
        if (!isAdded)
            v.addElement(newNode);
    

    for (int i=0; i<v.size(); i++)
        FileNode nd = (FileNode)v.elementAt(i);
        IconData idata = new IconData(FileTreeViewer.ICON_FOLDER, FileTreeViewer.ICON_EXPANDEDFOLDER, nd);
        DefaultMutableTreeNode node = new 
                DefaultMutableTreeNode(idata);
        parent.add(node);

        if (nd.hasSubDirs())
            node.add(new DefaultMutableTreeNode( 
                    new Boolean(true) ));
    

    return true;


public boolean hasSubDirs()
    File[] files = listFiles();
    if (files == null)
        return false;
    for (int k=0; k<files.length; k++)
    
        if (files[k].isDirectory())
            return true;
    
    return false;


public int compareTo(FileNode toCompare) 
    return  m_file.getName().compareToIgnoreCase(
            toCompare.m_file.getName() ); 


protected File[] listFiles()
    if (!m_file.isDirectory())
        return null;
    try
    
        return m_file.listFiles();
    
    catch (Exception ex)
    
        JOptionPane.showMessageDialog(null, "Error reading directory "+m_file.getAbsolutePath(),"Warning", JOptionPane.WARNING_MESSAGE);
        return null;
    


到目前为止,一切都很好而且很清楚。 现在我要粘贴选择控制器,我们开始:

这里我们创建复选框元素以添加到树节点之后

package com.demo.tree.checkbox;


import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.ButtonGroup;
import javax.swing.ButtonModel;
import javax.swing.Icon;
import javax.swing.JCheckBox;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.ActionMapUIResource;


public class TristateCheckBox extends JCheckBox 
static final  long serialVersionUID =0;

/** This is a type-safe enumerated type */
public static class State

    private State() 


public final        State NOT_SELECTED = new State();   
public final        State SELECTED     = new State();
public final static State DONT_CARE    = new State();

private final TristateDecorator model;

public TristateCheckBox(String text, Icon icon, State initial)
    super(text, icon);
    // Add a listener for when the mouse is pressed
    super.addMouseListener(new MouseAdapter() 
        public void mousePressed(MouseEvent e) 
            grabFocus();
            model.nextState();
        
    );

    // Reset the keyboard action map
    ActionMap map = new ActionMapUIResource();
    map.put("pressed", new AbstractAction() 

        private static final long serialVersionUID = 1L;

        public void actionPerformed(ActionEvent e) 
            grabFocus();
            model.nextState();
        
    );

    map.put("released", null);

    SwingUtilities.replaceUIActionMap(this, map);

    // set the model to the adapted model
    model = new TristateDecorator(getModel());
    setModel(model);
    setState(initial);


// Constractor types:
public TristateCheckBox(String text, State initial) 
    this(text, null, initial);


public TristateCheckBox(String text) 
    this(text, DONT_CARE);


public TristateCheckBox() 
    this(null);


/** No one may add mouse listeners, not even Swing! */
public void addMouseListener(MouseListener l)  
/**
 * Set the new state to either SELECTED, NOT_SELECTED or
 * DONT_CARE.  If state == null, it is treated as DONT_CARE.
 */
public void setState(State state) 
    model.setState(state);

/** Return the current state, which is determined by the
 * selection status of the model. */
public State getState()  
    return model.getState(); 


public void setSelected(boolean b) 
    if (b) 
        setState(SELECTED);         
     else 
        setState(NOT_SELECTED);         
    

/**
 * Exactly which Design Pattern is this?  Is it an Adapter,
 * a Proxy or a Decorator?  In this case, my vote lies with the
 * Decorator, because we are extending functionality and
 * "decorating" the original model with a more powerful model.
 */
private class TristateDecorator implements ButtonModel 
    private final ButtonModel other;
    private TristateDecorator(ButtonModel other) 
        this.other = other;
    
    private void setState(State state) 
        if (state == NOT_SELECTED) 
            other.setArmed(false);
            setPressed(false);
            setSelected(false);
         else if (state == SELECTED) 
            other.setArmed(false);
            setPressed(false);
            setSelected(true);
         else  // either "null" or DONT_CARE
            other.setArmed(true);
            setPressed(true);
            setSelected(false);
        
    
    /**
     * The current state is embedded in the selection / armed
     * state of the model.
     *
     * We return the SELECTED state when the checkbox is selected
     * but not armed, DONT_CARE state when the checkbox is
     * selected and armed (grey) and NOT_SELECTED when the
     * checkbox is deselected.
     */
    private State getState() 
        if (isSelected() && !isArmed()) 
            // normal black tick
            return SELECTED;
         else if (isSelected() && isArmed()) 
            // don't care grey tick
            return DONT_CARE;
         else 
            // normal deselected
            return NOT_SELECTED;
        
    

    /** We rotate between NOT_SELECTED, SELECTED and DONT_CARE.*/
    private void nextState() 
        State current = getState();
        if (current == NOT_SELECTED) 
            setState(SELECTED);
         else if (current == SELECTED) 
            setState(DONT_CARE);
         else if (current == DONT_CARE) 
            setState(NOT_SELECTED);
        
    

    /** Filter: No one may change the armed status except us. */
    public void setArmed(boolean b) 
    

    /** We disable focusing on the component when it is not
     * enabled. */
    public void setEnabled(boolean b) 
        setFocusable(b);
        other.setEnabled(b);
    

    /** All these methods simply delegate to the "other" model
     * that is being decorated. */
    public boolean isArmed()                              return other.isArmed(); 
    public boolean isSelected()                           return other.isSelected(); 
    public boolean isEnabled()                            return other.isEnabled(); 
    public boolean isPressed()                            return other.isPressed(); 
    public boolean isRollover()                           return other.isRollover(); 
    public int     getMnemonic()                          return other.getMnemonic(); 
    public String  getActionCommand()                     return other.getActionCommand();
    public Object[]getSelectedObjects()                   return other.getSelectedObjects();
    public void    setSelected(boolean b)                 other.setSelected(b);
    public void    setPressed(boolean b)                  other.setPressed(b);
    public void    setRollover(boolean b)                 other.setRollover(b);
    public void    setMnemonic(int key)                   other.setMnemonic(key);             
    public void    setActionCommand(String s)             other.setActionCommand(s);                      
    public void    setGroup(ButtonGroup group)            other.setGroup(group);      
    public void    addActionListener(ActionListener l)    other.addActionListener(l);
    public void    removeActionListener(ActionListener l) other.removeActionListener(l);      
    public void    addItemListener(ItemListener l)        other.addItemListener(l);       
    public void    removeItemListener(ItemListener l)     other.removeItemListener(l);    
    public void    addChangeListener(ChangeListener l)    other.addChangeListener(l);     
    public void    removeChangeListener(ChangeListener l) other.removeChangeListener(l);      



我们添加 CheckTreeManager 之后:

package com.demo.tree.checkbox;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Stack;

import javax.swing.JCheckBox;
import javax.swing.JPanel;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultTreeSelectionModel;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;


public class AddCheckBoxToTree 


public class CheckTreeSelectionModel extends DefaultTreeSelectionModel 
    static final  long serialVersionUID =0;
    private TreeModel model; 

    public CheckTreeSelectionModel(TreeModel model) 
        this.model = model; 

        setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION); 
           

    // tests whether there is any unselected node in the subtree of given path (DONT_CARE)
    public boolean isPartiallySelected(TreePath path)
        if(isPathSelected(path, true)) 
            return false; 
        

        TreePath[] selectionPaths = getSelectionPaths(); 

        if(selectionPaths==null)
            return false; 
        

        for(int j = 0; j<selectionPaths.length; j++) 
            if(isDescendant(selectionPaths[j], path))
                return true;
                                
         

        return false; 
     

    // tells whether given path is selected. 
    // if dig is true, then a path is assumed to be selected, if 
    // one of its ancestor is selected. 
    public boolean isPathSelected(TreePath path, boolean dig)                      
        if(!dig)
            return super.isPathSelected(path); 
        

        while(path!=null && !super.isPathSelected(path))
            path = path.getParentPath(); 
        

        return path!=null; 
     

    // is path1 descendant of path2 
    private boolean isDescendant(TreePath path1, TreePath path2) 
        Object obj1[] = path1.getPath(); 
        Object obj2[] = path2.getPath(); 
        for(int i = 0; i<obj2.length; i++) 
            if(obj1[i]!=obj2[i]) 
                return false; 
         
        return true; 
     

    public void setSelectionPaths(TreePath[] pPaths) 
        throw new UnsupportedOperationException("not implemented yet!!!"); 
     

    public void addSelectionPaths(TreePath[] paths) 

        // unselect all descendants of paths[] 
        for(int i = 0; i<paths.length; i++)
         
            TreePath path = paths[i]; 

            TreePath[] selectionPaths = getSelectionPaths(); 

            if(selectionPaths==null) 
                break; 
            

            ArrayList<TreePath> toBeRemoved = new ArrayList<TreePath>(); 

            for(int j = 0; j<selectionPaths.length; j++)
             
                if(isDescendant(selectionPaths[j], path))
                
                    toBeRemoved.add(selectionPaths[j]); 
                
             
            super.removeSelectionPaths((TreePath[])toBeRemoved.toArray(new TreePath[0])); 
         

        // if all siblings are selected then unselect them and select parent recursively 
        // otherwize just select that path. 
        for(int i = 0; i<paths.length; i++) 
            TreePath path = paths[i]; 

            TreePath temp = null; 

            while(areSiblingsSelected(path)) 
                temp = path; 

                if(path.getParentPath()==null)
                 
                    break; 
                

                path = path.getParentPath(); 
             

            if(temp!=null) 
                if(temp.getParentPath()!=null)
                 
                    addSelectionPath(temp.getParentPath());
                
                else
                 
                    if(!isSelectionEmpty())
                     
                        removeSelectionPaths(getSelectionPaths()); 
                    

                    super.addSelectionPaths(new TreePath[]temp); 
                                   
            
            else 
               
                super.addSelectionPaths(new TreePath[] path); 
            
         
     

    // tells whether all siblings of given path are selected. 
    private boolean areSiblingsSelected(TreePath path) 
        TreePath parent = path.getParentPath(); 

        if(parent==null) 
            return true; 
        

        Object node = path.getLastPathComponent(); 

        Object parentNode = parent.getLastPathComponent(); 

        int childCount = model.getChildCount(parentNode); 

        Boolean isParameters = false;
        Boolean isDescription = false;

        for(int i = 0; i<childCount; i++) 

            Object childNode = model.getChild(parentNode, i); 



            if(childNode==node) 
                continue;
            

// If last Path component equals to "parameters" or "description" - select second child too. 
            if(childCount == 2)
            
                if(childNode.toString().equals("parameters") && model.isLeaf(childNode))
                
                    isParameters = true;
                
                if(childNode.toString().equals("description") && model.isLeaf(childNode))
                
                    isDescription = true;
                
            


            if(!isPathSelected(parent.pathByAddingChild(childNode)) && !isParameters && !isDescription) 
                return false; 
            
        

        return true; 
     

    public void removeSelectionPaths(TreePath[] paths) 
        for(int i = 0; i<paths.length; i++) 
            TreePath path = paths[i]; 
            if(path.getPathCount()==1) 
                super.removeSelectionPaths(new TreePath[] path); 
            else 
                toggleRemoveSelection(path); 
         
     

    /** if any ancestor node of given path is selected then unselect it 
     *  and selection all its descendants except given path and descendants. 
     * otherwise just unselect the given path */
    private void toggleRemoveSelection(TreePath path) 

        Stack<TreePath> stack = new Stack<TreePath>(); 
        TreePath parent = path.getParentPath(); 

        Boolean isParameters = false;
        Boolean isDescription = false;

        while(parent!=null && !isPathSelected(parent)) 
            stack.push(parent); 
            parent = parent.getParentPath(); 
         
        if(parent!=null) 
            stack.push(parent); 
        else 
            super.removeSelectionPaths(new TreePath[]path); 
            return; 
         

        while(!stack.isEmpty()) 
            TreePath temp = (TreePath)stack.pop(); 

            TreePath peekPath = stack.isEmpty() ? path : (TreePath)stack.peek(); 

            Object node = temp.getLastPathComponent(); 
            Object peekNode = peekPath.getLastPathComponent(); 
            int childCount = model.getChildCount(node); 


            for(int i = 0; i<childCount; i++) 
                Object childNode = model.getChild(node, i); 

                if(childNode.toString().equals("parameters") && model.isLeaf(childNode))
                
                    isParameters = true;
                
                if(childNode.toString().equals("description")  && model.isLeaf(childNode))
                
                    isDescription = true;
                


                if(childNode!=peekNode)
                
                    if(!isParameters && !isDescription)
                    super.addSelectionPaths(new TreePath[]temp.pathByAddingChild(childNode)); 
                
             
         

        super.removeSelectionPaths(new TreePath[]parent); 
    

    public TreeModel getModel() 
        return model;
     



public class CheckTreeCellRenderer extends JPanel implements TreeCellRenderer  

    static final  long serialVersionUID =0;

    CheckTreeSelectionModel selectionModel; 
    private TreeCellRenderer delegate; 
    TristateCheckBox checkBox = new TristateCheckBox(); 





    public CheckTreeCellRenderer(TreeCellRenderer delegate, CheckTreeSelectionModel selectionModel) 
        this.delegate = delegate; 
        this.selectionModel = selectionModel; 

        setLayout(new BorderLayout()); 
        setOpaque(false); 
        checkBox.setOpaque(false);      

     


    public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) 
        Component renderer = delegate.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus); 


        TreePath path = tree.getPathForRow(row);            


        if(path!=null)
                           
            if(selectionModel.isPathSelected(path, true))
                               
                checkBox.setState(checkBox.SELECTED);   
                //System.out.println(">>>>>>     selected   " );
            
            else
                                   
                checkBox.setState(checkBox.NOT_SELECTED);   
                //System.out.println("not selected   ");
            

            if(selectionModel.isPartiallySelected(path))
            
                checkBox.setState(checkBox.DONT_CARE);                  
            
         

        removeAll();        

            add(checkBox, BorderLayout.WEST); 
            add(renderer, BorderLayout.CENTER);     


        return this; 
    


    public TreeCellRenderer getDelegate() 
        return delegate;
    


    public void setDelegate(TreeCellRenderer delegate) 
        this.delegate = delegate;
     
 


public class CheckTreeManager extends MouseAdapter implements TreeSelectionListener 
    CheckTreeSelectionModel selectionModel; 
    private JTree tree = new JTree(); 

    int hotspot = new JCheckBox().getPreferredSize().width; 

    public CheckTreeManager(JTree tree, CheckTreeSelectionModel checkTreeSelectionModel) 
        this.tree = tree;           


        if(checkTreeSelectionModel != null)
        
            //selectionModel = new CheckTreeSelectionModel(tree.getModel()); 
            selectionModel =  checkTreeSelectionModel;

        
        else
        
            selectionModel = new CheckTreeSelectionModel(tree.getModel()); 
            //System.out.println(selectionModel.getSelectionPath());
        



        tree.setCellRenderer(new CheckTreeCellRenderer(tree.getCellRenderer(), selectionModel)); 

        tree.addMouseListener(this); 
        selectionModel.addTreeSelectionListener(this); 
     

    public void mouseClicked(MouseEvent me) 
        //System.out.println("start...");

        TreePath path = tree.getPathForLocation(me.getX(), me.getY()); 
        //System.out.println(Arrays.asList(path));


        if(path==null) 
        
            //System.out.println("path==null");
            return;     
        

        if(me.getX()/1.2>tree.getPathBounds(path).x+hotspot)
        
            //System.out.println("me.getX()/1.2>tree.getPathBounds(path).x+hotspot");
            return; 
        


        boolean selected = selectionModel.isPathSelected(path, true); 
        selectionModel.removeTreeSelectionListener(this); 

        try 
            if(selected) 
            
                //System.out.println("selected");
                selectionModel.removeSelectionPath(path); 
            
            else 
            
                //System.out.println("not selected");
                selectionModel.addSelectionPath(path);
            
         
        finally
         
            //System.out.println("finally");
            selectionModel.addTreeSelectionListener(this); 
            tree.treeDidChange(); 
         
     

    public CheckTreeSelectionModel getSelectionModel() 
        return selectionModel; 
     

    public void setSelectionModel(CheckTreeSelectionModel selectionModel) 
        this.selectionModel = selectionModel;
    

    public void valueChanged(TreeSelectionEvent e) 
        tree.treeDidChange(); 
           
    

最无能为力的方法是:

boolean com.demo.tree.checkbox.AddCheckBoxToTree.CheckTreeSelectionModel.isPartiallySelected

检查给定路径(DONT_CARE)的子树中是否有任何未选择的节点。

这是我们所做的结果:


此答案基于 Santhosh Kumar's Weblog 帖子并进行了一些小修复。

【讨论】:

当心:您的扩展监听器修改了模型的基础数据关闭 EDT!通过从包含 fileNode(作为 userObject)的 treeNode 中删除/添加子节点,在 fileNode.expand 中发生 @AndrewThompson 是的,谢谢,这个 Q 和 A 是我在 SO 上的第一个问题;D @Desolator 查看上面发布的所有代码,它也包含FileTreeViewer @MaximShoustin 我如何setSelected(true) 一些复选框?我想写一个selectAll\deSelectAll方法 我没有在 JTree 上覆盖 getToolTipText(),而是尝试在 CheckTreeCellRenderer#getTreeCellRendererComponent() 方法中设置工具提示文本,因为我想根据某些条件设置工具提示。但它不起作用。有人可以帮我吗?

以上是关于如何将复选框添加到 JTree 节点以管理多选?的主要内容,如果未能解决你的问题,请参考以下文章

使用 JCheckBox 节点进行 JTree 渲染

Java Swing:需要具有复选框的高质量开发 JTree

Java:如何以编程方式选择和扩展 JTree 中的多个节点?

如何的制作带有多选框的treeview?

phpcms模型管理如何支持联动菜单多选?

如何在 Swift 中保存复选标记?