Android多级树形列表控件
程序员文章站
2024-02-29 15:52:22
我们开发app过程中,经常会碰到需要 多级列表展示的效果。而android原生sdk中根本没有3级 4级甚至更多级别的列表控件。
所以我们就要自己去实现一个类似treel...
我们开发app过程中,经常会碰到需要 多级列表展示的效果。而android原生sdk中根本没有3级 4级甚至更多级别的列表控件。
所以我们就要自己去实现一个类似treelistview 的控件,下面这个是我项目中的一个效果图,可支持多级列表扩展。
android中有expandlistview控件,但是这个控件只支持两级列表。对于多级列表如果重写这个不是很好用。
实现这种列表 思想就是递归,构造一个子父级的关系。
话不多说 代码中体会
activity
package com.example.customtreeviewdemo; import java.util.arraylist; import java.util.list; import android.app.activity; import android.os.bundle; import android.view.view; import android.view.view.onclicklistener; import android.widget.button; import android.widget.listview; import android.widget.toast; import com.example.customtreeviewdemo.bean.mynodebean; import com.example.customtreeviewdemo.tree.node; import com.example.customtreeviewdemo.tree.treelistviewadapter.ontreenodeclicklistener; public class mainactivity extends activity { private listview treelv; private button checkswitchbtn; private mytreelistviewadapter<mynodebean> adapter; private list<mynodebean> mdatas = new arraylist<mynodebean>(); //标记是显示checkbox还是隐藏 private boolean ishide = true; @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); initdatas(); treelv = (listview) this.findviewbyid(r.id.tree_lv); checkswitchbtn = (button)this.findviewbyid(r.id.check_switch_btn); checkswitchbtn.setonclicklistener(new onclicklistener(){ @override public void onclick(view v) { if(ishide){ ishide = false; }else{ ishide = true; } adapter.updateview(ishide); } }); try { adapter = new mytreelistviewadapter<mynodebean>(treelv, this, mdatas, 10, ishide); adapter.setontreenodeclicklistener(new ontreenodeclicklistener() { @override public void onclick(node node, int position) { if (node.isleaf()) { toast.maketext(getapplicationcontext(), node.getname(), toast.length_short).show(); } } @override public void oncheckchange(node node, int position, list<node> checkednodes) { stringbuffer sb = new stringbuffer(); for (node n : checkednodes) { int pos = n.getid() - 1; sb.append(mdatas.get(pos).getname()).append("---") .append(pos + 1).append(";"); } toast.maketext(getapplicationcontext(), sb.tostring(), toast.length_short).show(); } }); } catch (illegalargumentexception e) { e.printstacktrace(); } catch (illegalaccessexception e) { e.printstacktrace(); } treelv.setadapter(adapter); } private void initdatas() { mdatas.add(new mynodebean(1, 0, "中国古代")); mdatas.add(new mynodebean(2, 1, "唐朝")); mdatas.add(new mynodebean(3, 1, "宋朝")); mdatas.add(new mynodebean(4, 1, "明朝")); mdatas.add(new mynodebean(5, 2, "李世民")); mdatas.add(new mynodebean(6, 2, "李白")); mdatas.add(new mynodebean(7, 3, "赵匡胤")); mdatas.add(new mynodebean(8, 3, "苏轼")); mdatas.add(new mynodebean(9, 4, "朱元璋")); mdatas.add(new mynodebean(10, 4, "唐伯虎")); mdatas.add(new mynodebean(11, 4, "文征明")); mdatas.add(new mynodebean(12, 7, "赵建立")); mdatas.add(new mynodebean(13, 8, "苏东东")); mdatas.add(new mynodebean(14, 10, "秋香")); } }
adapter
这个adapter是继承了自己的定义的一个treelistviewadapter,核心实现都是在treelistviewadapter这个里面
package com.example.customtreeviewdemo; import java.util.list; import android.content.context; import android.view.view; import android.view.viewgroup; import android.widget.checkbox; import android.widget.compoundbutton; import android.widget.compoundbutton.oncheckedchangelistener; import android.widget.imageview; import android.widget.listview; import android.widget.textview; import com.example.customtreeviewdemo.tree.node; import com.example.customtreeviewdemo.tree.treelistviewadapter; public class mytreelistviewadapter<t> extends treelistviewadapter<t> { public mytreelistviewadapter(listview mtree, context context, list<t> datas, int defaultexpandlevel,boolean ishide) throws illegalargumentexception, illegalaccessexception { super(mtree, context, datas, defaultexpandlevel,ishide); } @suppresswarnings("unchecked") @override public view getconvertview(node node, int position, view convertview, viewgroup parent) { viewholder viewholder = null; if (convertview == null) { convertview = minflater.inflate(r.layout.list_item, parent, false); viewholder = new viewholder(); viewholder.icon = (imageview) convertview .findviewbyid(r.id.id_treenode_icon); viewholder.label = (textview) convertview .findviewbyid(r.id.id_treenode_name); viewholder.checkbox = (checkbox)convertview.findviewbyid(r.id.id_treenode_check); convertview.settag(viewholder); } else { viewholder = (viewholder) convertview.gettag(); } if (node.geticon() == -1) { viewholder.icon.setvisibility(view.invisible); } else { viewholder.icon.setvisibility(view.visible); viewholder.icon.setimageresource(node.geticon()); } if(node.ishidechecked()){ viewholder.checkbox.setvisibility(view.gone); }else{ viewholder.checkbox.setvisibility(view.visible); setcheckboxbg(viewholder.checkbox,node.ischecked()); } viewholder.label.settext(node.getname()); return convertview; } private final class viewholder { imageview icon; textview label; checkbox checkbox; } /** * checkbox是否显示 * @param cb * @param ischecked */ private void setcheckboxbg(checkbox cb,boolean ischecked){ if(ischecked){ cb.setbackgroundresource(r.drawable.check_box_bg_check); }else{ cb.setbackgroundresource(r.drawable.check_box_bg); } } }
自定义treelistviewadapter 这个是整个树形结构的一个适配器,这里面主要是实现对node节点的操作 点击,选中改变 更新等
package com.example.customtreeviewdemo.tree; import java.util.arraylist; import java.util.list; import android.content.context; import android.view.layoutinflater; import android.view.view; import android.view.viewgroup; import android.widget.adapterview; import android.widget.adapterview.onitemclicklistener; import android.widget.baseadapter; import android.widget.checkbox; import android.widget.compoundbutton; import android.widget.compoundbutton.oncheckedchangelistener; import android.widget.listview; import android.widget.relativelayout; /** * tree适配器 * @param <t> */ public abstract class treelistviewadapter<t> extends baseadapter { protected context mcontext; /** * 存储所有可见的node */ protected list<node> mnodes; protected layoutinflater minflater; /** * 存储所有的node */ protected list<node> mallnodes; /** * 点击的回调接口 */ private ontreenodeclicklistener ontreenodeclicklistener; public interface ontreenodeclicklistener { /** * 处理node click事件 * @param node * @param position */ void onclick(node node, int position); /** * 处理checkbox选择改变事件 * @param node * @param position * @param checkednodes */ void oncheckchange(node node, int position,list<node> checkednodes); } public void setontreenodeclicklistener( ontreenodeclicklistener ontreenodeclicklistener) { this.ontreenodeclicklistener = ontreenodeclicklistener; } /** * * @param mtree * @param context * @param datas * @param defaultexpandlevel * 默认展开几级树 * @throws illegalargumentexception * @throws illegalaccessexception */ public treelistviewadapter(listview mtree, context context, list<t> datas, int defaultexpandlevel, boolean ishide) throws illegalargumentexception, illegalaccessexception { mcontext = context; /** * 对所有的node进行排序 */ mallnodes = treehelper .getsortednodes(datas, defaultexpandlevel, ishide); /** * 过滤出可见的node */ mnodes = treehelper.filtervisiblenode(mallnodes); minflater = layoutinflater.from(context); /** * 设置节点点击时,可以展开以及关闭;并且将itemclick事件继续往外公布 */ mtree.setonitemclicklistener(new onitemclicklistener() { @override public void onitemclick(adapterview<?> parent, view view, int position, long id) { expandorcollapse(position); if (ontreenodeclicklistener != null) { ontreenodeclicklistener.onclick(mnodes.get(position), position); } } }); } /** * 相应listview的点击事件 展开或关闭某节点 * * @param position */ public void expandorcollapse(int position) { node n = mnodes.get(position); if (n != null)// 排除传入参数错误异常 { if (!n.isleaf()) { n.setexpand(!n.isexpand()); mnodes = treehelper.filtervisiblenode(mallnodes); notifydatasetchanged();// 刷新视图 } } } @override public int getcount() { return mnodes.size(); } @override public object getitem(int position) { return mnodes.get(position); } @override public long getitemid(int position) { return position; } @override public view getview(final int position, view convertview, viewgroup parent) { final node node = mnodes.get(position); convertview = getconvertview(node, position, convertview, parent); // 设置内边距 convertview.setpadding(node.getlevel() * 30, 3, 3, 3); if (!node.ishidechecked()) { //获取各个节点所在的父布局 relativelayout myview = (relativelayout) convertview; //父布局下的checkbox checkbox cb = (checkbox) myview.getchildat(1); cb.setoncheckedchangelistener(new oncheckedchangelistener(){ @override public void oncheckedchanged(compoundbutton buttonview, boolean ischecked) { treehelper.setnodechecked(node, ischecked); list<node> checkednodes = new arraylist<node>(); for(node n:mallnodes){ if(n.ischecked()){ checkednodes.add(n); } } ontreenodeclicklistener.oncheckchange(node,position,checkednodes); treelistviewadapter.this.notifydatasetchanged(); } }); } return convertview; } public abstract view getconvertview(node node, int position, view convertview, viewgroup parent); /** * 更新 * @param ishide */ public void updateview(boolean ishide){ for(node node:mallnodes){ node.sethidechecked(ishide); } this.notifydatasetchanged(); } }
node 模型类
package com.example.customtreeviewdemo.bean; public class mynodebean { /** * 节点id */ private int id; /** * 节点父id */ private int pid; /** * 节点name */ private string name; /** * */ private string desc; /** * 节点名字长度 */ private long length; public mynodebean(int id, int pid, string name) { super(); this.id = id; this.pid = pid; this.name = name; } public int getid() { return id; } public void setid(int id) { this.id = id; } public int getpid() { return pid; } public void setpid(int pid) { this.pid = pid; } public string getname() { return name; } public void setname(string name) { this.name = name; } public string getdesc() { return desc; } public void setdesc(string desc) { this.desc = desc; } public long getlength() { return length; } public void setlength(long length) { this.length = length; } }
treehelper这个也是核心操作类,主要功能是将业务数据和节点数据进行匹配
package com.example.customtreeviewdemo.tree; import java.lang.reflect.field; import java.util.arraylist; import java.util.list; import com.example.customtreeviewdemo.r; public class treehelper { /** * 根据所有节点获取可见节点 * * @param allnodes * @return */ public static list<node> filtervisiblenode(list<node> allnodes) { list<node> visiblenodes = new arraylist<node>(); for (node node : allnodes) { // 如果为根节点,或者上层目录为展开状态 if (node.isroot() || node.isparentexpand()) { setnodeicon(node); visiblenodes.add(node); } } return visiblenodes; } /** * 获取排序的所有节点 * * @param datas * @param defaultexpandlevel * @return * @throws illegalargumentexception * @throws illegalaccessexception */ public static <t> list<node> getsortednodes(list<t> datas, int defaultexpandlevel, boolean ishide) throws illegalaccessexception, illegalargumentexception { list<node> sortednodes = new arraylist<node>(); // 将用户数据转化为list<node> list<node> nodes = convertdata2nodes(datas, ishide); // 拿到根节点 list<node> rootnodes = getrootnodes(nodes); // 排序以及设置node间关系 for (node node : rootnodes) { addnode(sortednodes, node, defaultexpandlevel, 1); } return sortednodes; } /** * 把一个节点上的所有的内容都挂上去 */ private static void addnode(list<node> nodes, node node, int defaultexpandleval, int currentlevel) { nodes.add(node); if (defaultexpandleval >= currentlevel) { node.setexpand(true); } if (node.isleaf()) return; for (int i = 0; i < node.getchildrennodes().size(); i++) { addnode(nodes, node.getchildrennodes().get(i), defaultexpandleval, currentlevel + 1); } } /** * 获取所有的根节点 * * @param nodes * @return */ public static list<node> getrootnodes(list<node> nodes) { list<node> rootnodes = new arraylist<node>(); for (node node : nodes) { if (node.isroot()) { rootnodes.add(node); } } return rootnodes; } /** * 将泛型datas转换为node * * @param datas * @return * @throws illegalargumentexception * @throws illegalaccessexception */ public static <t> list<node> convertdata2nodes(list<t> datas, boolean ishide) throws illegalaccessexception, illegalargumentexception { list<node> nodes = new arraylist<node>(); node node = null; for (t t : datas) { int id = -1; int pid = -1; string name = null; class<? extends object> clazz = t.getclass(); field[] declaredfields = clazz.getdeclaredfields(); /** * 与mynodebean实体一一对应 */ for (field f : declaredfields) { if ("id".equals(f.getname())) { f.setaccessible(true); id = f.getint(t); } if ("pid".equals(f.getname())) { f.setaccessible(true); pid = f.getint(t); } if ("name".equals(f.getname())) { f.setaccessible(true); name = (string) f.get(t); } if ("desc".equals(f.getname())) { continue; } if ("length".equals(f.getname())) { continue; } if (id == -1 && pid == -1 && name == null) { break; } } node = new node(id, pid, name); node.sethidechecked(ishide); nodes.add(node); } /** * 比较nodes中的所有节点,分别添加children和parent */ for (int i = 0; i < nodes.size(); i++) { node n = nodes.get(i); for (int j = i + 1; j < nodes.size(); j++) { node m = nodes.get(j); if (n.getid() == m.getpid()) { n.getchildrennodes().add(m); m.setparent(n); } else if (n.getpid() == m.getid()) { n.setparent(m); m.getchildrennodes().add(n); } } } for (node n : nodes) { setnodeicon(n); } return nodes; } /** * 设置打开,关闭icon * * @param node */ public static void setnodeicon(node node) { if (node.getchildrennodes().size() > 0 && node.isexpand()) { node.seticon(r.drawable.tree_expand); } else if (node.getchildrennodes().size() > 0 && !node.isexpand()) { node.seticon(r.drawable.tree_econpand); } else node.seticon(-1); } public static void setnodechecked(node node, boolean ischecked) { // 自己设置是否选择 node.setchecked(ischecked); /** * 非叶子节点,子节点处理 */ setchildrennodechecked(node, ischecked); /** 父节点处理 */ setparentnodechecked(node); } /** * 非叶子节点,子节点处理 */ private static void setchildrennodechecked(node node, boolean ischecked) { node.setchecked(ischecked); if (!node.isleaf()) { for (node n : node.getchildrennodes()) { // 所有子节点设置是否选择 setchildrennodechecked(n, ischecked); } } } /** * 设置父节点选择 * * @param node */ private static void setparentnodechecked(node node) { /** 非根节点 */ if (!node.isroot()) { node rootnode = node.getparent(); boolean isallchecked = true; for (node n : rootnode.getchildrennodes()) { if (!n.ischecked()) { isallchecked = false; break; } } if (isallchecked) { rootnode.setchecked(true); } else { rootnode.setchecked(false); } setparentnodechecked(rootnode); } } }
核心的代码就是这些,希望对大家有帮助。
demo源码:http://xiazai.jb51.net/201611/yuanma/androidtreeview(jb51.net).rar
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。