RecyclerView已经成为了Android开发中列表控件的首选,它可以取代ListView,GridView和ViewPager,听说最近新出的ViewPager2底层都用RecyclerView实现了,所以我们不可避免的要为每一个RecyclerView编写Adapter的代码,但是日子久了你会发现你一直都在做重复代码的编写,特别是每次根据itemType,添加不同的item时,你都要写一遍上次一样的代码,所以为自己封装一个Adapter是很有必要的,我就根据自己平常经常使用的几个itemType封装到BaseAdapter中,只需要实现一个方法就能完成以前编写十几行代码的功能。
你会发现自动加载更多、添加emptyView和添加headerView都是属于多itemType的一种,所以归根到底就是多itemType的封装,对于多itemType的封装,我使用了AdapterDelegates中使用的方式,这位作者在这篇文章create adapter hell escape中讲解了这种方式的原理,使用一个Manager管理所有itemType的Delegate,用户通过对Delegate接口方法的不同实现,让外部来创建和绑定不同的itemType的ViewHolder,并且通过isForViewType这个方法的返回值true或false来决定这个item是否使用数据源中的这个position来在RecyclerView中的相应位置来展示自己,然后原本的Adapter中把 create和bind ViewHolder的任务都委托给了Manager来实现,因为Manager中持有所有用户添加的Delegate,Delegate会根据itemType来创建和绑定相应的ViewHolder。
其中头部绿色显示的是添加的HeaderView,接下来是数据源,一种颜色代表着一个itemType,拉到底部就会触发自动加载更多,最后清空数据后显示一个emptyView。
在项目根目录的build.gradle下添加仓库地址:
allprojects {
repositories {
mavenCentral()
}
}
在app目录下的build.grade下添加:
implementation 'io.github.rain9155:baseadapter:1.0.4'
如果你不需要添加itemType,只是正常的使用Adapter,你只需要继承BaseAdapter,然后重写onBindView方法,在这个方法里面完成数据向ViewHolder的绑定,如下:
public class DataAdapter extends BaseAdapter<String> {
public DataAdapter(List<String> datas, int layoutId) {
super(datas, layoutId);
}
@Override
protected void onBindView(BaseViewHolder holder, String item) {
holder.setText(R.id.tv_text, item);
}
}
其中BaseViewHolder是根据封装了一些setXX方法的ViewHolder,不用我们手动findByView,例如这里的setText方法。
添加多itemType,你只需要为每个item实现一个Delegate,如下:
//itemType1
//position等于1或10时显示这个item
public class MultipleItem1 implements IMultiItemDelegate<String> {
@Override
public boolean isForViewType(String item, int position) {
return position == 1 || position == 10;
}
@Override
public BaseViewHolder onCreateViewHolder(ViewGroup parent) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_multi1, parent, false);
return new BaseViewHolder(view);
}
@Override
public void onBindView(BaseViewHolder holder, String item, int position) {
holder.setText(R.id.tv_text, items);
}
}
//itemType2
//position等于5或30时显示这个item
public class MutipleItem2 implements IMultiItemDelegate<String> {
@Override
public boolean isForViewType(String item, int position) {
return position == 5 || position == 30;
}
@Override
public BaseViewHolder onCreateViewHolder(ViewGroup parent) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_multi2, parent, false);
return new BaseViewHolder(view);
}
@Override
public void onBindView(BaseViewHolder holder, String item, int position) {
holder.setText(R.id.tv_text, items);
}
}
在isForViewType方法中根据position或item返回true或者false来决定你是否使用数据源中的这个position来在RecyclerView中的相应位置来展示item,position就是数据源中item的位置。
然后在你自己的Adapter构造函数或外部调用addItemDelegate方法把这个两个itemType的Delegate添加进Manager中,如下:
public class DataAdapter extends BaseAdapter<String> {
public DataAdapter(List<String> datas, int layoutId) {
super(datas, layoutId);
addItemDelegate(new MutipleItem2())
.addItemDelegate(new MultipleItem1());
}
@Override
protected void onBindView(BaseViewHolder holder, String item) {
holder.setText(R.id.tv_text, item);
}
}
或
mDataAdapter.addItemDelegate(new MutipleItem2())
.addItemDelegate(new MultipleItem1());
你添加时还可以指定一个type值,如下:
mDataAdapter.addItemDelegate(1, new MutipleItem2())
.addItemDelegate(2, new MultipleItem1());
否则就会使用默认的type值。
添加HeaderView,可以连续添加多个,如下:
mDataAdapter.addHeaderView(LayoutInflater.from(this).inflate(R.layout.header_view, null));
或
//指定位置添加
mDataAdapter.addHeaderView(LayoutInflater.from(this).inflate(R.layout.header_view, null), 0);
移除HeaderView,如下:
//移除所有HeaderView
mDataAdapter.removeHeaderView();
或
//移除指定位置的HeaderView
mDataAdapter.removeHeaderView();
默认不适用自动加载,开启自动加载只需要添加一个OnLoadMoreListener,如下:
mDataAdapter.setOnLoadMoreListener(new BaseAdapter.OnLoadMoreListener() {
@Override
public void onLoadMore() {
Log.d(TAG, "onLoadMore");
mHandler.postDelayed(() -> {
List<String> newDatas = new ArrayList<>();
getDatas(newDatas, 10);
mDataAdapter.addDatas(newDatas);
mDataAdapter.loadingComplete();
Log.d(TAG, "loadingComplete");
}, 2000);
}
});
当处理完加载数据的逻辑后,需要调用loadingComplete或loadingEnd或loadingFail方法来结束自动加载,loadingComplete表示下次还有数据,loadingEnd表示下次没有数据了,loadingFail表示本次加载失败。
关闭自动加载,如下:
mDataAdapter.setLoadMoreEnable(false);
默认不使用EmptyView,添加EmptyView,如下:
mDataAdapter.addEmptyView(LayoutInflater.from(this).inflate(R.layout.empty_view, null));
关闭EmptyView,如下:
mDataAdapter.setUseEmptyView(false);
开启进入动画,如下:
//使用默认的动画
mDataAdapter.openItemAnim();
如果要改变默认的进入动画,内置了3种选择,如下:
//开启并更换动画,ANIM_ALPHA(渐显)、 ANIM_SCAL(缩放)、 ANIM_SLIDE_FROM_LEFT(从左边滑出来)
mDataAdapter.changeItemAnim(BaseAdapter.ANIM_ALPHA);
关闭加载动画,如下:
mDataAdapter.closeItemAnim();
使用策略模式,替换掉内部的默认实现。
自定义加载更多视图,实现ILoadMoreViewProvide,如下:
public class MyLoadMoreProvide implements ILoadMoreViewProvide {
@Override
public int getLoadMoreLayoutId() {
return R.layout.load_more;
}
@Override
public int getLoadingViewId() {
return R.id.loading;
}
@Override
public int getLoadingEndViewId() {
return R.id.loading_end;
}
@Override
public int getLoadingFailViewId() {
return R.id.loading_fail;
}
}
然后添加进Adapter,如下:
mDataAdapter.setLoadMoreProvide(new MyLoadMoreProvide());
自定义item进入动画,实现IAnim, 如下:
class AlphaAnim extends IAnim {
@Override
public void applyAnimation(View itemView) {
ObjectAnimator alphaAnimation = ObjectAnimator.ofFloat(itemView, "alpha", 0.5f, 1f);
alphaAnimation.setInterpolator(new LinearOutSlowInInterpolator());
alphaAnimation.start();
}
}
然后添加进Adapter,如下:
mDataAdapter.changeItemAnim(new AlphaAnim());