Android中RecyclerView实现多级折叠列表效果(二)
前言
在本文开始之前请大家先看一下这篇文章:
上面的这篇文章是之前写的,里面发现有很多不好用地方,也学到些新姿势,改动了许多地方。下面来看看详细的介绍:
要点:
1.可以通过后台控制item的展示.
2.treerecycleradapter,可以展开,折叠.多级展示
3.adapter可以使用装饰者模式进行扩展.支持emptyadapter.可以添加headview和footview
4.item的样式可以编写文档,type与class进行对应,实现后台控置,相同item复用.
思路:(包含第一篇的思路)
1.adapter应该只需要关心list<baseitem> datas 的内容
2.把每个item看成独立的个体. 布局样式,每行所占比,onbindviewholder由各个item实现。
3.每一个item应该只关心自己的数据和自己的下一级的数据,不会去关心上上级,下下级
4.展开的实现,点击时item把子数据拿出来,然后添加到adapter的datas中,变成同级,因为只会展开自己的下级数据。
5.折叠的实现,拿到下级数据(可以理解因为一个文件夹下文件),然后从adapter的datas中删除这些数据。
6.后台控制可以通过初始化注册的方法,将item的class注册.保存到集合里
7.后台返回字段,获取对应class文件,通过class.newinstance()
方法构建实例.
8.将viewholder与adapter写成通用的,不需要再写多个adatper与viewholder,只需要写多个baseitem.与baseitamdata(javabean).
目录介绍
+ 1.adapter * wapper------扩展的wapper, * emptywapper --------当无数据时显示页面. * headerandfootwapper --------添加头部view和尾部view - baserecycleradapter --------封装的adatper基类 - itemmanager --------接口,管理adatper刷新,增删操作 - treerecycleradapter ----多级列表,树形结构的adapter - treerecyclerviewtype ----多级列表的显示样式,枚举 - viewholder----封装的通用viewhodler * 2.base baseitem<d extends baseitemdata> ------item的封装 baseitemdata-----item的数据要求.javabean需要继承该类. * 3.factory itemconfig ----添加item的class,配置样式 itemfactory----通过class生成baseitem的工厂类 * 4.view treeitem ----树形列表的子item treeitemgroup ----树形列表的父item treeparent---treeitemgroup 实现该接口 treeselectitemgroup---可以选中子item的treeitemgroup. demo:见购物页面
来张丑丑的图:
下面贴出部分代码:
(一).baserecycleradapter :
/** * 普通baserecycleradapter,itme无父子关系. * 限定泛型为baseitem的子类. * 通过baseitem去处理viewholder */ public class baserecycleradapter<t extends baseitem> extends recyclerview.adapter<viewholder> { private list<t> mdatas;//展示数据 private itemmanager<t> mitemmanager; private checkitem mcheckitem; @override public viewholder oncreateviewholder(viewgroup parent, int viewtype) { //看源码,这里的parent就是recyclerview,所以不会为null.可以通过它拿到context return viewholder.createviewholder(parent.getcontext(), parent, viewtype); } @override public void onbindviewholder(viewholder holder, int position) { t t = getdatas().get(position); //检查是否绑定了itemmanage,因为item需要通过itemmanage告诉adapter刷新,增删 checkitemmanage(t); //具体onbindviewholder放到item里面去实现 t.onbindviewholder(holder); //实现点击事件 onbindviewholderclick(holder); } /** * 实现item的点击事件 * * @param holder 绑定点击事件的viewholder */ public void onbindviewholderclick(final viewholder holder) { //判断当前holder是否已经设置了点击事件 if (!holder.itemview.hasonclicklisteners()) { holder.itemview.setonclicklistener(new view.onclicklistener() { @override public void onclick(view v) { //获得holder的position int layoutposition = holder.getlayoutposition(); //检查position是否可以点击 if (getcheckitem().checkposition(layoutposition)) { //检查并得到真实的position int itemposition = getcheckitem().getaftercheckingposition(layoutposition); //拿到对应item,回调. getdatas().get(itemposition).onclick(); } } }); } } /** * 这里将layoutid作为type,因为layoutid不可能相同,个人觉得可以作为item的标志 * @param position * @return */ @override public int getitemviewtype(int position) { return mdatas.get(position).getlayoutid(); } @override public int getitemcount() { return mdatas == null ? 0 : mdatas.size(); } public list<t> getdatas() { if (mdatas == null) { mdatas = new arraylist<>(); } return mdatas; } /** * 需要手动setdatas(list<t> datas),否则数据为空 * @param datas */ public void setdatas(list<t> datas) { if (datas != null) { mdatas = datas; getitemmanager().notifydatasetchanged(); } } }
(二).treerecycleradapter
/** * created by baozi on 2017/4/20. * 树级结构recycleradapter. * item之间有子父级关系, */ public class treerecycleradapter<t extends treeitem> extends baserecycleradapter<t> { private treerecyclerviewtype type; /** * 最初的数据.没有经过增删操作. */ private list<t> initialdatas; @override public void onbindviewholderclick(final viewholder holder) { //判断是否已有点击监听 if (!holder.itemview.hasonclicklisteners()) { holder.itemview.setonclicklistener(new view.onclicklistener() { @override public void onclick(view v) { //获得holder的layoutposition. int layoutposition = holder.getlayoutposition(); //判断是否需要点击 if (getcheckitem().checkposition(layoutposition)) { int itemposition = getcheckitem().getaftercheckingposition(layoutposition); //展开,折叠和item点击不应该同时响应事件. if (type != treerecyclerviewtype.show_all) { //展开,折叠 expandorcollapse(itemposition); } else { //点击事件 t item = getdatas().get(itemposition); treeitemgroup itemparentitem = item.getparentitem(); //判断上一级是否需要拦截这次事件,只处理当前item的上级,不关心上上级如何处理. if (itemparentitem != null && itemparentitem.oninterceptclick(item)) { return; } item.onclick(); } } } }); } } @override public void setdatas(list<t> datas) { //保存未修改过的list initialdatas = datas; //如果展开显示全部,则需要递归处理添加所以的item if (type == treerecyclerviewtype.show_all) { for (int i = 0; i < datas.size(); i++) { t t = datas.get(i); getdatas().add(t); //判断item是否是treeitemgroup if (t instanceof treeitemgroup) { list childs = ((treeitemgroup) t).getallchilds(); if (childs != null) { //添加到item的后面. getdatas().addall(childs); } } } } else { super.setdatas(datas); } } /** * 相应recyclerview的点击事件 展开或关闭某节点 * * @param position 触发的条目 */ private void expandorcollapse(int position) { t baseitem = getdatas().get(position); if (baseitem instanceof treeitemgroup && ((treeitemgroup) baseitem).iscanchangeexpand()) { treeitemgroup treeparentitem = (treeitemgroup) baseitem; boolean expand = treeparentitem.isexpand(); list<t> allchilds = treeparentitem.getallchilds(); if (expand) { getdatas().removeall(allchilds); treeparentitem.oncollapse(); treeparentitem.setexpand(false); } else { getdatas().addall(position + 1, allchilds); treeparentitem.onexpand(); treeparentitem.setexpand(true); } getitemmanager().notifydatasetchanged(); } } }
(三).baseitem(部分get.set代码省略)
/** * item的基类 */ public abstract class baseitem<d extends baseitemdata> { /** * 当前item的数据 */ protected d data; /** * item在每行中的spansize * 默认为0,如果为0则占满一行 * * @return 所占值, 比如recyclerview的列数为6, item需要占一半宽度, 就设置3 */ private int spansize; /** * 可以通过itemmanager ,操作adatper * @return */ private itemmanager mitemmanager; public int getlayoutid() { if (initlayoutid() <= 0) { throw new resources.notfoundexception("请设置布局id"); } return initlayoutid(); } /** * 子类需要实现该方法,并返回item的布局id * * @return 返回布局id.如果返回0,则会抛出异常 */ protected abstract int initlayoutid(); /** * 抽象holder的绑定 */ public abstract void onbindviewholder(viewholder viewholder); /** * 当前条目的点击回调 * 如果不需要点击事件,则可以不复写该方法. */ public void onclick() { }
(四).treeitem
/** * 组合模式 * treerecycleradapter的item */ public abstract class treeitem<d extends baseitemdata> extends baseitem<d> { private treeitemgroup parentitem; public void setparentitem(treeitemgroup parentitem) { this.parentitem = parentitem; } /** * 获取当前item的父级 * * @return */ @nullable public treeitemgroup getparentitem() { return parentitem; } }
(五).treeitemgroup
/** * created by baozi on 2016/12/22. * 拥有子集 * 子集可以是parent,也可以是child */ public abstract class treeitemgroup<d extends baseitemdata> extends treeitem<d> implements treeparent { /** * 持有的子item */ private list<? extends baseitem> childs; /** * 是否展开 */ private boolean isexpand; /** * 能否展开 */ protected boolean iscanchangeexpand = true; /** * 展开 */ @override public void onexpand() { } /** * 折叠 */ @override public void oncollapse() { } /** * 获得自己的childs. * @return */ @nullable public list<? extends baseitem> getchilds() { return childs; } /** * 获得所有childs,包括子item的childs * @return */ @nullable public list<? extends baseitem> getallchilds() { if (getchilds() == null) { return null; } arraylist<baseitem> baseitems = new arraylist<>(); for (int i = 0; i < childs.size(); i++) { //下级 baseitem baseitem = childs.get(i); baseitems.add(baseitem); //判断是否还有下下级,并且处于expand的状态 if (baseitem instanceof treeitemgroup && ((treeitemgroup) baseitem).isexpand()) { //调用下级的getallchilds遍历,相当于递归遍历 list list = ((treeitemgroup) baseitem).getallchilds(); if (list != null && list.size() > 0) { baseitems.addall(list); } } } return baseitems; } public int getchildscount() { return childs == null ? 0 : childs.size(); } /** * 初始化子集 * * @param data * @return */ protected abstract list<? extends baseitem> initchildslist(d data); /** * 是否消费child的click事件 * * @param child 具体click的item * @return 返回true代表消费此次事件,child不会走onclick(),返回false说明不消费此次事件,child依然会走onclick() */ public boolean oninterceptclick(treeitem child) { return false; }
(六).treeselectitemgroup
/** * created by baozi on 2016/12/22. * 可以选中子item的treeitemgroup,点击的item会保存起来.可以通过 getselectitems()获得选中item */ public abstract class treeselectitemgroup<d extends baseitemdata> extends treeitemgroup<d> { /** * 选中的子item.只支持下一级,不支持下下级 */ private list<baseitem> selectitems; public list<baseitem> getselectitems() { if (selectitems == null) { selectitems = new arraylist<>(); } return selectitems; } /** * 是否有选中item, * @return */ public boolean ishavecheck() { return !getselectitems().isempty(); } @override public boolean oninterceptclick(treeitem child) { //单选 if (selectflag() == selectflag.single_choice) { //如果已经有选中的,则替换 if (getselectitems().size() != 0) { getselectitems().set(0, child); } else { //没有选中的则添加 getselectitems().add(child); } } else { //判断是否已添加. int index = getselectitems().indexof(child); if (index == -1) {//不存在则添加 getselectitems().add(child); } else {//存在则删除 getselectitems().remove(index); } } return super.oninterceptclick(child); } /** * 必须指定选中样式 * @return */ public abstract selectflag selectflag(); /** * 决定treeselectitemgroup的选中样式 */ public enum selectflag { /** * 单选 */ single_choice, /** * 多选 */ multiple_choice }
具体的使用实例效果:
1.购物页面:
demo中的代码:
//拿到数据 list<storebean> storebean = initdata(); //通过itemfactory生成第一级item,如果是通过后台控制,则不需要传class //list<shoptitileitem> itemlist = itemfactory.createitemlist(storebean); list<shoptitileitem> itemlist = itemfactory.createitemlist(storebean, shoptitileitem.class); //创建treerecycleradapter madapter = new treerecycleradapter<>(); //设置adapter的显示样式 madapter.settype(treerecyclerviewtype.show_all); //将数据设置到adapter中 madapter.setdatas(itemlist); //设置adapter mrecyclerview.setadapter(madapter); /** * item的代码 * created by baozi on 2016/12/22. */ public class shoptitileitem extends treeselectitemgroup<storebean> { @override protected list<? extends baseitem> initchildslist(storebean data) { return itemfactory.createtreeitemlist(data.getshoplistbeen(), this); } @override protected int initlayoutid() { return r.layout.item_shopcart_title; } @override public void onbindviewholder(viewholder holder) { holder.setchecked(r.id.cb_ischeck, ishavecheck()); } @override public void onclick() { if (!ishavecheck()) { getselectitems().clear(); getselectitems().addall(getchilds()); } else { getselectitems().clear(); } int size = getchilds().size(); for (int i = 0; i < size; i++) { shoplistbean data = (shoplistbean) getchilds().get(i).getdata(); data.setcheck(ishavecheck()); } getitemmanager().notifydatasetchanged(); } @override public selectflag selectflag() { return selectflag.multiple_choice; } @override public boolean canexpandorcollapse() { return false; }
2.多级列表
demo中的代码:
//拿到数据 list<citybean> citybeen = initdata(); //通过itemfactory生成list<baseitem> list<onetreeitemparent> itemlist = itemfactory.createitemlist(citybeen); treerecycleradapter treerecycleradapter = new treerecycleradapter(); //设置数据 treerecycleradapter.setdatas(itemlist); recyclerview.setadapter(treerecycleradapter); /** *item的代码 * created by baozi on 2016/12/8. */ public class onetreeitemparent extends treeitemgroup<citybean> { @override public list<? extends treeitem> initchildslist(citybean data) { return itemfactory.createtreeitemlist(data.getcitys(), twotreeitemparent.class, this); } @override public int initlayoutid() { return r.layout.itme_one; } @override public void onbindviewholder(viewholder holder) { holder.settext(r.id.tv_content, data.getprovincename()); } @override public boolean canexpandorcollapse() { return false; } }
3.多种type的列表
总结:
1.我觉得像购物车那种页面挺复杂的,既然写了多级列表,何不扩展一个出来
2.recyclerview的点击事件,看了很多封装,发现很多都是每次onbindviewholder去重新设置一遍,感觉挺不好的.
3.我喜欢把数据集合让adatper去持有,然后通过adapter进行增删改查操作.直接在activity里持有数据集合进行操作,我不是习惯(- -)
4.用的习惯没bug的才是好东西,如果你觉得实用或者能学到姿势,就点个赞把,哈哈.
5.大部分的逻辑都在item中,由于拆分开了,会发现每个item的代码也不会很多
6.多级列表我已经用在某个项目里了(- -),还没发现什么问题(多级列表的简单使用- -)
下面附上demo.详细代码
github传送门:treerecyclerview
本地下载:http://xiazai.jb51.net/201705/yuanma/treerecyclerview(jb51.net).rar
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对的支持。
推荐阅读
-
Android中RecyclerView实现多级折叠列表效果(二)
-
Android中RecyclerView实现多级折叠列表效果(TreeRecyclerView)
-
Android单个RecyclerView实现列表嵌套的效果
-
Android中RecyclerView实现多级折叠列表效果(TreeRecyclerView)
-
Android中RecyclerView实现多级折叠列表效果(二)
-
Android利用RecyclerView实现列表倒计时效果
-
Android实现多级列表中的新建功能
-
Android RecyclerView实现数据列表展示效果
-
Android如何利用RecyclerView实现列表倒计时效果实例代码
-
Android RecyclerView优雅实现复杂列表布局(二)