2012-10-13 163 views

Respuesta

15

Este es un ejemplo completo que muestra cómo agregar una casilla de verificación al nodo Jtree. Usé JTree con nodos basados ​​en el contenido del sistema de archivos.

Usé también la clase AddCheckBoxToTree.CheckTreeManager para administrar las opciones de selección o semi selección.

Uso

public AddCheckBoxToTree.CheckTreeManager getCheckTreeManager() { 
    return checkTreeManager; 
} 

método para jugar con el camino de árbol de selección.

por ejemplo:

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

aquí me pegan todo el código que hacen eso:

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; 
    } 
} 
} 

Hasta ahora todo era bueno y claro. Ahora voy a pegar Controlador de selección, aquí vamos:

Aquí creamos elemento de opción para añadir después a nodo del árbol

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

} 
} 

Después añadimos 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(); 
    }  
}  
} 

El el método más impotente es:

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

que comprueba si hay una y nodo no seleccionado en el subárbol de la ruta determinada (DONT_CARE).

Y este es el resultado de lo que hemos hecho: And this is the result of what we did:


Esta respuesta se basa en Santhosh Kumar's Weblog post con correcciones menores.

+3

cuidado: your expansionListener modifica los datos subyacentes del modelo _off_ el EDT! Ocurre en fileNode.expand al eliminar/agregar elementos secundarios de treeNode que contiene el fileNode (como userObject) – kleopatra

+0

Los iconos se ven más naturales usando el PLAF nativo. –

+0

@AndrewThompson Sí, gracias, esta Q y A fue mi primera vez en SO ; D –

Cuestiones relacionadas