Java带复选框的树(Java CheckBox Tree)实现和应用

在使用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);
 }
}

其执行结果如下图所示:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • java通过复选框控件数组实现添加多个复选框控件示例分享

    思路如下: 创建JPanel面板对象:使用JPanel类的setLayout(0,4)方法设置网格布局管理器,即列数为4,行数自动调节:创建一个字符串型一维数组作为控件文本数组:创建一个JCheckBox型一维数组作为控件数组:使用for循环遍历控件数组,初始化数组中的复选框组件,使用JPanel类的add()方法把数组元素(即每个复选框)添加到面板中.代码如下: 复制代码 代码如下: package cn.edu.xidian.crytoll; import java.awt.BorderLa

  • Java Swing组件复选框JCheckBox用法示例

    本文实例讲述了Java Swing组件复选框JCheckBox用法.分享给大家供大家参考,具体如下: JCheckBox是Swing中的复选框.所谓复选框就是指,可以同时存在多个这样的控件,它们可以有多个处于被选中状态.对于每一个复选框而言,它只有选中和未选中两种状态. JCheckBox的常用方法如下图所示: 可以为它添加ActionListener对象来响应事件. 使用demo如下: JCheckBoxDemo.java package awtDemo; import java.awt.Bo

  • java之swing实现复选框的方法

    本文实例讲述了java之swing实现复选框的方法.分享给大家供大家参考.具体如下: import java.awt.*; import javax.swing.*; import java.awt.event.*; public class test extends JApplet implements ItemListener{ JTextField jtf; public void init(){ Container contentPane = getContentPane(); cont

  • java JTree JCheckBox树复选框详解

    本文实例为大家分享了java JTree JCheckBox树复选框展示的具体代码,供大家参考,具体内容如下 1.CheckTreeManager.java public class CheckTreeManager extends MouseAdapter implements TreeSelectionListener { private CheckTreeSelectionModel selectionModel = null; // private JTree tree = new JT

  • Java带复选框的树(Java CheckBox Tree)实现和应用

    在使用Java Swing开发UI程序时,很有可能会遇到使用带复选框的树的需求,但是Java Swing并没有提供这个组件,因此如果你有这个需求,你就得自己动手实现带复选框的树. CheckBoxTree与JTree在两个层面上存在差异: 1.在模型层上,CheckBoxTree的每个结点需要一个成员来保存其是否被选中,但是JTree的结点则不需要. 2.在视图层上,CheckBoxTree的每个结点比JTree的结点多显示一个复选框. 既然存在两个差异,那么只要我们把这两个差异部分通过自己的实

  • Java如何做带复选框的菜单实例代码

    说明:         上面是我用Java做的扫雷游戏,其中就用到了带复选框式的菜单,原来也是用JCheckBoxMenuItem做的,但发现实在是问题多多,后干脆就用普通的JMenuItem来做,效果也不错.实际上说穿了很简单,就是在菜单的文本上做文章,前面加上一个 √ 即可.通过比较文本内容来判断是显示选中还是未选中,前面加还是不加 √ ,同时其他的文本内容如何变化,就好像扫雷的难度,初级.中级.高级只能选中一个. 代码: package com.game.mine; import java

  • Android TreeView实现带复选框树形组织结构

    之前做项目的时候做人员组织架构时候需要用到,同样可以用于目录视图.简单搜了一下没有合适的,只找到一个基础的有瑕疵的树形结构,就在基础上改了增加了复选框以及简化了部分代码.下面上演示效果图,时长25秒,手机卡见谅. 复选框有两种设计模式: 1.子节点选中则父节点选中,适合多级多item下方便了解哪些被选中: 2.子节点全部选中父节点才选中,更符合日常逻辑,适合少数量以及少层级. 下面上主要代码: 首先上MainActivity,主要作用上加载layout以及读取数据.实际中一般从数据库获取.命名较

  • jquery实现带复选框的表格行选中删除时高亮显示

    通过jquery技术来操作表格是件简单的事,通过jquery的语法,可以很轻松的完成表格的隔行换色,悬浮高亮,在实际的应用中可能会出现表格中带复选框的,删除时,将复选框所在的行的记录删除.在这的地方,可以加个特效,单击某行的同时将该行的复选框选中,该行的背景色也高亮显示.这样给人的感觉非常好. 效果如下:  我做的这里有两个功能: 功能1.单击某行,该行的复选框被选中,同时改变一下背景色. 功能2.单击全选/全不选标签后,改变行的颜色. 两个功能我封装到了js文件中,使用的时候引入就行了. 先看

  • js+csss实现的一个带复选框的下拉框

    效果图: css: <style type="text/css"> /* 带复选框的下拉框 */ ul li{ list-style: none; padding:0px; margin: 0px; } .select_checkBox{ border:0px solid red; position: relative; display:inline-block; } .chartQuota{ height:23px; float:left; display:inline-

  • bootstrap treeview 树形菜单带复选框及级联选择功能

    具体代码如下所示: <div id="searchTree"></div> <script> var treeData = [{ text: "Parent 1", nodes: [{ text: "Child 1", nodes: [{ text: "Grandchild 1" }, { text: "Grandchild 2", nodes: [{ text: &qu

  • vue实现带复选框的树形菜单

    本文实例为大家分享了vue实现带复选框的树形菜单展示的具体代码,供大家参考,具体内容如下 代码: <template> <div id="checkTree"> <div class="tree-box"> <div class="zTreeDemoBackground left"> <ul id="treeDemo" class="ztree">

  • vue+element UI实现树形表格带复选框的示例代码

    一:在component文件夹下新建如下treeTable文件夹,里面有2个文件: eval.js:将数据转换成树形数据 /** * @Author: jianglei * @Date: 2017-10-12 12:06:49 */ "use strict"; import Vue from "vue"; export default function treeToArray( data, expandAll, parent = null, level = null

  • Java Swing JCheckBox复选框的实现方法

    1. 概述 官方JavaDocsApi:javax.swing.JCheckBox JCheckBox,复选框. JCheckBox 常用构造方法: // 无文本,默认未选中 JCheckBox() // 有文本,默认未选中 JCheckBox(String text) // 有文本,并指定是否选中 JCheckBox(String text, boolean selected) JCheckBox 常用方法: // 设置复选框的 文本.字体 和 字体颜色 void setText(String

随机推荐