当前位置: 移动技术网 > IT编程>移动开发>Android > Android TreeView实现带复选框树形组织结构

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

2019年09月06日  | 移动技术网IT编程  | 我要评论

张廉珍,毕 畅,最新手机行情

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

复选框有两种设计模式:

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);
}}

资源地址:

github链接:treelistview

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

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网