全网整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:400-708-3566

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

前言

在本文开始之前请大家先看一下这篇文章:https://www./article/113510.htm

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

要点:

      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./201705/yuanma/TreeRecyclerView().rar

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


# recyclerview多级列表  # recyclerview  # 折叠  # recyclerview分组列表  # Android Studio使用recyclerview实现展开和折叠功能(在之前的微信页面基础之上  # Android中RecyclerView实现多级折叠列表效果(TreeRecyclerView)  # android给RecyclerView加上折叠的效果示例  # 一文搞懂Android RecyclerView点击展开、折叠效果的实现代码  # 自己的  # 可以通过  # 递归  # 不需要  # 这篇文章  # 判断是否  # 多个  # 子类  # 遍历  # 所占  # 绑定  # 回调  # 单选  # 心上  # 都是  # 也不  # 好了  # 还没  # 不可能  # 看了 


相关文章: 南宁网站建设制作定制,南宁网站建设可以定制吗?  C++中引用和指针有什么区别?(代码说明)  名字制作网站免费,所有小说网站的名字?  整人网站在线制作软件,整蛊网站退不出去必须要打我是白痴才能出去?  如何快速搭建安全的FTP站点?  矢量图网站制作软件,用千图网的一张矢量图做公司app首页,该网站并未说明版权等问题,这样做算不算侵权?应该如何解决?  黑客如何通过漏洞一步步攻陷网站服务器?  如何选购建站域名与空间?自助平台全解析  ,购物网站怎么盈利呢?  营销式网站制作方案,销售哪个网站招聘效果最好?  建站主机选购指南与交易推荐:核心配置解析  建站之星如何助力网站排名飙升?揭秘高效技巧  宝塔建站教程:一键部署配置流程与SEO优化实战指南  如何快速启动建站代理加盟业务?  外贸公司网站制作,外贸网站建设一般有哪些步骤?  C++时间戳转换成日期时间的步骤和示例代码  视频网站制作教程,怎么样制作优酷网的小视频?  ,南京靠谱的征婚网站?  大连网站制作费用,大连新青年网站,五年四班里的视频怎样下载啊?  大型企业网站制作流程,做网站需要注册公司吗?  历史网站制作软件,华为如何找回被删除的网站?  如何快速生成高效建站系统源代码?  简易网站制作视频教程,使用记事本编写一个简单的网页html文件?  建站之星安装后如何配置SEO及设计样式?  佛山网站制作系统,佛山企业变更地址网上办理步骤?  武汉网站设计制作公司,武汉有哪些比较大的同城网站或论坛,就是里面都是武汉人的?  如何零基础开发自助建站系统?完整教程解析  建站主机如何选?性能与价格怎样平衡?  如何快速选择适合个人网站的云服务器配置?  建站之星安装需要哪些步骤及注意事项?  代刷网站制作软件,别人代刷火车票靠谱吗?  深圳网站制作培训,深圳哪些招聘网站比较好?  怀化网站制作公司,怀化新生儿上户网上办理流程?  枣阳网站制作,阳新火车站打的到仙岛湖多少钱?  成都网站制作价格表,现在成都广电的单独网络宽带有多少的,资费是什么情况呢?  建站之星安装步骤有哪些常见问题?  已有域名和空间,如何快速搭建网站?  建站主机无法访问?如何排查域名与服务器问题  如何快速配置高效服务器建站软件?  建站三合一如何选?哪家性价比更高?  jQuery 常见小例汇总  企业在线网站设计制作流程,想建设一个属于自己的企业网站,该如何去做?  北京制作网站的公司,北京铁路集团官方网站?  如何用好域名打造高点击率的自主建站?  如何在云服务器上快速搭建个人网站?  ,在苏州找工作,上哪个网站比较好?  湖北网站制作公司有哪些,湖北清能集团官网?  开心动漫网站制作软件下载,十分开心动画为何停播?  建站主机是否等同于虚拟主机?  手机怎么制作网站教程步骤,手机怎么做自己的网页链接? 

您的项目需求

*请认真填写需求信息,我们会在24小时内与您取得联系。