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

本文介绍了一种基于RecyclerView的多级列表实现方案,包括TreeRecyclerAdapter的使用方法及示例。该方案支持后台控制Item展示、展开折叠多级列表、使用装饰者模式扩展Adapter等功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

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

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

要点:

1.可以通过后台控制Item的展示.

2.TreeRecyclerAdapter,可以展开,折叠.多级展示

3.adapter可以使用装饰者模式进行扩展.支持EmptyAdapter.可以添加headview和footview

4.item的样式可以编写文档,type与Class进行对应,实现后台控置,相同Item复用.

思路:(包含第一篇的思路)

1.adapter应该只需要关心List 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 ------item的封装

BaseItemData-----item的数据要求.javabean需要继承该类.

* 3.factory

ItemConfig ----添加item的class,配置样式

Itemfactory----通过class生成BaseItem的工厂类

* 4.view

TreeItem ----树形列表的子item

TreeItemGroup ----树形列表的父item

TreeParent---TreeItemGroup 实现该接口

TreeSelectItemGroup---可以选中子item的TreeItemGroup. demo:见购物页面

来张丑丑的图:

b62ba20ee1b6dfbc4f2141b352da1c2e.png

下面贴出部分代码:

(一).BaseRecyclerAdapter :

/**

* 普通BaseRecyclerAdapter,itme无父子关系.

* 限定泛型为BaseItem的子类.

* 通过BaseItem去处理ViewHolder

*/

public class BaseRecyclerAdapter extends

RecyclerView.Adapter {

private List mDatas;//展示数据

private ItemManager 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 getDatas() {

if (mDatas == null) {

mDatas = new ArrayList<>();

}

return mDatas;

}

/**

* 需要手动setDatas(List datas),否则数据为空

* @param datas

*/

public void setDatas(List datas) {

if (datas != null) {

mDatas = datas;

getItemManager().notifyDataSetChanged();

}

}

}

(二).TreeRecyclerAdapter

/**

* Created by baozi on 2017/4/20.

* 树级结构recycleradapter.

* item之间有子父级关系,

*/

public class TreeRecyclerAdapter extends BaseRecyclerAdapter {

private TreeRecyclerViewType type;

/**

* 最初的数据.没有经过增删操作.

*/

private List 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 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 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 {

/**

* 当前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 extends BaseItem {

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 extends TreeItem

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 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

extends TreeItemGroup {

/**

* 选中的子item.只支持下一级,不支持下下级

*/

private List selectItems;

public List 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.购物页面:

6927595723a2db82f50b43014a480c55.png

Demo中的代码:

//拿到数据

List storeBean = initData();

//通过ItemFactory生成第一级Item,如果是通过后台控制,则不需要传Class

//List itemList = ItemFactory.createItemList(storeBean);

List 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 {

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

836a63f88959cad4ebdacbc8092c6737.png

Demo中的代码:

//拿到数据

List cityBeen = initData();

//通过ItemFactory生成List

List 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 {

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

686eae64317425349c24ee90067b6fb8.png

cb2d855b32e651051df962f9816c4058.png

总结:

1.我觉得像购物车那种页面挺复杂的,既然写了多级列表,何不扩展一个出来

2.RecyclerView的点击事件,看了很多封装,发现很多都是每次onBindViewHolder去重新设置一遍,感觉挺不好的.

3.我喜欢把数据集合让Adatper去持有,然后通过Adapter进行增删改查操作.直接在Activity里持有数据集合进行操作,我不是习惯(- -)

4.用的习惯没bug的才是好东西,如果你觉得实用或者能学到姿势,就点个赞把,哈哈.

5.大部分的逻辑都在Item中,由于拆分开了,会发现每个item的代码也不会很多

6.多级列表我已经用在某个项目里了(- -),还没发现什么问题(多级列表的简单使用- -)

下面附上Demo.详细代码

本地下载:http://xiazai.jb51.net/201705/yuanma/TreeRecyclerView(jb51.net).rar

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

时间: 2017-05-10

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值