BRVAH学习使用
一、前言: GitHub 地址:
官方网站:
BRVAH是一个强大的RecyclerAdapter框架(什么是RecyclerView?),它能节约开发者大量的开发时间,集成了大部分列表常用需求解决方案。为什么会有它?请查看「Android开源框架BRVAH由来篇」该框架于2016年4月10号发布的第1个版本到现在已经一年多了,经历了800多次代码提交,140多次版本打包,修复了1000多个问题,获得了9000多star,非常感谢大家的使用以及反馈。
本篇为BRVAH的使用指南以及包含常见问题会第一时间更新最新的使用方法。最新版本请查看releases,由于持续更新。
二、基本使用:
1. 框架的引入 先在 build.gradle(Project:XXXX) 的 repositories 添加:
allprojects {
repositories {
...
maven { url "https://jitpack.io" }
}
}
然后在 build.gradle(Module:app) 的 dependencies 添加:
dependencies {
implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.47'
}
更新说明:https://github.com/CymChad/BaseRecyclerViewAdapterHelper/releases
2. 注意: 一旦出现加载失败的情况,只有两种情况:配置没配置好 1. 配置没配置好,有几种情况:
- 只配置了dependencies
- 配置repositories,但是位置错了,build.gradle(Project:XXXX) 文件下的repositories有两个,一个是buildscript下面的,一个是allprojects下面的,要配置到allprojects下面才是对的。
- 版本号前面多一个v,这个是我的锅,在2.1.2版本之前都是带v的,之后(包含2.1.2)都不需要带v。
- 有的电脑需要;
- 有的要设置代理;
- 有的电脑就是网络不好;
和原始的adapter相对,减少70%的代码量。
文章图片
4.使用代码
public class HomeAdapter extends BaseQuickAdapter {
public HomeAdapter(int layoutResId, List data) {
super(layoutResId, data);
}@Override
protected void convert(BaseViewHolder helper, HomeItem item) {
helper.setText(R.id.text, item.getTitle());
helper.setImageResource(R.id.icon, item.getImageResource());
// 加载网络图片
Glide.with(mContext).load(item.getUserAvatar()).crossFade().into((ImageView) helper.getView(R.id.iv));
}
}
注:如果想深入了解的原理可以查看RecyclerView.Adapter优化了吗?
使用
首先需要继承BaseQuickAdapter,然后BaseQuickAdapter第一个泛型Status是数据实体类型,第二个BaseViewHolder是ViewHolder其目的是为了支持扩展ViewHolder。
赋值
可以直接使用viewHolder对象点相关方法通过传入viewId和数据进行,方法支持链式调用。如果是加载网络图片或自定义view可以通过viewHolder.getView(viewId)获取该控件。
常用方法
- viewHolder.getLayoutPosition() 获取当前item的position
这些问题不是使用该库的问题,但是经常有人问这些问题,所以特意写出来,帮助后续遇到以下问题的开发者们。为什么有数据不显示?
请检查一下你的RecyclerView是否设置了LayoutManager。
为什么有10条数据,只显示1条?
请检查一下item的布局最外层的Layout是不是layout_height设置了match_parent.
数据状态错乱
这个问题无论是RecyclerView还是ListView不做处理都会出现问题,这个本质上是由于布局重用机制导致的,解决办法是通过数据状态来控制控件的状态,一定要设置状态无论什么状态,if和else是少不了的,
如下代码:
if(entity.isCheck){
checkBox.isChecked(true);
} else {
checkBox.isChecked(false);
}
解决缓存问题案例:
- 处理文本框和单选的缓存问题
- 实现三级伸缩,并解决多选框的复用
文章图片
Item的点击事件
adapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
@Override
public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
Log.d(TAG, "onItemClick: ");
Toast.makeText(ItemClickActivity.this, "onItemClick" + position, Toast.LENGTH_SHORT).show();
}
});
Item的长按事件
adapter.setOnItemLongClickListener(new BaseQuickAdapter.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(BaseQuickAdapter adapter, View view, int position) {
Log.d(TAG, "onItemLongClick: ");
Toast.makeText(ItemClickActivity.this, "onItemLongClick" + position, Toast.LENGTH_SHORT).show();
return false;
}
});
注意:嵌套recycleView的情况下需要使用你使用 adapter. setOnItemClickListener 来设置点击事件,如果使用recycleView.addOnItemTouchListener会累计添加的。
Item子控件的点击事件
首先在adapter的convert方法里面通过viewHolder.addOnClickListener绑定一下的控件id
@Override
protected void convert(BaseViewHolder viewHolder, Status item) {
viewHolder.setText(R.id.tweetName, item.getUserName())
.setText(R.id.tweetText, item.getText())
.setText(R.id.tweetDate, item.getCreatedAt())
.setVisible(R.id.tweetRT, item.isRetweet())
.addOnClickListener(R.id.tweetAvatar)
.addOnClickListener(R.id.tweetName)
.linkify(R.id.tweetText);
}
然后在设置
adapter.setOnItemChildClickListener(new BaseQuickAdapter.OnItemChildClickListener() {
@Override
public boolean onItemChildClick(BaseQuickAdapter adapter, View view, int position) {
Log.d(TAG, "onItemChildClick: ");
Toast.makeText(ItemClickActivity.this, "onItemChildClick" + position, Toast.LENGTH_SHORT).show();
return false;
}
});
Item子控件的长按事件
步骤同上使用方法不同。
- adapter中绑定方法将addOnClickListener改成addOnLongClickListener.
- 设置点击事件方法setOnItemChildClickListener改成setOnItemChildLongClickListener
如果需要在点击事件中获取其他子控件可以使用:
getViewByPosition(RecyclerView recyclerView, int position, @IdRes int viewId)注意:如果有header的话需要处理一下position加上 headerlayoutcount。
7.添加列表加载动画
文章图片
开启动画(默认为渐显效果)
adapter.openLoadAnimation();默认提供5种方法(渐显、缩放、从下到上,从左到右、从右到左)
public static final int ALPHAIN = 0x00000001;
/**
* Use with {@link #openLoadAnimation}
*/
public static final int SCALEIN = 0x00000002;
/**
* Use with {@link #openLoadAnimation}
*/
public static final int SLIDEIN_BOTTOM = 0x00000003;
/**
* Use with {@link #openLoadAnimation}
*/
public static final int SLIDEIN_LEFT = 0x00000004;
/**
* Use with {@link #openLoadAnimation}
*/
public static final int SLIDEIN_RIGHT = 0x00000005;
切换动画
quickAdapter.openLoadAnimation(BaseQuickAdapter.ALPHAIN);
自定义动画
quickAdapter.openLoadAnimation(new BaseAnimation() {
@Override
public Animator[] getAnimators(View view) {
return new Animator[]{
ObjectAnimator.ofFloat(view, "scaleY", 1, 1.1f, 1),
ObjectAnimator.ofFloat(view, "scaleX", 1, 1.1f, 1)
};
}
});
动画默认只执行一次,如果想重复执行可设置
mQuickAdapter.isFirstOnly(false);注:如果想深入了解的原理可以查看BaseRecyclerAdapter之添加动画(策略模式)
设置不显示动画数量
adapter.setNotDoAnimationCount(count);首次到界面的item都依次执行加载动画
由于进入界面的item都是很快的速度进来的所以不会出现滑动显示的依次执行动画效果,这个时候会一起执行动画,如果觉得这样的效果不好可以使用setNotDoAnimationCount设置第一屏item不执行动画,但是如果需要依次执行动画可以重写startAnim让第一个屏幕的item动画延迟执行即可。
@Override
protected void startAnim(Animator anim, int index) {
super.startAnim(anim, index);
if (index < count)
anim.setStartDelay(index * 150);
}
8.添加头部、尾部
文章图片
添加
mQuickAdapter.addHeaderView(getView());注:如果想深入了解的原理可以查看BaseRecyclerAdapter之添加不同布局(头部尾部)
mQuickAdapter.addFooterView(getView());
删除指定view
mQuickAdapter.removeHeaderView(getView);删除所有
mQuickAdapter.removeFooterView(getView);
mQuickAdapter.removeAllHeaderView();默认出现了头部就不会显示Empty,和尾部,配置以下方法也支持同时显示:
mQuickAdapter.removeAllFooterView();
setHeaderAndEmpty默认头部尾部都是占满一行,如果需要不占满可以配置:
setHeaderFooterEmpty
setHeaderViewAsFlow9.自动加载 上拉加载
setFooterViewAsFlow
文章图片
// 滑动最后一个Item的时候回调onLoadMoreRequested方法默认第一次加载会进入回调,如果不需要可以配置:
setOnLoadMoreListener(RequestLoadMoreListener);
mQuickAdapter.disableLoadMoreIfNotFullPage();回调处理代码
mQuickAdapter.setOnLoadMoreListener(new BaseQuickAdapter.RequestLoadMoreListener() {
@Override public void onLoadMoreRequested() {
mRecyclerView.postDelayed(new Runnable() {
@Override
public void run() {
if (mCurrentCounter >= TOTAL_COUNTER) {
//数据全部加载完毕
mQuickAdapter.loadMoreEnd();
} else {
if (isErr) {
//成功获取更多数据
mQuickAdapter.addData(DataServer.getSampleData(PAGE_SIZE));
mCurrentCounter = mQuickAdapter.getData().size();
mQuickAdapter.loadMoreComplete();
} else {
//获取更多数据失败
isErr = true;
Toast.makeText(PullToRefreshUseActivity.this, R.string.network_err, Toast.LENGTH_LONG).show();
mQuickAdapter.loadMoreFail();
}
}
}}, delayMillis);
}
}, mReyclerView);
加载完成(注意不是加载结束,而是本次数据加载结束并且还有下页数据)
mQuickAdapter.loadMoreComplete();加载失败
mQuickAdapter.loadMoreFail();加载结束
mQuickAdapter.loadMoreEnd();注意:如果上拉结束后,下拉刷新需要再次开启上拉监听,需要使用setNewData方法填充数据。
打开或关闭加载(一般用于下拉的时候做处理,因为上拉下拉不能同时操作)
mQuickAdapter.setEnableLoadMore(boolean);预加载
// 当列表滑动到倒数第N个Item的时候(默认是1)回调onLoadMoreRequested方法设置自定义加载布局
mQuickAdapter.setPreLoadNumber(int);
mQuickAdapter.setLoadMoreView(new CustomLoadMoreView());
public final class CustomLoadMoreView extends LoadMoreView {@Override public int getLayoutId() {
return R.layout.view_load_more;
}/**
* 如果返回true,数据全部加载完毕后会隐藏加载更多
* 如果返回false,数据全部加载完毕后会显示getLoadEndViewId()布局
*/
@Override public boolean isLoadEndGone() {
return true;
}@Override protected int getLoadingViewId() {
return R.id.load_more_loading_view;
}@Override protected int getLoadFailViewId() {
return R.id.load_more_load_fail_view;
}/**
* isLoadEndGone()为true,可以返回0
* isLoadEndGone()为false,不能返回0
*/
@Override protected int getLoadEndViewId() {
return 0;
}
}
下拉加载(符合聊天软件下拉历史数据需求)
【BRVAH学习使用】
设置开启开关
mAdapter.setUpFetchEnable(true);设置监听
mAdapter.setUpFetchListener(new BaseQuickAdapter.UpFetchListener() {
@Override
public void onUpFetch() {
startUpFetch();
}
});
private void startUpFetch() {
count++;
/**
* set fetching on when start network request.
*/
mAdapter.setUpFetching(true);
/**
* get data from internet.
*/
mRecyclerView.postDelayed(new Runnable() {
@Override
public void run() {
mAdapter.addData(0, genData());
/**
* set fetching off when network request ends.
*/
mAdapter.setUpFetching(false);
/**
* set fetch enable false when you don't need anymore.
*/
if (count > 5) {
mAdapter.setUpFetchEnable(false);
}
}
}, 300);
}
开始加载的位置
mAdapter.setStartUpFetchPosition(2);10. 分组布局 实体类必须继承SectionEntity
public class MySection extends SectionEntity
adapter构造需要传入两个布局id,第一个是item的,第二个是head的,在convert方法里面加载item数据,在convertHead方法里面加载head数据.
public class SectionAdapter extends BaseSectionQuickAdapter {
public SectionAdapter(int layoutResId, int sectionHeadResId, List data) {
super(layoutResId, sectionHeadResId, data);
}
@Override
protected void convert(BaseViewHolder helper, MySection item) {
helper.setImageUrl(R.id.iv, (String) item.t);
}
@Override
protected void convertHead(BaseViewHolder helper,final MySection item) {
helper.setText(R.id.header, item.header);
helper.setOnClickListener(R.id.more, new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context,item.header+"more..",Toast.LENGTH_LONG).show();
}
});
}
11. 多布局
文章图片
实体类必须实现MultiItemEntity,在设置数据的时候,需要给每一个数据设置itemType
public class MultipleItem implements MultiItemEntity {
public static final int TEXT = 1;
public static final int IMG = 2;
private int itemType;
public MultipleItem(int itemType) {
this.itemType = itemType;
}@Override
public int getItemType() {
return itemType;
}
}
在构造里面addItemType绑定type和layout的关系
public class MultipleItemQuickAdapter extends BaseMultiItemQuickAdapter {public MultipleItemQuickAdapter(List data) {
super(data);
addItemType(MultipleItem.TEXT, R.layout.text_view);
addItemType(MultipleItem.IMG, R.layout.image_view);
}@Override
protected void convert(BaseViewHolder helper, MultipleItem item) {
switch (helper.getItemViewType()) {
case MultipleItem.TEXT:
helper.setImageUrl(R.id.tv, item.getContent());
break;
case MultipleItem.IMG:
helper.setImageUrl(R.id.iv, item.getContent());
break;
}
}}
注:如果想深入了解的原理可以查看BaseRecyclerAdapter之添加不同布局(优化篇)
如果考虑到在GridLayoutManager复用item问题可以配置:
multipleItemAdapter.setSpanSizeLookup(new BaseQuickAdapter.SpanSizeLookup() {
@Override
public int getSpanSize(GridLayoutManager gridLayoutManager, int position) {
return data.get(position).getSpanSize();
}
});
如果大家觉得这种多布局方式有点由于耦合了实体类,还有支持另外一种多布局方式具体可查看更便捷的多布局, 为 BaseQuickAdapter 设置代理.
如果使用多布局出现这个NotFoundException异常,有可能是addItemType()两个参数写反了。
12. 设置空布局
文章图片
// 没有数据的时候默认显示该布局
mQuickAdapter.setEmptyView(getView());
如果用网格布局的话,设置空布局就不能给全屏,可以使用瀑布流布局。
13. 添加拖拽、滑动删除
文章图片
拖拽和滑动删除的回调方法
OnItemDragListener onItemDragListener = new OnItemDragListener() {
@Override
public void onItemDragStart(RecyclerView.ViewHolder viewHolder, int pos){}
@Override
public void onItemDragMoving(RecyclerView.ViewHolder source, int from, RecyclerView.ViewHolder target, int to) {}
@Override
public void onItemDragEnd(RecyclerView.ViewHolder viewHolder, int pos) {}
}OnItemSwipeListener onItemSwipeListener = new OnItemSwipeListener() {
@Override
public void onItemSwipeStart(RecyclerView.ViewHolder viewHolder, int pos) {}
@Override
public void clearView(RecyclerView.ViewHolder viewHolder, int pos) {}
@Override
public void onItemSwiped(RecyclerView.ViewHolder viewHolder, int pos) {}
};
adapter需要继承
BaseItemDraggableAdapter
public class ItemDragAdapter extends BaseItemDraggableAdapter {
public ItemDragAdapter(List data) {
super(R.layout.item_draggable_view, data);
}@Override
protected void convert(BaseViewHolder helper, String item) {
helper.setText(R.id.tv, item);
}
}
Activity使用代码
mAdapter = new ItemDragAdapter(mData);
ItemDragAndSwipeCallback itemDragAndSwipeCallback = new ItemDragAndSwipeCallback(mAdapter);
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(itemDragAndSwipeCallback);
itemTouchHelper.attachToRecyclerView(mRecyclerView);
// 开启拖拽
mAdapter.enableDragItem(itemTouchHelper, R.id.textView, true);
mAdapter.setOnItemDragListener(onItemDragListener);
// 开启滑动删除
mAdapter.enableSwipeItem();
mAdapter.setOnItemSwipeListener(onItemSwipeListener);
默认不支持多个不同的 ViewType 之间进行拖拽,如果开发者有所需求:
重写ItemDragAndSwipeCallback里的onMove()方法,return true即可14. 自定义ViewHolder 需要继承BaseViewHolder
public class MovieViewHolder extends BaseViewHolder然后修改adapter的第二个泛型为自定义的ViewHolder
public class DataBindingUseAdapter extends BaseQuickAdapter注意:需要单独建一个外部类继承BaseViewHolder,否则部分机型会出现ClassCastException,如果是内部类的构造方法要是public,定义的那个类也最好是public。
文章图片
15. 混淆
-keep class com.chad.library.adapter.** {
*;
}
-keep public class * extends com.chad.library.adapter.base.BaseQuickAdapter
-keep public class * extends com.chad.library.adapter.base.BaseViewHolder
-keepclassmembersclass **$** extends com.chad.library.adapter.base.BaseViewHolder {
(...);
}
16 . 扩展框架 由于adapter本身能力有限,我们又不想耦合view层所以有些需求是现实不了,于是合作了一些优秀开源库,为开发者提供更多可能性。以下扩展框架都是有结合BRVAH的demo。
- PinnedSectionItemDecoration:一个强大的粘性标签库
- EasyRefreshLayout:这个库让你轻松实现下拉刷新和上拉更多
- EasySwipeMenuLayout:独立的侧滑删除
Demo下载地址:
链接:https://www.jianshu.com/p/b343fcff51b0/
推荐阅读
- 由浅入深理解AOP
- 【译】20个更有效地使用谷歌搜索的技巧
- 继续努力,自主学习家庭Day135(20181015)
- mybatisplus如何在xml的连表查询中使用queryWrapper
- MybatisPlus|MybatisPlus LambdaQueryWrapper使用int默认值的坑及解决
- MybatisPlus使用queryWrapper如何实现复杂查询
- python学习之|python学习之 实现QQ自动发送消息
- 一起来学习C语言的字符串转换函数
- 定制一套英文学习方案
- 漫画初学者如何学习漫画背景的透视画法(这篇教程请收藏好了!)