前言
在本文开始之前请大家先看一下这篇文章: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:见购物页面
来张丑丑的图:
下面贴出部分代码:
(一).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.购物页面:
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.多级列表
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的列表
总结:
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