为什么提出来RecycleView?
RecyclerView提供了一种插拔式的体验,高度解耦,使用灵活。RecyclerView只管回收与复用View,通过设置LayoutManager,ItemDecoration , ItemAnimator实现各种特效,达到自由定制的目的,如实现ListView、GridView、瀑布流效果等。
有什么特点,适用场景?
优点:
标准化了ViewHolder,编写Adapter面向的是ViewHolder而不再是View了,复用的逻辑被封装了,写起来更加简单。
设置布局管理器以控制Item的布局方式,横向、竖向以及瀑布流方式。
可设置Item的间隔样式(可绘制),通过继承RecyclerView的ItemDecoration这个类,然后针对自己的业务需求去书写代码。
可以控制Item增删的动画,可以通过ItemAnimator这个类进行控制,当然针对增删的动画,RecyclerView有其自己默认的实现。
缺点:
关于Item的点击和长按事件,需要用户自己去实现。
使用成本相对高一些;
五大组成
RecyclerView由layoutManager,Adapter,ItemAnimator,ItemDecoration,ViewHolder五大核心组件。五个组件分别负责不同的功能,组合成为功能强大拓展性强的RecyclerView。
组件 | 功能 |
---|---|
LayoutManager | 负责测量和布局 |
Adapter | 负责数据和视图的绑定 |
ViewHolder | 视图的载体 |
ItemAnimator | 负责Item View的动画(包括移除,增加,改变等) |
ItemDecoration | 负责Item View的间距控制和装饰 |
LayoutManager(布局管理器)
LayoutManager负责RecyclerView的布局,其中包含了Item View的获取与回收。RecyclerView提供了3种布局管理器:
1)LinearLayoutManager:以垂直或者水平列表方式展示Item
2)GridLayoutManager:以网格方式展示Item
3)StaggeredGridLayoutManager:以瀑布流方式展示Item
Adapter(适配器)
- 创建适配器的一般步骤
1) 创建Adapter
创建一个继承RecyclerView.Adapter的Adapter类(VH是ViewHolder的类名)
2)创建ViewHolder
在Adapter中创建一个继承RecyclerView.ViewHolder的静态内部类,记为VH。ViewHolder的实现和ListView的ViewHolder实现几乎一样。
3)在Adapter中实现3个方法
方法 | 含义 |
---|---|
onCreateViewHolder() | 此方法把View直接封装在ViewHolder中,在以后使用ViewHolder代替View |
onBindViewHolder() | 方法提供viewHolder而不是原来的convertView,将数据设置到viewHolder中 |
getItemCount() | 类似于BaseAdapter的getCount方法了,即总共有多少个条目 |
说明:可以看出,RecyclerView将ListView中getView()的功能拆分成了onCreateViewHolder()和onBindViewHolder()。
基本的Adapter实现如下:
// ① 创建Adapter
public class NormalAdapter extends RecyclerView.Adapter<NormalAdapter.VH>{
//② 创建ViewHolder
public static class VH extends RecyclerView.ViewHolder{
public final TextView title;
public VH(View v) {
super(v);
title = (TextView) v.findViewById(R.id.title);
}
}
private List<String> mDatas;
public NormalAdapter(List<String> data) {
this.mDatas = data;
}
//③ 在Adapter中实现3个方法
@Override
public VH onCreateViewHolder(ViewGroup parent, int viewType) {
//LayoutInflater.from指定写法
View v = LayoutInflater.from(parent.getContext()).inflate(
R.layout.item_1, parent, false);
return new VH(v);
}
@Override
public void onBindViewHolder(VH holder, int position) {
holder.title.setText(mDatas.get(position));
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//item 点击事件
}
});
}
@Override
public int getItemCount() {
return mDatas.size();
}
}
- 万能适配器
为了创建一个RecyclerView的Adapter,每次我们都需要去做重复劳动,包括重写onCreateViewHolder()、onBindViewHolder()、getItemCount(),并且实现过程大同小异,因此万能适配器出现了。
我们通过public abstract class QuickAdapter extends RecyclerView.Adapter<QuickAdapter.VH>定义万能适配器QuickAdapter类,T是列表数据中每个元素的类型,QuickAdapter.VH是QuickAdapter的ViewHolder实现类,称为万能ViewHolder。
ublic abstract class QuickAdapter<T> extends RecyclerView.Adapter<QuickAdapter.VH>{
private List<T> mDatas;
public QuickAdapter(List<T> datas){
this.mDatas = datas;
}
public abstract int getLayoutId(int viewType);
@Override
public VH onCreateViewHolder(ViewGroup parent, int viewType) {
return VH.get(parent,getLayoutId(viewType));
}
@Override
public void onBindViewHolder(VH holder, int position) {
convert(holder, mDatas.get(position), position);
}
@Override
public int getItemCount() {
return mDatas.size();
}
public abstract void convert(VH holder, T data, int position);
static class VH extends RecyclerView.ViewHolder{
private SparseArray<View> mViews;
private View mConvertView;
private VH(View v){
super(v);
mConvertView = v;
mViews = new SparseArray<>();
}
public static VH get(ViewGroup parent, int layoutId){
View convertView = LayoutInflater.from(parent.getContext()).
inflate(layoutId, parent, false);
return new VH(convertView);
}
public <T extends View> T getView(int id){
View v = mViews.get(id);
if(v == null){
v = mConvertView.findViewById(id);
mViews.put(id, v);
}
return (T)v;
}
}
}
1)万能ViewHolder
其中的关键点在于通过SparseArray存储item view的控件,getView(int id)的功能就是通过id获得对应的View(首先在mViews中查询是否存在,如果没有,那么findViewById()并放入mViews中,避免下次再执行findViewById())。
2)万能适配器
getLayoutId(int viewType)是根据viewType返回布局ID;
convert()做具体的bind操作;
通过万能适配器快捷创建一个Adapter:
mAdapter = new QuickAdapter<Model>(data) {
@Override
public int getLayoutId(int viewType) {
switch(viewType){
case TYPE_1:
return R.layout.item_1;
case TYPE_2:
return R.layout.item_2;
}
}
@Override
public int getItemViewType(int position) {
if(position % 2 == 0){
return TYPE_1;
} else{
return TYPE_2;
}
}
@Override
public void convert(VH holder, Model data, int position) {
int type = getItemViewType(position);
switch(type){
case TYPE_1:
holder.setText(R.id.text, data.text);
break;
case TYPE_2:
holder.setImage(R.id.image, data.image);
break;
}
}
};
ItemDecoration(间隔样式)
作用:item之间的间隔样式。
接口定义
public static abstract class ItemDecoration {
public void onDraw(Canvas c, RecyclerView parent, State state) {
onDraw(c, parent);
}
public void onDrawOver(Canvas c, RecyclerView parent, State state) {
onDrawOver(c, parent);
}
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(), parent);
}
@Deprecated
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
outRect.set(0, 0, 0, 0);
}
}
说明:
① onDraw方法先于drawChildren;
② onDrawOver在drawChildren之后;
③ getItemOffsets 可以通过outRect.set()为每个Item设置一定的偏移量,主要用于绘制Decorator;
说明:系统自带的间隔样式是DividerItemDecoration。
ItemAnimator(动画)
RecyclerView能够通过mRecyclerView.setItemAnimator(ItemAnimator animator)设置添加、删除、移动、改变的动画效果。
系统提供默认实现类:DefaultItemAnimator
DefaultItemAnimator继承自SimpleItemAnimator,SimpleItemAnimator继承自ItemAnimator。DefaultItemAnimator的实现过程非常复杂,如果基于SimpleItemAnimator自定义动画类非常麻烦。
开源动画recyclerview-animators
recyclerview-animators提供了一系列的Animator,比如FadeInAnimator,ScaleInAnimator。自定义动画继承BaseItemAnimator,更加方便;
常见问题
1)局部刷新闪屏问题解决
问题描述:当Item视图中有图片和文字,当更新文字并调用notifyItemChanged()时,文字改变的同时图片会闪一下。
这个问题的原因是当调用notifyItemChanged()时,会调用DefaultItemAnimator的animateChangeImpl()执行change动画,该动画会使得Item的透明度从0变为1,从而造成闪屏。解决方便很简单禁用change动画,在rv.setAdapter()之前调用((SimpleItemAnimator)rv.getItemAnimator()).setSupportsChangeAnimations(false)。
注意事项
notifyItemInserted(position)与notifyItemRemoved(position)进行更新,否则没有动画效果。
如何使用?
使用过程
引用 --> 布局 --> 创建适配器 --> 设置RecyclerView参数 --> 增加点击事件
具体步骤介绍
引用
在build.gradle文件中引入该类:
compile 'com.android.support:recyclerview-v7:23.4.0'
布局
① Activity布局文件;
② Item的布局文件;
创建适配器
参看上面适配内容
设置RecyclerView
一般来说,需要为RecyclerView进行四大设置:
① Layout Manager(必选)
② Adapter(必选)
③ Item Decoration(可选,默认为空)
④ Item Animator(可选,默认为DefaultItemAnimator)
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
LinearLayoutManager layoutManager = new LinearLayoutManager(this );
layoutManager.setOrientation(OrientationHelper. VERTICAL);
//设置布局管理器
recyclerView.setLayoutManager(layoutManager);
//设置Adapter
recyclerView.setAdapter(recycleAdapter);
//设置分隔线
recyclerView.addItemDecoration( new DividerGridItemDecoration(this ));
//设置增加或删除条目的动画
recyclerView.setItemAnimator( new DefaultItemAnimator());
点击事件
RecyclerView并没有像ListView一样暴露出Item点击事件或者长按事件处理的api,也就是说使用RecyclerView时候,需要我们自己来实现Item的点击和长按等事件的处理。
实现方法有很多:
① 监听RecyclerView的Touch事件然后判断手势做相应的处理;
② 通过在绑定ViewHolder的时候设置监听,然后通过Apater回调出去;
我们选择第二种方法,更加直观和简单,其代码如下:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder>{
// 展示数据
private ArrayList<String> mData;
// 事件回调监听
private MyAdapter.OnItemClickListener o