代码基本上是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源代码如下:

 package CheckBoxTree;
import javax.swing.tree.DefaultMutableTreeNode;
import java.util.Vector; import javax.swing.tree.*;
import java.util.*; public class CheckBoxTreeNode extends DefaultMutableTreeNode implements Comparable
{
protected boolean isSelected; public CheckBoxTreeNode()
{
this(null);
} public CheckBoxTreeNode(Object userObject)
{
this(userObject, true,false);
} public void add(MutableTreeNode childNode)
{
super.add(childNode);
Collections.sort(super.children);
} public int compareTo(Object o)
{
return this.toString().compareTo(o.toString());
} public CheckBoxTreeNode(Object userObject,boolean allowsChildren, boolean isSelected)
{
super(userObject, allowsChildren);
this.isSelected = isSelected;
} public boolean isSelected()
{
return isSelected;
} public Object[] getChildNode()
{
if(children !=null)
{
return children.toArray();
} return null;
} 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 CheckBoxTree;
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 CheckBoxTree;
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")); //选中的文字颜色
//System.out.println("XXXXX");
}
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 CheckBoxTree;
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);
System.out.println("XXXX " + tree.getLastSelectedPathComponent().toString() + " has been selected!(mouse)");
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);
}
}
}
}

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

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

         tree.addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
treeMouseClicked(e);
}
});
 private void treeMouseClicked(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(null != path)
{
CheckBoxTreeNode node = (CheckBoxTreeNode)path.getLastPathComponent();
if(null != node)
{
boolean isSelected = !node.isSelected();
node.setSelected(isSelected);
((DefaultTreeModel)tree.getModel()).nodeStructureChanged(node);
}
}
}

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

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

下面是具体的调用过程:

 import CheckBoxTree.*;
import java.awt.*;
import javax.swing.JPanel;
import javax.swing.JFrame;
import javax.swing.JSplitPane;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import java.awt.event.*;
import javax.swing.event.*; public class WriteForBlog extends JFrame
{
private GridBagLayout gridBagLayout = new GridBagLayout();
private JSplitPane splitPane = new JSplitPane();
private GridBagLayout gridBagLayoutForCheckBoxTree = new GridBagLayout();
private JPanel checkBoxTreePanel = new JPanel();
private JTree tree = new JTree();
private JScrollPane ScrollPaneForTree = new JScrollPane();
private CheckBoxTreeNode checkBoxTreeNode = new CheckBoxTreeNode();
private DefaultTreeModel defaultTreeModel = new DefaultTreeModel(checkBoxTreeNode); public WriteForBlog()
{
try
{
jbInit();
}
catch(Exception ex)
{
System.out.println(ex.getMessage());
}
} private void jbInit() throws Exception
{
this.setLayout(gridBagLayout);
this.setBounds(200, 200, 1000, 600); checkBoxTreePanel.setLayout(gridBagLayoutForCheckBoxTree);
splitPane.setLastDividerLocation(-1);
tree.setModel(defaultTreeModel);
tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
tree.addTreeSelectionListener(new TreeSelectionListener()
{
public void valueChanged(TreeSelectionEvent e)
{
tree_valueChanged(e);
}
}); tree.addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
treeMouseClicked(e);
}
}); this.add(splitPane, new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0
,GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5), 0, 0));
splitPane.add(checkBoxTreePanel, JSplitPane.LEFT);
splitPane.setDividerLocation(150);
checkBoxTreePanel.add(ScrollPaneForTree, new GridBagConstraints(0, 1, 1, 1, 1.0, 1.0
,GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
ScrollPaneForTree.getViewport().add(tree, null); this.loadTree(); } private void loadTree()
{
CheckBoxTreeNode rootNode = (CheckBoxTreeNode) ( (DefaultTreeModel) tree.getModel()).getRoot();
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");
CheckBoxTreeNode node1_5 = new CheckBoxTreeNode("node_1_5");
CheckBoxTreeNode node1_6 = new CheckBoxTreeNode("node_1_6"); node1.add(node1_1);
node1.add(node1_2);
node1.add(node1_3);
node1.add(node1_6);
node1.add(node1_5); rootNode.add(node1);
//rootNode.add(node2); DefaultTreeModel model = new DefaultTreeModel(node1);
tree.expandPath(new TreePath(rootNode.getPath()));
//tree.addMouseListener(new CheckBoxTreeNodeSelectionListener());
tree.setModel(model);
tree.setCellRenderer(new CheckBoxTreeCellRenderer());
tree.updateUI();
} public static void main(String[] args)
{
WriteForBlog test = new WriteForBlog();
test.setVisible(true);
} private void tree_valueChanged(TreeSelectionEvent e)
{ } private void treeMouseClicked(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);
}
}
}
}

[转载] Java CheckBoxTree的更多相关文章

  1. [转载]Java中继承、装饰者模式和代理模式的区别

    [转载]Java中继承.装饰者模式和代理模式的区别 这是我在学Java Web时穿插学习Java设计模式的笔记 我就不转载原文了,直接指路好了: 装饰者模式和继承的区别: https://blog.c ...

  2. [转载]Java序列化与反序列化

    [转载]Java序列化与反序列化 来源: https://www.cnblogs.com/anitinaj/p/9253921.html 序列化和反序列化作为Java里一个较为基础的知识点,那你能说一 ...

  3. [转载]java中import作用详解

    [转载]java中import作用详解 来源: https://blog.csdn.net/qq_25665807/article/details/74747868 这篇博客讲的真的很清楚,这个作者很 ...

  4. [转载]Java中异常的捕获顺序(多个catch)

    http://blog.sina.com.cn/s/blog_6b022bc60101cdbv.html [转载]Java中异常的捕获顺序(多个catch) (2012-11-05 09:47:28) ...

  5. [转载]java int与integer的区别

    声明: 本篇文章属于转载文章,来源:

  6. [转载]Java程序员使用的20几个大数据工具

    最近我问了很多Java开发人员关于最近12个月内他们使用的是什么大数据工具. 这是一个系列,主题为: 语言web框架应用服务器SQL数据访问工具SQL数据库大数据构建工具云提供商今天我们就要说说大数据 ...

  7. [转载] Java高新技术第一篇:类加载器详解

    本文转载自: http://blog.csdn.net/jiangwei0910410003/article/details/17733153 首先来了解一下字节码和class文件的区别: 我们知道, ...

  8. [ 转载]JAVA Socket超时浅析

    JAVA Socket超时浅析 转载自 http://blog.csdn.net/sureyonder/article/details/5633647 套接字或插座(socket)是一种软件形 式的抽 ...

  9. [转载] java中byte数组与int,long,short间的转换

    文章转载自http://blog.csdn.net/leetcworks/article/details/7390731 package com.util; /** * * <ul> * ...

随机推荐

  1. XMind共享未保存的思维导图的教程

    我们在XMind 6对导图进行局域网共享时,一般都是对XMind文件先进行保存再共享,那样是忘记保存呢,该如何共享.局域网共享功能是XMind 6特有的功能之一,自是较为完善,性能强大的功能,当然有办 ...

  2. Java实现多种方式的http数据抓取

    前言: 时下互联网第一波的浪潮已消逝,随着而来的基于万千数据的物联网时代,因而数据成为企业的重要战略资源之一.基于数据抓取技术,本文介绍了java相关抓取工具,并附上demo源码供感兴趣的朋友测试! ...

  3. 微博轻量级RPC框架Motan

    Motan 是微博技术团队研发的基于 Java 的轻量级 RPC 框架,已在微博内部大规模应用多年,每天稳定支撑微博上亿次的内部调用.Motan 基于微博的高并发和高负载场景优化,成为一套简单.易用. ...

  4. Smallest Common Multiple

    FCC题目:找出能被两个给定参数和它们之间的连续数字整除的最小公倍数. 范围是两个数字构成的数组,两个数字不一定按数字顺序排序. 例如对 1 和 3 -- 找出能被 1 和 3 和它们之间所有数字整除 ...

  5. C#工具代码

    反射获取当前请求参数 var prop = System.Type.GetType("System.Web.HttpContext, System.Web, Version=4.0.0.0, ...

  6. php的empty(),trim(),strlen()方法

    如果empty()函数的参数是非空或非零的值,则empty()返回FALSE.换句话说,"".0."0".NULL.array().var$var:以及没有任何 ...

  7. sql笨办法同步数据

    Helpers.SqlHelper sqlHelper = new Helpers.SqlHelper("server=***;database=Cms;user id=sa;passwor ...

  8. 使用Jquery.AJAX方法和PHP后台数据交互小结

    使用jQuery的AJAX方法和后台PHP进行数据交互,交互采用的数据格式JSON格式. 我主要小小的总结了一下,我使用AJAX方法时候遇到一些小小的问题. 第一:在传递数据的时候,传输地址注意是否正 ...

  9. MySQL主从复制实现

    上回提到了用ThinkPHP框架来实现数据库的读写分离,现在就来简单说说MySQL的主从复制. 形式 一主一从(也就是这里要实现的形式) 主主复制 一主多从 多主一从(MySQL5.7开始支持) 联级 ...

  10. [译]flexbox全揭秘

    原文:http://css-tricks.com/snippets/css/a-guide-to-flexbox/ 弹性布局(弹性盒子,现今仍是w3c的候选推荐),目标在于,对于一个容器中的各个项目块 ...