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

Android中RecyclerView实现多级折叠列表效果(二)

程序员文章站 2023-12-04 17:54:22
前言 在本文开始之前请大家先看一下这篇文章: 上面的这篇文章是之前写的,里面发现有很多不好用地方,也学到些新姿势,改动了许多地方。下面来看看详细的介绍: 要点: &...

前言

在本文开始之前请大家先看一下这篇文章:

上面的这篇文章是之前写的,里面发现有很多不好用地方,也学到些新姿势,改动了许多地方。下面来看看详细的介绍:

要点:

      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:见购物页面

来张丑丑的图:

Android中RecyclerView实现多级折叠列表效果(二)

下面贴出部分代码:

(一).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.购物页面:

Android中RecyclerView实现多级折叠列表效果(二)

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.多级列表

Android中RecyclerView实现多级折叠列表效果(二)

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的列表

Android中RecyclerView实现多级折叠列表效果(二)

Android中RecyclerView实现多级折叠列表效果(二)

总结:

      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

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对的支持。