欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2022-07-06 14:39:21
之前做项目的时候做人员组织架构时候需要用到,同样可以用于目录视图。简单搜了一下没有合适的,只找到一个基础的有瑕疵的树形结构,就在基础上改了增加了复选框以及简化了部分代码。下...

之前做项目的时候做人员组织架构时候需要用到,同样可以用于目录视图。简单搜了一下没有合适的,只找到一个基础的有瑕疵的树形结构,就在基础上改了增加了复选框以及简化了部分代码。下面上演示效果图,时长25秒,手机卡见谅。

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

复选框有两种设计模式:

1、子节点选中则父节点选中,适合多级多item下方便了解哪些被选中;

2、子节点全部选中父节点才选中,更符合日常逻辑,适合少数量以及少层级。

下面上主要代码:

首先上mainactivity,主要作用上加载layout以及读取数据。实际中一般从数据库获取。命名较为随意请见谅。

public class mainactivity extends appcompatactivity {
 
 list<node> list = new arraylist<node>();
 private treelistview listview;
 private relativelayout relativelayout, rl;
 
 @override
 protected void oncreate(bundle savedinstancestate) {
 super.oncreate(savedinstancestate);
 setcontentview(r.layout.activity_main);
 relativelayout = (relativelayout) findviewbyid(r.id.main_relative_layout);
 context context=mainactivity.this;
 rl = new relativelayout(context);
 rl.setlayoutparams(new relativelayout.layoutparams(relativelayout.layoutparams.match_parent, relativelayout.layoutparams.match_parent));
 listview = new treelistview(context, initnodetree());
 listview.setlayoutparams(new relativelayout.layoutparams(relativelayout.layoutparams.match_parent, relativelayout.layoutparams.match_parent));
 relativelayout.addview(listview);
 }
 public list<node> initnodetree() {
 
 list<node> member_list =new arraylist<node>();
// -1表示为根节点,id的作用为标识对象身份,第三个参数此例子中是text文本
 member_list.add(new node("" + -1, "1" , "111"));
 member_list.add(new node(""+1 , "2" , "222"));
 member_list.add(new node("" + -1, "3" , "333"));
 member_list.add(new node("" + 1, "4" , "444"));
 member_list.add(new node("" + 4, "5" , "555"));
 member_list.add(new node("" + 4, "6" , "666"));
 member_list.add(new node("" + 4, "7" , "777"));
 member_list.add(new node("" + 7, "8" , "888"));
 member_list.add(new node("" + 8, "9" , "999"));
 member_list.add(new node("" + 8, "10" , "101010"));
 list.addall(member_list);
 return list;
 }
}

接下来是node类:

node对象当前主要有父节点id,自身id以及值组成,自身id自加,父节点id,使用过程中根据实际使用增加成员属性。比如作为组织架构,标识为人名还是一个空的部门,当前对象为第几层级等等,以及从数据库中获取时候直接设置默认选中。

public class node implements serializable {
 private node parent = null; // 父节点
 private list<node> childrens = new arraylist<node>();//子节点
 private string value;//节点显示值
 private boolean ischecked = false; //是否被选中
 private boolean isexpand = true;//是否处于扩展状态
 private boolean hascheckbox = true;//是否有复选框
 private string parentid = null;
 private string curid = null;
 
 
 //父节点集合
 private list<node> parents = new arraylist<>();
 
 /**
 * 设置节点值
 *
 * @param parentid
 *  todo
 * @param curid
 *  todo
 */
 public node( string parentid, string curid, string value) {
 // todo auto-generated constructor stub
 
 this.value = value;
 this.parentid = parentid;
 this.curid = curid;
 
 }
 
 public list<node> getparents() {
 return parents;
 }
 
 public void setparents(node node) {
 if(node != null) {
 if (!parents.contains(node)) {
 parents.add(node);
 }
 }
 }
 
 /**
 * 得到父节点
 */
 public node getparent() {
 return parent;
 }
 /**
 * 设置父节点
 * @param parent
 */
 public void setparent(node parent) {
 this.parent = parent;
 }
 /**
 * 得到子节点
 * @return
 */
 public list<node> getchildrens() {
 return childrens;
 }
 /**
 * pandu是否根节点
 * @return
 *
 */
 public boolean isroot(){
 return parent ==null?true:false;
 }
 
 /**
 * 是否被选中
 * @return
 *
 */
 public boolean ischecked() {
 return ischecked;
 }
 public void setchecked(boolean ischecked) {
 this.ischecked = ischecked;
 }
 /**
 * 是否是展开状态
 * @return
 *
 */
 public boolean isexplaned() {
 return isexpand;
 }
 /**
 * 设置展开状态
 * @param isexplaned
 *
 */
 public void setexplaned(boolean isexplaned) {
 this.isexpand = isexplaned;
 }
 /**
 * 是否有复选框
 * @return
 *
 */
 public boolean hascheckbox() {
 return hascheckbox;
 }
 /**
 * 设置是否有复选框
 * @param hascheckbox
 *
 */
 public void sethascheckbox(boolean hascheckbox) {
 this.hascheckbox = hascheckbox;
 }
 
 
 
 
 /**
 * 得到节点值
 * @return
 *
 */
 public string getvalue() {
 return value;
 }
 /**
 * 设置节点值
 * @param value
 *
 */
 public void setvalue(string value) {
 this.value = value;
 }
 /**
 * 增加一个子节点
 * @param node
 *
 */
 public void addnode(node node){
 if(!childrens.contains(node)){
 childrens.add(node);
 }
 }
 /**
 * 移除一个子节点
 * @param node
 *
 */
 public void removenode(node node){
 if(childrens.contains(node))
 childrens.remove(node);
 }
 /**
 * 移除指定位置的子节点
 * @param location
 *
 */
 public void removenode(int location){
 childrens.remove(location);
 }
 /**
 * 清除所有子节点
 *
 */
 public void clears(){
 childrens.clear();
 }
 /**
 * 判断给出的节点是否当前节点的父节点
 * @param node
 * @return
 *
 */
 public boolean isparent(node node){
 if(parent == null)return false;
 if(parent.equals(node))return true;
 return parent.isparent(node);
 }
 /**
 * 递归获取当前节点级别
 * @return
 *
 */
 public int getlevel(){
 return parent ==null?0:parent.getlevel()+1;
 }
 /**
 * 父节点是否处于折叠的状态
 * @return
 *
 */
 public boolean isparentcollapsed(){
 if(parent ==null)return false;
 if(!parent.isexplaned())return true;
 return parent.isparentcollapsed();
 }
 /**
 * 是否叶节点(没有展开下级的几点)
 * @return
 *
 */
 public boolean isleaf(){
 return childrens.size()<1?true:false;
 }
 /**
 * 返回自己的id
 * @return
 **/
 public string getcurid() {
 // todo auto-generated method stub
 return curid;
 }
 /**
 * 返回的父id
 * @return
 **/
 public string getparentid() {
 // todo auto-generated method stub
 return parentid;
 }
}

下面是核心代码:

两种选择模式在treeadapter中进行修改。

package com.example.administrator.treeview.treeview;
 
 
import android.content.context;
import android.util.log;
import android.view.layoutinflater;
import android.view.view;
import android.view.view.onclicklistener;
import android.view.viewgroup;
import android.widget.baseadapter;
import android.widget.checkbox;
import android.widget.imageview;
import android.widget.textview;
 
 
import com.example.administrator.treeview.r;
 
import java.util.arraylist;
import java.util.list;
 
public class treeadapter extends baseadapter {
 private context con;
 private layoutinflater lif;
 public list<node> all = new arraylist<node>();//展示
 private list<node> cache = new arraylist<node>();//缓存,记录点状态
 private treeadapter tree = this;
 boolean hascheckbox;
 private int expandicon = -1;//展开图标
 private int collapseicon = -1;//收缩图标
 viewitem vi = null;
 
// //存储checkbox选中的集合
// private list<>
 
 /**
 * 构造方法
 */
 public treeadapter(context context, list<node> rootnodes){
 this.con = context;
 this.lif = (layoutinflater)con.getsystemservice(context.layout_inflater_service);
 for(int i=0;i<rootnodes.size();i++){
 addnode(rootnodes.get(i));
 }
 }
 /**
 * 把一个节点上的所有的内容都挂上去
 * @param node
 */
 public void addnode(node node){
 all.add(node);
 cache.add(node);
 if(node.isleaf())return;
 for(int i = 0;i<node.getchildrens().size();i++){
 addnode(node.getchildrens().get(i));
 }
 }
 /**
 * 设置展开收缩图标
 * @param expandicon
 * @param collapseicon
 */
 public void setcollapseandexpandicon(int expandicon,int collapseicon){
 this.collapseicon = collapseicon;
 this.expandicon = expandicon;
 }
 /**
 * 一次性对某节点的所有节点进行选中or取消操作
 */
 public void checknode(node n,boolean ischecked){
 n.setchecked(ischecked);
 checkchildren(n,ischecked);
// 有一个子节点选中,则父节点选中
 if (n.getparent()!=null)
 checkparent(n,ischecked);
// 有一个子节点未选中,则父节点未选中
// unchecknode(n, ischecked);
 }
 
 /**
 * 对父节点操作时,同步操作子节点
 */
 public void checkchildren(node n,boolean ischecked){
 for(int i =0 ;i<n.getchildrens().size();i++){
 n.getchildrens().get(i).setchecked(ischecked);
 checkchildren(n.getchildrens().get(i),ischecked);
 }
 }
 /**
 * 有一个子节点选中,则父节点选中
 */
 public void checkparent(node n,boolean ischecked){
// 有一个子节点选中,则父节点选中
 if (n.getparent()!=null&&ischecked){
 n.getparent().setchecked(ischecked);
 checkparent(n.getparent(),ischecked);
 }
// 全部子节点取消选中,则父节点取消选中
 if (n.getparent()!=null &&!ischecked){
 for (int i = 0; i < n.getparent().getchildrens().size(); i++) {
 if (n.getparent().getchildrens().get(i).ischecked()) {
 checkparent(n.getparent(),!ischecked);
 return ;
 }
 }
 n.getparent().setchecked(ischecked);
 checkparent(n.getparent(),ischecked);
 }
 }
 
 /**
 * 有一个子节点未选中,则父节点未选中
 */
 public void unchecknode(node n, boolean ischecked){
 boolean flag = false;
 n.setchecked(ischecked);
 if(n.getparent() != null ){
 log.d("parentsize", n.getparent().getchildrens().get(0).ischecked() + "");
 for (int i = 0; i < n.getparent().getchildrens().size(); i++) {
 if((n.getparent().getchildrens().get(i)) != n && (n.getparent().getchildrens().get(i).ischecked() != true)){
 flag = true;
 break;
 }
 }
 if(!flag) {
 unchecknode(n.getparent(), ischecked);
 }
 }
 }
 
 /**
 * 获取所有选中节点
 * @return
 *
 */
 public list<node> getselectednode(){
 log.d("getselectednode", "我被执行了!");
 list<node> checks =new arraylist<node>() ;
 for(int i = 0;i<cache.size();i++){
 node n =(node)cache.get(i);
 if(n.ischecked())
 checks.add(n);
 }
 return checks;
 }
 
 public void setselectednode(list<string> selectednode){
 for (int i=0;i<cache.size();i++) {
 if(selectednode.contains(cache.get(i).getcurid())) {
 cache.get(i).setchecked(true);
 cache.get(i).getparent().setchecked(true);
 }
 }
 }
 /**
 * 设置是否有复选框
 * @param hascheckbox
 *
 */
 public void setcheckbox(boolean hascheckbox){
 this.hascheckbox = hascheckbox;
 }
 /**
 * 控制展开缩放某节点
 * @param location
 *
 */
 public void expandorcollapse(int location){
 node n = all.get(location);//获得当前视图需要处理的节点 
 if(n!=null)//排除传入参数错误异常
 {
 if(!n.isleaf()){
 n.setexplaned(!n.isexplaned());// 由于该方法是用来控制展开和收缩的,所以取反即可
 filternode();//遍历一下,将所有上级节点展开的节点重新挂上去
 this.notifydatasetchanged();//刷新视图
 }
 }
 }
 
 /**
 * 设置展开等级
 * @param level
 *
 */
 public void setexpandlevel(int level){
 all.clear();
 for(int i = 0;i<cache.size();i++){
 node n = cache.get(i);
 if(n.getlevel()<=level){
 if(n.getlevel()<level)
 n.setexplaned(true);
 else
 n.setexplaned(false);
 all.add(n);
 }
 }
 
 }
 /* 清理all,从缓存中将所有父节点不为收缩状态的都挂上去*/
 public void filternode(){
 all.clear();
 for(int i = 0;i<cache.size();i++){
 node n = cache.get(i);
 if(!n.isparentcollapsed()||n.isroot())//凡是父节点不收缩或者不是根节点的都挂上去
 all.add(n);
 }
 }
 
 @override
 public int getcount() {
 // todo auto-generated method stub
 return all.size();
 }
 
 
 @override
 public object getitem(int location) {
 // todo auto-generated method stub
 return all.get(location);
 }
 
 
 @override
 public long getitemid(int location) {
 // todo auto-generated method stub
 return location;
 }
 
 
 @override
 public view getview(final int location, view view, viewgroup viewgroup) {
 
 final node n = all.get(location);
 
 //viewitem vi = null;
 if(view == null){
 view = lif.inflate(r.layout.member_item, null);
 vi = new viewitem();
 vi.cb = (checkbox)view.findviewbyid(r.id.checkbox);
 vi.flagicon = (imageview)view.findviewbyid(r.id.disclosureimg);
 vi.tv = (textview)view.findviewbyid(r.id.contenttext);
 vi.cb.setonclicklistener(new onclicklistener() {
 private node mcheckboxn;
 @override
 public void onclick(view v) {
 mcheckboxn = (node) v.gettag();
 checknode(mcheckboxn, ((checkbox) v).ischecked());
 //unchecknode(n, ((checkbox) v).ischecked());
 tree.notifydatasetchanged(); //只有点击部门后刷新页面,不然刷新频繁导致卡顿
 
 }
 });
 view.settag(vi);
 }
 else{
 vi = (viewitem)view.gettag();
 }
 if(n!=null){
 if(vi==null||vi.cb==null)
 system.out.println();
 vi.cb.settag(n);
 vi.cb.setchecked(n.ischecked());
 //叶节点不显示展开收缩图标
 if(n.isexplaned()){
 if(expandicon!=-1){
 vi.flagicon.setimageresource(expandicon);
 }
 }
 else{
 if(collapseicon!=-1){
 vi.flagicon.setimageresource(collapseicon);
 }
 }
 //显示文本
 vi.tv.settext(n.getvalue());
 // 控制缩进
 vi.flagicon.setpadding(100*n.getlevel(), 3,3, 3);
 if(n.isleaf()){
 vi.flagicon.setvisibility(view.invisible);
 }
 else{
 vi.flagicon.setvisibility(view.visible);
 }
 //设置是否显示复选框
 if(n.hascheckbox()){
 vi.cb.setvisibility(view.visible);
 }
 else{
 vi.cb.setvisibility(view.gone);
 }
 }
 return view;
 }
 
 
 public class viewitem{
 private checkbox cb;
 private imageview flagicon;
 private textview tv;
 }
}

接下来是treelistview: 

package com.example.administrator.treeview.treeview;
 
import android.content.context;
import android.util.log;
import android.view.view;
import android.view.viewgroup;
import android.widget.adapterview;
import android.widget.linearlayout;
import android.widget.listview;
import android.widget.relativelayout;
import com.example.administrator.treeview.r;
import java.util.arraylist;
import java.util.collection;
import java.util.iterator;
import java.util.linkedhashmap;
import java.util.list;
import java.util.map;
import java.util.set;
 
public class treelistview extends listview {
 listview treelist = null;
 treeadapter ta = null;
 public list<node> mnodelist;
 private list<node> checklist;
 
 
 public treelistview(final context context, list<node> res) {
 super(context);
 treelist = this;
 treelist.setfocusable(false);
 treelist.setbackgroundcolor(0xffffff);
 treelist.setfadingedgelength(0);
 treelist.setlayoutparams(new viewgroup.layoutparams(linearlayout.layoutparams.match_parent, relativelayout.layoutparams.match_parent));
 
 treelist.setonitemclicklistener(new onitemclicklistener() {
 
 @override
 public void onitemclick(adapterview<?> parent, view view,
     int position, long id) {
 ((treeadapter) parent.getadapter()).expandorcollapse(position);
 }
 });
 initnode(context, initnodroot(res), true, -1, -1, 0);
 }
 
 // 使用 onmeasure 方法,来解决尺寸高度的问题,以及事件冲突的问题;
 protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {
 heightmeasurespec = measurespec.makemeasurespec(
 integer.max_value>>2,
 measurespec.at_most
 );
 super.onmeasure(widthmeasurespec, heightmeasurespec);
 }
// /**
// *
// * @param context
// *  响应监听的上下文
// * @param root
// *  已经挂好树的根节点
// * @param hascheckbox
// *  是否整个树有复选框
// * @param tree_ex_id
// *  展开iconid -1会使用默认的
// * @param tree_ec_id
// *  收缩iconid -1会使用默认的
// * @param expandlevel
// *  初始展开等级
// *
// */
 public list<node> initnodroot(list<node> res) {
 arraylist<node> list = new arraylist<node>();
 arraylist<node> roots = new arraylist<node>();
 map<string, node> nodemap = new linkedhashmap<string, node>();
 for (int i = 0; i < res.size(); i++) {
 node nr = res.get(i);
 node n = new node( nr.getparentid(), nr.getcurid(), nr.getvalue());
 nodemap.put(n.getcurid(), n);// 生成map树
 }
 set<string> set = nodemap.keyset();
 collection<node> collections = nodemap.values();
 iterator<node> iterator = collections.iterator();
 while (iterator.hasnext()) {// 添加所有根节点到root中
 node n = iterator.next();
 if (!set.contains(n.getparentid()))
 roots.add(n);
 list.add(n);
 }
 for (int i = 0; i < list.size(); i++) {
 node n = list.get(i);
 for (int j = i + 1; j < list.size(); j++) {
 node m = list.get(j);
 if (m.getparentid() .equals( n.getcurid())) {
 n.addnode(m);
 m.setparent(n);
 m.setparents(n);
 } else if (m.getcurid() .equals( n.getparentid())) {
 m.addnode(n);
 n.setparent(m);
 m.setparents(m);
 }
 }
 }
 return roots;
 }
 
 public void initnode(context context, list<node> root, boolean hascheckbox,
    int tree_ex_id, int tree_ec_id, int expandlevel) {
 ta = new treeadapter(context, root);
 //获取
 mnodelist = ta.all;
 // 设置整个树是否显示复选框
 ta.setcheckbox(true);
 // 设置展开和折叠时图标
 int tree_ex_id_ = (tree_ex_id == -1) ? r.drawable.down_icon : tree_ex_id;
 int tree_ec_id_ = (tree_ec_id == -1) ? r.drawable.right_icon : tree_ec_id;
 ta.setcollapseandexpandicon(tree_ex_id_, tree_ec_id_);
 // 设置默认展开级别
 ta.setexpandlevel(expandlevel);
 this.setadapter(ta);
 }
 /* 返回当前所有选中节点的list数组 */
 public list<node> get() {
 log.d("get", ta.getselectednode().size() + "");
 return ta.getselectednode();
 }
public void setselect(list<string> allselect){
 ta.setselectednode(allselect);
}}

资源地址:android带复选框的树形组织架构treelistview

github链接:treelistview

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