zoukankan      html  css  js  c++  java
  • [转载] Java CheckBoxTree

        代码基本上是copy的。只是在使用上有一些自己的想法。

        先上code吧! 虽然别的地方也有。但是还是转一份给自己。

        出处:http://blog.csdn.net/joy_125/article/details/20397869

        下面这段是直接摘抄的:

        1.在模型层上,CheckBoxTree的每个结点需要一个成员来保存其是否被选中,但是JTree的结点则不需要。
        2.在视图层上,CheckBoxTree的每个结点比JTree的结点多显示一个复选框。
        既然存在两个差异,那么只要我们把这两个差异部分通过自己的实现填补上,那么带复选框的树也就实现了。
        现在开始解决第一个差异。为了解决第一个差异,需要定义一个新的结点类CheckBoxTreeNode,该类继承DefaultMutableTreeNode,并增加新的成员isSelected来表示该结点是否被选中。对于一颗CheckBoxTree,如果某一个结点被选中的话,其复选框会勾选上,并且使用CheckBoxTree的动机在于可以一次性地选中一颗子树。那么,在选中或取消一个结点时,其祖先结点和子孙结点应该做出某种变化。在此,我们应用如下递归规则:
            1.如果某个结点被手动选中,那么它的所有子孙结点都应该被选中;如果选中该结点使其父节点的所有子结点都被选中,则选中其父结点。
            2.如果某个结点被手动取消选中,那么它的所有子孙结点都应该被取消选中;如果该结点的父结点处于选中状态,则取消选中其父结点。
            注意:上面的两条规则是递归规则,当某个结点发生变化,导致另外的结点发生变化时,另外的结点也会导致其他的结点发生变化。
            在上面两条规则中,强调手动,是因为手动选中或者手动取消选中一个结点,会导致其他结点发生非手动的选中或者取消选中,
            这种非手动导致的选中或者非取消选中则不适用于上述规则。

        按照上述规则实现的CheckBoxTreeNode源代码如下:

      1 package CheckBoxTree;
      2 import javax.swing.tree.DefaultMutableTreeNode;
      3 import java.util.Vector;
      4 
      5 import javax.swing.tree.*;
      6 import java.util.*;
      7 
      8 
      9 public class CheckBoxTreeNode extends DefaultMutableTreeNode implements Comparable
     10 { 
     11     protected boolean isSelected; 
     12      
     13     public CheckBoxTreeNode() 
     14     { 
     15         this(null); 
     16     } 
     17      
     18     public CheckBoxTreeNode(Object userObject) 
     19     { 
     20         this(userObject, true,false); 
     21     } 
     22     
     23     public void add(MutableTreeNode childNode)
     24     {
     25         super.add(childNode);
     26         Collections.sort(super.children);
     27     }
     28     
     29     public int compareTo(Object o)
     30     {
     31         return this.toString().compareTo(o.toString());
     32     }
     33     
     34     public CheckBoxTreeNode(Object userObject,boolean allowsChildren, boolean isSelected) 
     35     { 
     36         super(userObject, allowsChildren); 
     37         this.isSelected = isSelected; 
     38     } 
     39  
     40     public boolean isSelected() 
     41     { 
     42         return isSelected; 
     43     }
     44     
     45     public Object[] getChildNode()
     46     {
     47         if(children !=null)
     48         {
     49             return children.toArray();
     50         }
     51         
     52         return null;
     53     }
     54      
     55     public void setSelected(boolean _isSelected) 
     56     { 
     57         this.isSelected = _isSelected; 
     58          
     59         if(_isSelected) 
     60         { 
     61             // 如果选中,则将其所有的子结点都选中 
     62             if(children !=null) 
     63             { 
     64                 for(Object obj : children) 
     65                 { 
     66                     CheckBoxTreeNode node = (CheckBoxTreeNode)obj; 
     67                     if(_isSelected != node.isSelected()) 
     68                         node.setSelected(_isSelected); 
     69                 } 
     70             } 
     71             // 向上检查,如果父结点的所有子结点都被选中,那么将父结点也选中 
     72             CheckBoxTreeNode pNode = (CheckBoxTreeNode)parent; 
     73             // 开始检查pNode的所有子节点是否都被选中 
     74             if(pNode != null) 
     75             { 
     76                 int index =0; 
     77                 for(; index < pNode.children.size(); ++ index) 
     78                 { 
     79                     CheckBoxTreeNode pChildNode = (CheckBoxTreeNode)pNode.children.get(index); 
     80                     if(!pChildNode.isSelected()) 
     81                         break; 
     82                 } 
     83                 /*
     84                  * 表明pNode所有子结点都已经选中,则选中父结点,
     85                  * 该方法是一个递归方法,因此在此不需要进行迭代,因为
     86                  * 当选中父结点后,父结点本身会向上检查的。
     87                  */ 
     88                 if(index == pNode.children.size()) 
     89                 { 
     90                     if(pNode.isSelected() != _isSelected) 
     91                         pNode.setSelected(_isSelected); 
     92                 } 
     93             } 
     94         } 
     95         else  
     96         { 
     97             /*
     98              * 如果是取消父结点导致子结点取消,那么此时所有的子结点都应该是选择上的;
     99              * 否则就是子结点取消导致父结点取消,然后父结点取消导致需要取消子结点,但
    100              * 是这时候是不需要取消子结点的。
    101              */ 
    102             if(children !=null) 
    103             { 
    104                 int index =0; 
    105                 for(; index < children.size(); ++ index) 
    106                 { 
    107                     CheckBoxTreeNode childNode = (CheckBoxTreeNode)children.get(index); 
    108                     if(!childNode.isSelected()) 
    109                         break; 
    110                 } 
    111                 // 从上向下取消的时候 
    112                 if(index == children.size()) 
    113                 { 
    114                     for(int i =0; i < children.size(); ++ i) 
    115                     { 
    116                         CheckBoxTreeNode node = (CheckBoxTreeNode)children.get(i); 
    117                         if(node.isSelected() != _isSelected) 
    118                             node.setSelected(_isSelected); 
    119                     } 
    120                 } 
    121             } 
    122              
    123             // 向上取消,只要存在一个子节点不是选上的,那么父节点就不应该被选上。 
    124             CheckBoxTreeNode pNode = (CheckBoxTreeNode)parent; 
    125             if(pNode != null && pNode.isSelected() != _isSelected) 
    126                 pNode.setSelected(_isSelected); 
    127         } 
    128     } 
    129 }  

        第一个差异通过继承DefaultMutableTreeNode定义CheckBoxTreeNode解决了,接下来需要解决第二个差异。第二个差异是外观上的差异,JTree的每个结点是通过TreeCellRenderer进行显示的。为了解决第二个差异,我们定义一个新的类CheckBoxTreeCellRenderer。
    该类实现了TreeCellRenderer接口。CheckBoxTreeRenderer的源代码如下:

     1 package CheckBoxTree;
     2 import java.awt.Color; 
     3 import java.awt.Component; 
     4 import java.awt.Dimension; 
     5  
     6 import javax.swing.JCheckBox; 
     7 import javax.swing.JPanel; 
     8 import javax.swing.JTree; 
     9 import javax.swing.UIManager; 
    10 import javax.swing.plaf.ColorUIResource; 
    11 import javax.swing.tree.TreeCellRenderer; 
    12  
    13 public class CheckBoxTreeCellRenderer extends JPanel implements TreeCellRenderer 
    14 { 
    15     protected JCheckBox check; 
    16     protected CheckBoxTreeLabel label; 
    17      
    18     public CheckBoxTreeCellRenderer() 
    19     { 
    20         setLayout(null); 
    21         add(check = new JCheckBox()); 
    22         add(label = new CheckBoxTreeLabel()); 
    23         check.setBackground(UIManager.getColor("Tree.textBackground")); 
    24         label.setForeground(UIManager.getColor("Tree.textForeground")); 
    25     } 
    26      
    27     /**
    28      * 返回的是一个<code>JPanel</code>对象,该对象中包含一个<code>JCheckBox</code>对象
    29      * 和一个<code>JLabel</code>对象。并且根据每个结点是否被选中来决定<code>JCheckBox</code>
    30      * 是否被选中。
    31      */ 
    32     @Override 
    33     public Component getTreeCellRendererComponent(JTree tree, Object value, 
    34             boolean selected,boolean expanded, boolean leaf,int row, 
    35             boolean hasFocus) 
    36     { 
    37         String stringValue = tree.convertValueToText(value, selected, expanded, leaf, row, hasFocus); 
    38         setEnabled(tree.isEnabled()); 
    39         check.setSelected(((CheckBoxTreeNode)value).isSelected()); 
    40         label.setFont(tree.getFont()); 
    41         label.setText(stringValue); 
    42         label.setSelected(selected); 
    43         label.setFocus(hasFocus); 
    44         if(leaf) 
    45             label.setIcon(UIManager.getIcon("Tree.leafIcon")); 
    46         else if(expanded) 
    47             label.setIcon(UIManager.getIcon("Tree.openIcon")); 
    48         else 
    49             label.setIcon(UIManager.getIcon("Tree.closedIcon")); 
    50              
    51         return this; 
    52     } 
    53  
    54     @Override 
    55     public Dimension getPreferredSize() 
    56     { 
    57         Dimension dCheck = check.getPreferredSize(); 
    58         Dimension dLabel = label.getPreferredSize(); 
    59         return new Dimension(dCheck.width + dLabel.width, dCheck.height < dLabel.height ? dLabel.height: dCheck.height); 
    60     } 
    61      
    62     @Override 
    63     public void doLayout() 
    64     { 
    65         Dimension dCheck = check.getPreferredSize(); 
    66         Dimension dLabel = label.getPreferredSize(); 
    67         int yCheck = 0; 
    68         int yLabel = 0; 
    69         if(dCheck.height < dLabel.height) 
    70             yCheck = (dLabel.height - dCheck.height) / 2; 
    71         else 
    72             yLabel = (dCheck.height - dLabel.height) / 2; 
    73         check.setLocation(0, yCheck); 
    74         check.setBounds(0, yCheck, dCheck.width, dCheck.height); 
    75         label.setLocation(dCheck.width, yLabel); 
    76         label.setBounds(dCheck.width, yLabel, dLabel.width, dLabel.height); 
    77     } 
    78      
    79     @Override 
    80     public void setBackground(Color color) 
    81     { 
    82         if(color instanceof ColorUIResource) 
    83             color = null; 
    84         super.setBackground(color); 
    85     } 
    86 }  

        在CheckBoxTreeCellRenderer的实现中,getTreeCellRendererComponent方法返回的是JPanel,而不是像DefaultTreeCellRenderer那样返回JLabel,
        因此JPanel中的JLabel无法对选中做出反应,因此我们重新实现了一个JLabel的子类CheckBoxTreeLabel,它可以对选中做出反应,其源代码如下:

       

     1 package CheckBoxTree;
     2 import java.awt.Color; 
     3 import java.awt.Dimension; 
     4 import java.awt.Graphics; 
     5  
     6 import javax.swing.Icon; 
     7 import javax.swing.JLabel; 
     8 import javax.swing.UIManager; 
     9 import javax.swing.plaf.ColorUIResource; 
    10  
    11 public class CheckBoxTreeLabel extends JLabel 
    12 { 
    13     private boolean isSelected; 
    14     private boolean hasFocus; 
    15      
    16     public CheckBoxTreeLabel() 
    17     { 
    18     } 
    19      
    20     @Override 
    21     public void setBackground(Color color) 
    22     { 
    23         if(color instanceof ColorUIResource) 
    24             color = null; 
    25         super.setBackground(color); 
    26     } 
    27      
    28     @Override 
    29     public void paint(Graphics g) 
    30     { 
    31         String str; 
    32         if((str = getText()) !=null) 
    33         { 
    34             if(0 < str.length()) 
    35             { 
    36                 if(isSelected) 
    37                     {
    38                         g.setColor(UIManager.getColor("Tree.selectionBackground"));  //选中的文字颜色
    39                         //System.out.println("XXXXX");
    40                     }
    41                 else 
    42                 {
    43                     g.setColor(UIManager.getColor("Tree.textBackground"));
    44                 }
    45                      
    46                 Dimension d = getPreferredSize(); 
    47                 int imageOffset = 0; 
    48                 Icon currentIcon = getIcon(); 
    49                 if(currentIcon != null) 
    50                     imageOffset = currentIcon.getIconWidth() + Math.max(0, getIconTextGap() -1); 
    51                 g.fillRect(imageOffset, 0, d.width -1 - imageOffset, d.height); 
    52                 if(hasFocus) 
    53                 { 
    54                     g.setColor(UIManager.getColor("Tree.selectionBorderColor")); 
    55                     g.drawRect(imageOffset, 0, d.width -1 - imageOffset, d.height - 1); 
    56                 } 
    57             } 
    58         } 
    59         super.paint(g); 
    60     } 
    61      
    62     @Override 
    63     public Dimension getPreferredSize() 
    64     { 
    65         Dimension retDimension = super.getPreferredSize(); 
    66         if(retDimension !=null) 
    67             retDimension = new Dimension(retDimension.width +3, retDimension.height); 
    68         return retDimension; 
    69     } 
    70      
    71     public void setSelected(boolean isSelected) 
    72     { 
    73         this.isSelected = isSelected; 
    74     } 
    75      
    76     public void setFocus(boolean hasFocus) 
    77     { 
    78         this.hasFocus = hasFocus; 
    79     } 
    80 }  

        通过定义CheckBoxTreeNode和CheckBoxTreeCellRenderer。我们解决了CheckBoxTree和JTree的两个根本差异,但是还有一个细节问题需要解决,就是CheckBoxTree可以响应用户事件决定是否选中某个结点。为此,我们为CheckBoxTree添加一个响应用户鼠标事件的监听器CheckBoxTreeNodeSelectionListener,
        该类的源代码如下:

     1 package CheckBoxTree;
     2 import java.awt.event.MouseAdapter; 
     3 import java.awt.event.MouseEvent; 
     4  
     5 import javax.swing.JTree; 
     6 import javax.swing.tree.TreePath; 
     7 import javax.swing.tree.DefaultTreeModel; 
     8  
     9 public class CheckBoxTreeNodeSelectionListener extends MouseAdapter 
    10 { 
    11     @Override 
    12     public void mouseClicked(MouseEvent event) 
    13     { 
    14         JTree tree = (JTree)event.getSource(); 
    15         int x = event.getX(); 
    16         int y = event.getY(); 
    17         int row = tree.getRowForLocation(x, y); 
    18         System.out.println("XXXX " + tree.getLastSelectedPathComponent().toString() + " has been selected!(mouse)");
    19         TreePath path = tree.getPathForRow(row); 
    20         if(path != null) 
    21         { 
    22             CheckBoxTreeNode node = (CheckBoxTreeNode)path.getLastPathComponent(); 
    23             if(node != null) 
    24             { 
    25                 boolean isSelected = !node.isSelected(); 
    26                 node.setSelected(isSelected); 
    27                 ((DefaultTreeModel)tree.getModel()).nodeStructureChanged(node); 
    28             } 
    29         } 
    30     } 
    31 }  

        上述是原blog中的内容。下面是一些具体的使用心得。

        经过实际的测试发现,如果使用 CheckBoxTreeNodeSelectionListener 这个类来监控鼠标的点击动作在点击父节点以后,在点击任何结点都是会产生两次时间触发的。(偶现)我一直没找到问题根源。后来在使用的过程中用下面的代码代替了这个类的效果。

    1         tree.addMouseListener(new MouseAdapter()
    2         {
    3             public void mouseClicked(MouseEvent e)
    4             {
    5                 treeMouseClicked(e);
    6             }
    7         });
     1 private void treeMouseClicked(MouseEvent event)
     2 {
     3         JTree tree = (JTree)event.getSource();
     4         int x = event.getX();
     5         int y = event.getY();
     6         int row = tree.getRowForLocation(x, y);
     7         TreePath path = tree.getPathForRow(row);
     8 
     9         if(null != path)
    10         {
    11             CheckBoxTreeNode node = (CheckBoxTreeNode)path.getLastPathComponent();
    12             if(null != node)
    13             {
    14                 boolean isSelected = !node.isSelected(); 
    15                 node.setSelected(isSelected); 
    16                 ((DefaultTreeModel)tree.getModel()).nodeStructureChanged(node); 
    17             } 
    18         }
    19 }

        其本质没有什么区别。只是原有的是由tree自带的调用。后面的是由自己指定函数调用。

        我把这个几个类放到一个CheckBoxTree目录下。

        下面是具体的调用过程:

      1 import CheckBoxTree.*;
      2 import java.awt.*;
      3 import javax.swing.JPanel;
      4 import javax.swing.JFrame;
      5 import javax.swing.JSplitPane;
      6 import javax.swing.JScrollPane;
      7 import javax.swing.JTree;
      8 import javax.swing.tree.DefaultTreeModel;
      9 import javax.swing.tree.TreePath;
     10 import javax.swing.tree.TreeSelectionModel;
     11 import java.awt.event.*;
     12 import javax.swing.event.*;
     13 
     14 public class WriteForBlog extends JFrame
     15 {
     16     private GridBagLayout gridBagLayout = new GridBagLayout();
     17     private JSplitPane splitPane = new JSplitPane();
     18     private GridBagLayout gridBagLayoutForCheckBoxTree = new GridBagLayout();
     19     private JPanel checkBoxTreePanel = new JPanel();
     20     private JTree tree = new JTree();
     21     private JScrollPane ScrollPaneForTree = new JScrollPane();
     22     private CheckBoxTreeNode checkBoxTreeNode = new CheckBoxTreeNode();
     23     private DefaultTreeModel defaultTreeModel = new DefaultTreeModel(checkBoxTreeNode);
     24     
     25     public WriteForBlog()
     26     {
     27         try
     28         {
     29             jbInit();
     30         }
     31         catch(Exception ex)
     32         {
     33             System.out.println(ex.getMessage());
     34         }
     35     }
     36     
     37     private void jbInit() throws Exception
     38     {
     39         this.setLayout(gridBagLayout);
     40         this.setBounds(200, 200, 1000, 600);
     41         
     42         checkBoxTreePanel.setLayout(gridBagLayoutForCheckBoxTree);
     43         splitPane.setLastDividerLocation(-1);
     44         tree.setModel(defaultTreeModel); 
     45         tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
     46         tree.addTreeSelectionListener(new TreeSelectionListener()
     47         {
     48             public void valueChanged(TreeSelectionEvent e)
     49             {
     50                 tree_valueChanged(e);
     51             }
     52         });
     53         
     54         tree.addMouseListener(new MouseAdapter()
     55         {
     56             public void mouseClicked(MouseEvent e)
     57             {
     58                 treeMouseClicked(e);
     59             }
     60         });
     61         
     62         this.add(splitPane,   new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0
     63                 ,GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5), 0, 0));
     64         splitPane.add(checkBoxTreePanel, JSplitPane.LEFT);
     65         splitPane.setDividerLocation(150);
     66         checkBoxTreePanel.add(ScrollPaneForTree,   new GridBagConstraints(0, 1, 1, 1, 1.0, 1.0
     67                 ,GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
     68         ScrollPaneForTree.getViewport().add(tree, null);
     69         
     70         this.loadTree();
     71         
     72     }
     73     
     74     private void loadTree()
     75     {
     76         CheckBoxTreeNode rootNode = (CheckBoxTreeNode) ( (DefaultTreeModel) tree.getModel()).getRoot(); 
     77         CheckBoxTreeNode node1 = new CheckBoxTreeNode("node_1"); 
     78         
     79         CheckBoxTreeNode node1_1 = new CheckBoxTreeNode("node_1_1"); 
     80         CheckBoxTreeNode node1_2 = new CheckBoxTreeNode("node_1_2"); 
     81         CheckBoxTreeNode node1_3 = new CheckBoxTreeNode("node_1_3"); 
     82         CheckBoxTreeNode node1_5 = new CheckBoxTreeNode("node_1_5"); 
     83         CheckBoxTreeNode node1_6 = new CheckBoxTreeNode("node_1_6"); 
     84         
     85         node1.add(node1_1); 
     86         node1.add(node1_2);
     87         node1.add(node1_3);
     88         node1.add(node1_6);
     89         node1.add(node1_5); 
     90         
     91         rootNode.add(node1);
     92         //rootNode.add(node2);
     93         
     94         DefaultTreeModel model = new DefaultTreeModel(node1);
     95         tree.expandPath(new TreePath(rootNode.getPath()));
     96         //tree.addMouseListener(new CheckBoxTreeNodeSelectionListener()); 
     97         tree.setModel(model); 
     98         tree.setCellRenderer(new CheckBoxTreeCellRenderer()); 
     99         tree.updateUI();
    100     }
    101     
    102     public static void main(String[] args)
    103     {
    104         WriteForBlog test = new WriteForBlog();
    105         test.setVisible(true);
    106     }
    107     
    108     private void tree_valueChanged(TreeSelectionEvent e)
    109     {
    110         
    111     }
    112     
    113     private void treeMouseClicked(MouseEvent event)
    114     {
    115         JTree tree = (JTree)event.getSource();
    116         int x = event.getX();
    117         int y = event.getY();
    118         int row = tree.getRowForLocation(x, y);
    119         TreePath path = tree.getPathForRow(row);
    120         if(path != null) 
    121         { 
    122             CheckBoxTreeNode node = (CheckBoxTreeNode)path.getLastPathComponent(); 
    123             if(node != null) 
    124             { 
    125                 boolean isSelected = !node.isSelected(); 
    126                 node.setSelected(isSelected); 
    127                 ((DefaultTreeModel)tree.getModel()).nodeStructureChanged(node); 
    128             } 
    129         }
    130     }
    131 }
  • 相关阅读:
    vuejs2从入门到精通视频教程
    vuejs2项目开发实战视频教程
    vuejs2从入门到精通视频教程
    Bootstrap视频教程
    开通博卡拉
    阿里云Ubuntu 16 FTP安装配置注意事项
    [解决方法] Java-Class.forName() 反射/映射子类 并转化为父类/接口
    [HTML/CSS] ul元素居中处理
    [HTML/Javascript] JS判断IE浏览器各版本
    [HTML/JS] JQuery 页面滚动回到顶部
  • 原文地址:https://www.cnblogs.com/AndyStudy/p/6039642.html
Copyright © 2011-2022 走看看