新西兰服务器

Java怎么实现带复选框的树


Java怎么实现带复选框的树

发布时间:2021-08-06 10:46:21 来源:高防服务器网 阅读:80 作者:小新 栏目:编程语言

小编给大家分享一下Java怎么实现带复选框的树,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!

在使用Java Swing开发UI程序时,很有可能会遇到使用带复选框的树的需求,但是Java Swing并没有提供这个组件,因此如果你有这个需求,你就得自己动手实现带复选框的树。

CheckBoxTree与JTree在两个层面上存在差异:

1.在模型层上,CheckBoxTree的每个结点需要一个成员来保存其是否被选中,但是JTree的结点则不需要。
2.在视图层上,CheckBoxTree的每个结点比JTree的结点多显示一个复选框。

既然存在两个差异,那么只要我们把这两个差异部分通过自己的实现填补上,那么带复选框的树也就实现了。
现在开始解决第一个差异。为了解决第一个差异,需要定义一个新的结点类CheckBoxTreeNode,该类继承DefaultMutableTreeNode,并增加新的成员isSelected来表示该结点是否被选中。对于一颗CheckBoxTree,如果某一个结点被选中的话,其复选框会勾选上,并且使用CheckBoxTree的动机在于可以一次性地选中一颗子树。那么,在选中或取消一个结点时,其祖先结点和子孙结点应该做出某种变化。在此,我们应用如下递归规则:

1.如果某个结点被手动选中,那么它的所有子孙结点都应该被选中;如果选中该结点使其父节点的所有子结点都被选中,则选中其父结点。
2.如果某个结点被手动取消选中,那么它的所有子孙结点都应该被取消选中;如果该结点的父结点处于选中状态,则取消选中其父结点。

注意:上面的两条规则是递归规则,当某个结点发生变化,导致另外的结点发生变化时,另外的结点也会导致其他的结点发生变化。在上面两条规则中,强调手动,是因为手动选中或者手动取消选中一个结点,会导致其他结点发生非手动的选中或者取消选中,这种非手动导致的选中或者非取消选中则不适用于上述规则。

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

package demo;      import javax.swing.tree.DefaultMutableTreeNode;      public class CheckBoxTreeNode extends DefaultMutableTreeNode   {    protected boolean isSelected;        public CheckBoxTreeNode()    {     this(null);    }        public CheckBoxTreeNode(Object userObject)    {     this(userObject, true, false);    }        public CheckBoxTreeNode(Object userObject, boolean allowsChildren, boolean isSelected)    {     super(userObject, allowsChildren);     this.isSelected = isSelected;    }       public boolean isSelected()    {     return isSelected;    }        public void setSelected(boolean _isSelected)    {     this.isSelected = _isSelected;          if(_isSelected)     {      // 如果选中,则将其所有的子结点都选中      if(children != null)      {       for(Object obj : children)       {        CheckBoxTreeNode node = (CheckBoxTreeNode)obj;        if(_isSelected != node.isSelected())         node.setSelected(_isSelected);       }      }      // 向上检查,如果父结点的所有子结点都被选中,那么将父结点也选中      CheckBoxTreeNode pNode = (CheckBoxTreeNode)parent;      // 开始检查pNode的所有子节点是否都被选中      if(pNode != null)      {       int index = 0;       for(; index < pNode.children.size(); ++ index)       {        CheckBoxTreeNode pChildNode = (CheckBoxTreeNode)pNode.children.get(index);        if(!pChildNode.isSelected())         break;       }       /*        * 表明pNode所有子结点都已经选中,则选中父结点,        * 该方法是一个递归方法,因此在此不需要进行迭代,因为        * 当选中父结点后,父结点本身会向上检查的。        */       if(index == pNode.children.size())       {        if(pNode.isSelected() != _isSelected)         pNode.setSelected(_isSelected);       }      }     }     else     {      /*       * 如果是取消父结点导致子结点取消,那么此时所有的子结点都应该是选择上的;       * 否则就是子结点取消导致父结点取消,然后父结点取消导致需要取消子结点,但       * 是这时候是不需要取消子结点的。       */      if(children != null)      {       int index = 0;       for(; index < children.size(); ++ index)       {        CheckBoxTreeNode childNode = (CheckBoxTreeNode)children.get(index);        if(!childNode.isSelected())         break;       }       // 从上向下取消的时候       if(index == children.size())       {        for(int i = 0; i < children.size(); ++ i)        {         CheckBoxTreeNode node = (CheckBoxTreeNode)children.get(i);         if(node.isSelected() != _isSelected)          node.setSelected(_isSelected);        }       }      }            // 向上取消,只要存在一个子节点不是选上的,那么父节点就不应该被选上。      CheckBoxTreeNode pNode = (CheckBoxTreeNode)parent;      if(pNode != null && pNode.isSelected() != _isSelected)       pNode.setSelected(_isSelected);     }    }   }

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

package demo;      import java.awt.Color;   import java.awt.Component;   import java.awt.Dimension;      import javax.swing.JCheckBox;   import javax.swing.JPanel;   import javax.swing.JTree;   import javax.swing.UIManager;   import javax.swing.plaf.ColorUIResource;   import javax.swing.tree.TreeCellRenderer;      public class CheckBoxTreeCellRenderer extends JPanel implements TreeCellRenderer   {    protected JCheckBox check;    protected CheckBoxTreeLabel label;        public CheckBoxTreeCellRenderer()    {     setLayout(null);     add(check = new JCheckBox());     add(label = new CheckBoxTreeLabel());     check.setBackground(UIManager.getColor("Tree.textBackground"));     label.setForeground(UIManager.getColor("Tree.textForeground"));    }        /**     * 返回的是一个<code>JPanel</code>对象,该对象中包含一个<code>JCheckBox</code>对象     * 和一个<code>JLabel</code>对象。并且根据每个结点是否被选中来决定<code>JCheckBox</code>     * 是否被选中。     */    @Override    public Component getTreeCellRendererComponent(JTree tree, Object value,      boolean selected, boolean expanded, boolean leaf, int row,      boolean hasFocus)    {     String stringValue = tree.convertValueToText(value, selected, expanded, leaf, row, hasFocus);     setEnabled(tree.isEnabled());     check.setSelected(((CheckBoxTreeNode)value).isSelected());     label.setFont(tree.getFont());     label.setText(stringValue);     label.setSelected(selected);     label.setFocus(hasFocus);     if(leaf)      label.setIcon(UIManager.getIcon("Tree.leafIcon"));     else if(expanded)      label.setIcon(UIManager.getIcon("Tree.openIcon"));     else      label.setIcon(UIManager.getIcon("Tree.closedIcon"));           return this;    }       @Override    public Dimension getPreferredSize()    {     Dimension dCheck = check.getPreferredSize();     Dimension dLabel = label.getPreferredSize();     return new Dimension(dCheck.width + dLabel.width, dCheck.height < dLabel.height ? dLabel.height: dCheck.height);    }        @Override    public void doLayout()    {     Dimension dCheck = check.getPreferredSize();     Dimension dLabel = label.getPreferredSize();     int yCheck = 0;     int yLabel = 0;     if(dCheck.height < dLabel.height)      yCheck = (dLabel.height - dCheck.height) / 2;     else      yLabel = (dCheck.height - dLabel.height) / 2;     check.setLocation(0, yCheck);     check.setBounds(0, yCheck, dCheck.width, dCheck.height);     label.setLocation(dCheck.width, yLabel);     label.setBounds(dCheck.width, yLabel, dLabel.width, dLabel.height);    }        @Override    public void setBackground(Color color)    {     if(color instanceof ColorUIResource)      color = null;     super.setBackground(color);    }   }

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

package demo;      import java.awt.Color;   import java.awt.Dimension;   import java.awt.Graphics;      import javax.swing.Icon;   import javax.swing.JLabel;   import javax.swing.UIManager;   import javax.swing.plaf.ColorUIResource;      public class CheckBoxTreeLabel extends JLabel   {    private boolean isSelected;    private boolean hasFocus;        public CheckBoxTreeLabel()    {    }        @Override    public void setBackground(Color color)    {     if(color instanceof ColorUIResource)      color = null;     super.setBackground(color);    }        @Override    public void paint(Graphics g)    {     String str;     if((str = getText()) != null)     {      if(0 < str.length())      {       if(isSelected)        g.setColor(UIManager.getColor("Tree.selectionBackground"));       else        g.setColor(UIManager.getColor("Tree.textBackground"));       Dimension d = getPreferredSize();       int imageOffset = 0;       Icon currentIcon = getIcon();       if(currentIcon != null)        imageOffset = currentIcon.getIconWidth() + Math.max(0, getIconTextGap() - 1);       g.fillRect(imageOffset, 0, d.width - 1 - imageOffset, d.height);       if(hasFocus)       {        g.setColor(UIManager.getColor("Tree.selectionBorderColor"));        g.drawRect(imageOffset, 0, d.width - 1 - imageOffset, d.height - 1);       }      }     }     super.paint(g);    }        @Override    public Dimension getPreferredSize()    {     Dimension retDimension = super.getPreferredSize();     if(retDimension != null)      retDimension = new Dimension(retDimension.width + 3, retDimension.height);     return retDimension;    }        public void setSelected(boolean isSelected)    {     this.isSelected = isSelected;    }        public void setFocus(boolean hasFocus)    {     this.hasFocus = hasFocus;    }   }

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

package demo;      import java.awt.event.MouseAdapter;   import java.awt.event.MouseEvent;      import javax.swing.JTree;   import javax.swing.tree.TreePath;   import javax.swing.tree.DefaultTreeModel;      public class CheckBoxTreeNodeSelectionListener extends MouseAdapter   {    @Override    public void mouseClicked(MouseEvent event)    {     JTree tree = (JTree)event.getSource();     int x = event.getX();     int y = event.getY();     int row = tree.getRowForLocation(x, y);     TreePath path = tree.getPathForRow(row);     if(path != null)     {      CheckBoxTreeNode node = (CheckBoxTreeNode)path.getLastPathComponent();      if(node != null)      {       boolean isSelected = !node.isSelected();       node.setSelected(isSelected);       ((DefaultTreeModel)tree.getModel()).nodeStructureChanged(node);      }     }    }   }

到此为止,CheckBoxTree所需要的所有组件都已经完成了,接下来就是如何使用这些组件。下面给出了使用这些组件的源代码:

package demo;      import javax.swing.JFrame;   import javax.swing.JScrollPane;   import javax.swing.JTree;   import javax.swing.tree.DefaultTreeModel;      public class DemoMain   {    public static void main(String[] args)    {     JFrame frame = new JFrame("CheckBoxTreeDemo");     frame.setBounds(200, 200, 400, 400);     JTree tree = new JTree();     CheckBoxTreeNode rootNode = new CheckBoxTreeNode("root");     CheckBoxTreeNode node1 = new CheckBoxTreeNode("node_1");     CheckBoxTreeNode node1_1 = new CheckBoxTreeNode("node_1_1");     CheckBoxTreeNode node1_2 = new CheckBoxTreeNode("node_1_2");     CheckBoxTreeNode node1_3 = new CheckBoxTreeNode("node_1_3");     node1.add(node1_1);     node1.add(node1_2);     node1.add(node1_3);     CheckBoxTreeNode node2 = new CheckBoxTreeNode("node_2");     CheckBoxTreeNode node2_1 = new CheckBoxTreeNode("node_2_1");     CheckBoxTreeNode node2_2 = new CheckBoxTreeNode("node_2_2");     node2.add(node2_1);     node2.add(node2_2);     rootNode.add(node1);     rootNode.add(node2);     DefaultTreeModel model = new DefaultTreeModel(rootNode);     tree.addMouseListener(new CheckBoxTreeNodeSelectionListener());     tree.setModel(model);     tree.setCellRenderer(new CheckBoxTreeCellRenderer());     JScrollPane scroll = new JScrollPane(tree);     scroll.setBounds(0, 0, 300, 320);     frame.getContentPane().add(scroll);          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);     frame.setVisible(true);    }   }

其执行结果如下图所示:

以上是“Java怎么实现带复选框的树”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注高防服务器网行业资讯频道!

[微信提示:高防服务器能助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。

[图文来源于网络,不代表本站立场,如有侵权,请联系高防服务器网删除]
[