`
helin
  • 浏览: 151060 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

JTree with CheckBox

    博客分类:
  • java
阅读更多
Let me explain what is "JTree with CheckBoxes".

Each node of the JTree contains a CheckBox.
When a node's checkbox is selected, all its descendants should also get checked and vice versa.
When all descendants of a node are not checked but some of them are checked (i.e, partial selection) then the node should be checked with a gray tick.
When all children of a node are unselected, the node should unselected.
If you google for "JTree CheckBox" you will get :
  http://www.java2s.com/ExampleCode/Swing-Components/CheckboxNodeTreeExample.htm

There are many drawbacks in the above implementation. The major drawback is, It restricts to use DefaultTreeModel whose nodes are CheckNode.

JTree should have two selections: one is normal selection and another is checkSelection (checkbox selected)

Why don't we use TreeSelectionModel to maintain checkSelection. But we can't use DefaultTreeSelection as-it-is without any changes. because we have to restrict the selection as explained in the above points:

In our check selection model, it contains only the selected node but not all its descendants. i.e. let us say Node A has children B, C, D;
When user checks Node A, the tree shows B, C and D also checked. But the selectionModel actually contains only A,

// @author Santhosh Kumar T - santhosh@in.fiorano.com 
public class CheckTreeSelectionModel extends DefaultTreeSelectionModel{ 
  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 
  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!=obj2) 
          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; 
        TreePath[] selectionPaths = getSelectionPaths(); 
        if(selectionPaths==null) 
          break; 
        ArrayList toBeRemoved = new ArrayList(); 
        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; 
        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); 
    for(int i = 0; i<childCount; i++){ 
        Object childNode = model.getChild(parentNode, i); 
        if(childNode==node) 
          continue; 
        if(!isPathSelected(parent.pathByAddingChild(childNode))) 
          return false; 
    } 
    return true; 
  } 

  public void removeSelectionPaths(TreePath[] paths){ 
    for(int i = 0; i<paths.length; i++){ 
        TreePath path = paths; 
        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 stack = new Stack(); 
    TreePath parent = path.getParentPath(); 
    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!=peekNode) 
            super.addSelectionPaths(new TreePath[]{temp.pathByAddingChild(childNode)}); 
        } 
    } 
    super.removeSelectionPaths(new TreePath[]{parent}); 
  } 
} 

You can ask the the above selection model whether B is selected as below:
  checkSelectionModel.isPathSelected(getPath(B), true); // the second argument tells that selection is to be digged.

Please take time to understand the above code (It is little bit complex, I guess).

As usual we attacked the model. next is renderer, In the google link I specified in the beginning restricts you to use its renderer. But in our implementation we let user add checkbox to any TreeCellRenderer:

public class CheckTreeCellRenderer extends JPanel implements TreeCellRenderer{ 
  private CheckTreeSelectionModel selectionModel; 
  private TreeCellRenderer delegate; 
  private 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(Boolean.TRUE); 
        else 
          checkBox.setState(selectionModel.isPartiallySelected(path) ? null : Boolean.FALSE); 
    } 
    removeAll(); 
    add(checkBox, BorderLayout.WEST); 
    add(renderer, BorderLayout.CENTER); 
    return this; 
  } 
} 
The above renderer can wrap any of your TreeCellRenderer implementation.
I used TristateCheckBox from http://www.javaspecialists.co.za/archive/Issue082.html

We can also implement editor in the same way such that it can wrap any TreeCellEditor implementation. But I am leaving it as exercise for you

The next question comes is:
  We have SelectionModel and renderer. But who will listen of mouseclicks in checkbox and update the selectionmodel ???
Here is that class, CheckTreeManager:

public class CheckTreeManager extends MouseAdapter implements TreeSelectionListener{ 
  private CheckTreeSelectionModel selectionModel; 
  private JTree tree = new JTree(); 
  int hotspot = new JCheckBox().getPreferredSize().width; 

  public CheckTreeManager(JTree tree){ 
    this.tree = tree; 
    selectionModel = new CheckTreeSelectionModel(tree.getModel()); 
    tree.setCellRenderer(new CheckTreeCellRenderer(tree.getCellRenderer(), selectionModel)); 
    tree.addMouseListener(this); 
    selectionModel.addTreeSelectionListener(this); 
  } 

  public void mouseClicked(MouseEvent me){ 
    TreePath path = tree.getPathForLocation(me.getX(), me.getY()); 
    if(path==null) 
        return; 
    if(me.getX()>tree.getPathBounds(path).x+hotspot) 
        return; 

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

    try{ 
        if(selected) 
          selectionModel.removeSelectionPath(path); 
        else 
          selectionModel.addSelectionPath(path); 
    } finally{ 
        selectionModel.addTreeSelectionListener(this); 
        tree.treeDidChange(); 
    } 
  } 

  public CheckTreeSelectionModel getSelectionModel(){ 
    return selectionModel; 
  } 

  public void valueChanged(TreeSelectionEvent e){ 
    tree.treeDidChange(); 
  } 
} 
It listens for mouse clicks in the hotspot (inside checkbox) and updates the selection
updates the tree (repaints) when checkselection is changed
listens for selectionmodel changes and updates the view. Because it allows you tocheck a node programmatically..
How to use this:

// makes your tree as CheckTree
CheckTreeManager checkTreeManager = new CheckTreeManager(yourTree);

// to get the paths that were checked
TreePath checkedPaths[] = checkTreeManager.getSelectionModel().getSelectionPaths();

See how powerful our solution is:

You can convert any JTree (with any TreeModel and any TreeCellRenderer) just by one line.
normal selection doesn't interfere with checkselection. In the google link i specified, when you select a node, it gets checked.
you can reuse your renderers/editors


The webstart demo shows the selection at the bottom of the window in a JLable. This lets you understand how our CheckSelectionModel is updated with your changes to checkselection.
分享到:
评论

相关推荐

    JTree with heckbox and TextField

    Jtree with checkbox and textField

    JTree(节点带有checkbox)代码实例2

    在特定场景下,我们可能希望在`JTree`的节点上添加复选框(CheckBox),以便用户可以进行多选操作。在本示例中,我们将深入探讨如何实现`JTree`与复选框结合的代码实例。 首先,我们需要创建一个自定义的`...

    JTree(节点带有checkbox)代码实例

    JFrame frame = new JFrame("JTree with CheckBoxes"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.getContentPane().add(new JScrollPane(tree)); frame.pack(); frame.setVisible(true); ...

    (一)如何使用JTree

    在Java Swing库中,JTree组件是一个非常有用的可视化控件,用于展示层次化的数据结构,如文件系统目录或组织结构图。这篇博文将探讨如何在Java应用程序中有效地使用JTree。 首先,理解JTree的基本概念至关重要。...

    JTree with JCheckBox的资料整理

    在Java Swing库中,`JTree`是一个组件,用于显示层次结构的数据,而`JCheckBox`则是一个可以勾选或取消勾选的图形用户界面元素。将`JCheckBox`与`JTree`结合使用,可以在树形结构的节点上提供复选框功能,允许用户...

    jtree

    在Java的Swing库中,`JTree`组件是一个用于展示层次数据的控件,它以树状结构显示数据。`jtree`标题所指的可能是关于如何在`JTree`中添加复选框(checkboxes)的功能。这通常是为了让用户能够多选树中的节点,进行...

    如何使用JTree,JTree使用方法

    ### 如何使用JTree:详解与实践 #### JTree简介 `JTree`是Java Swing中的一个组件,用于展示层次化的数据结构。它允许用户在应用程序中构建树形视图来表示目录结构、组织结构图等。`JTree`继承自`JComponent`类,...

    jTree和jTable 双向联动

    实现jTable和jTree的双向联动,点击jTree会选中jTable单元格,点击jTable会选中jTree节点。

    JTree的例子,可以拖动节点

    在Java Swing库中,`JTree`是一个非常重要的组件,用于显示和操作树形数据结构。这个例子展示了如何实现一个可拖动节点的`JTree`,这在用户界面设计中是一个常见且实用的功能,特别是在需要展示层次结构信息时。`...

    JAVA JTREE JTABLE实例代码

    在Java Swing库中,JTree和JTable是两个非常重要的组件,它们被广泛用于构建用户界面,特别是处理数据展示和交互。JTree用于显示层次结构的数据,而JTable则适用于二维表格形式的数据展现。 首先,我们来深入了解...

    JTree

    **JTree 深度解析** `JTree` 是Java Swing库中的一个重要组件,它用于在用户界面中显示树状数据结构。这个组件允许用户以图形化的方式浏览和操作层次结构的数据,例如文件系统目录、数据库结构或者组织结构图。在...

    Java JTree+JTable应用

    在Java Swing库中,`JTree`和`JTable`是两种非常重要的组件,用于创建用户界面。`JTree`通常用于展示层次化的数据结构,而`JTable`则适用于二维表格数据的显示和操作。本项目是关于如何在Java应用程序中结合使用这两...

    JTree的实现代码

    在IT领域,尤其是在Web开发中,`JTree`是一个常用组件,它用于在用户界面中展示数据的层次结构,比如文件系统、组织架构等。在本案例中,我们讨论的是一个使用JavaScript实现的JTree,这通常是通过HTML、CSS和...

    java组件之jtree使用

    ### Java组件之JTree使用详解 #### 一、引言 `JTree`是Java Swing中的一个重要组件,用于展示层次化的数据结构。它提供了一种直观的方式来表示具有层级关系的数据,例如文件系统的目录结构或者组织架构等。本文将...

    JTree的Doc文档

    **JTree的Doc文档详解** Java Swing库中的`JTree`组件是用于显示和操作树状数据结构的可视化工具。这个组件允许用户以图形化的方式查看层次化的信息,如文件系统目录、数据库结构或者应用程序的对象模型。在Java...

    Jtree

    在IT领域,JTree是一种广泛使用的Java Swing组件,它允许用户以树形结构来展示数据。这个组件在GUI(图形用户界面)应用中特别常见,因为它能清晰地组织层次化的信息,比如文件系统目录、数据库结构或者项目组织。...

    JTree用法大全 swing java

    ### JTree用法详解:Swing中的树状结构控件 在Java Swing中,`JTree`是一个非常重要的组件,用于展示层次结构的数据,如文件系统、组织结构图等。本文将详细介绍`JTree`的各种使用方法,包括如何创建、自定义外观、...

    JTree2.0 目录树

    **JTree 2.0:构建直观的目录树视图** `JTree`是Java Swing库中的一个组件,用于在用户界面中展示层次结构数据,通常表现为目录树的形式。在`JTree2.0`版本中,这个组件得到了进一步的改进和增强,提供更丰富的功能...

    java使用jtree动态实现二叉树

    在Java编程中,`JTree`是Swing库中的一个组件,用于展示树形数据结构。这个组件在GUI(图形用户界面)应用中非常常见,因为它可以清晰地展示层次关系的数据,比如文件系统目录结构。在Java中动态实现二叉树,即在...

    JTree的用法

    ### JTree的用法详解 #### 一、引言 `JTree` 是 Java Swing 库中的一个重要组件,用于在图形用户界面 (GUI) 中显示层次结构数据。它非常适合用来展示具有分层关系的信息,例如文件系统目录结构、组织结构等。 ####...

Global site tag (gtag.js) - Google Analytics