RecyclerView完全解析
https://blog.csdn.net/developer_jiangqq/article/details/49927631
一、前言
- 话说RecyclerView已经面市很久,也在很多应用中得到广泛的使用,在整个开发者圈子里面也拥有很不错的口碑,那说明RecyclerView拥有比ListView,GridView之类控件有很多的优点,例如:数据绑定,Item View创建,View的回收以及重用等机制。那么今天开始我们来重点学习一下RecyclerView控件,本系列文章会包括到以下三个部分:
1. RecyclerView控件的基本使用,包括基础,进阶,高级部分,动画之类
2. RecyclerView控件的实战实例
3. RecyclerView控件集合AA(Android Annotations)注入框架实例
- 那么今天我们首先来看第一部分:RecyclerView控件的基本使用,进阶,动画相关知识点。本次讲解所有用的Demo例子已经全部更新到下面的项目中了,欢迎大家star和fork。
FastDev4Android框架项目地址:https://github.com/jiangqqlmj/FastDev4Android
二、RecyclerView基本介绍:
- 通过使用RecyclerView控件,我们可以在APP中创建带有Material Design风格的复杂列表。RecyclerView控件和ListView的原理有很多相似的地方,都是维护少量的View来进行显示大量的数据,不过RecyclerView控件比ListView更加高级并且更加灵活。当我们的数据因为用户事件或者网络事件发生改变的时候也能很好的进行显示。
- 和ListView不同的是,RecyclerView不用在负责Item的显示相关的功能,在这边所有有关布局,绘制,数据绑定等都被分拆成不同的类进行管理,下面我这边会一个个的进行讲解。同时RecyclerView控件提供了以下两种方法来进行简化和处理大数量集合:
1. 采用LayoutManager来处理Item的布局
2. 提供Item操作的默认动画,例如在增加或者删除item的时候
-
你也可以自定义LayoutManager或者设置添加/删除的动画,整体的RecyclerView结构图如下:
-
为了使用RecyclerView控件,我们需要创建一个Adapter和一个LayoutManager:
Adapter:继承自RecyclerView.Adapetr类,主要用来将数据和布局item进行绑定。
LayoutManager:布局管理器,设置每一项view在RecyclerView中的位置布局以及控件item view的显示或者隐藏。当View重用或者回收的时候,LayoutManger都会向Adapter来请求新的数据来进行替换原来数据的内容。这种回收重用的机制可以提供性能,避免创建很多的view或者是频繁的调用findViewById方法。这种机制和ListView还是很相似的。
- RecyclerView提供了三种内置的LayoutManager:
1. LinearLayoutManager:线性布局,横向或者纵向滑动列表
2. GridLayoutManager:表格布局
3. StaggeredGridLayoutManager:流式布局,例如瀑布流效果
-
当然除了上面的三种内部布局之外,我们还可以继承RecyclerView.LayoutManager来实现一个自定义的LayoutManager。
-
Animations(动画)效果:
RecyclerView对于Item的添加和删除是默认开启动画的。我们当然也可以通过RecyclerView.ItemAnimator类定制动画,然后通过RecyclerView.setItemAnimator()方法来进行使用。
- RecyclerView相关类:
类名 | 说明 |
---|---|
RecyclerView.Adapter | 可以托管数据集合,为每一项Item创建视图并且绑定数据 |
RecyclerView.ViewHolder | 承载Item视图的子布局 |
RecyclerView.LayoutManager | 负责Item视图的布局的显示管理 |
RecyclerView.ItemDecoration | 给每一项Item视图添加子View,例如可以进行画分隔线之类的 |
RecyclerView.ItemAnimator | 负责处理数据添加或者删除时候的动画效果 |
三、RecyclerView基本实现:
- 我这边实例采用Android Studio 1.3.2。
1、添加库依赖:
dependencies {
…….
compile'com.android.support:recyclerview-v7:23.1.1'
}
2、新建布局,引入RecyclerView控件:
<?xmlversionxmlversion="1.0" encoding="utf-8"?>
<LinearLayout xmlns:androidLinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"android:layout_width="match_parent"
android:layout_height="match_parent">
<includelayoutincludelayout="@layout/common_top_bar_layout"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView_one"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
></android.support.v7.widget.RecyclerView>
</LinearLayout>
3、在Activity中获取RecyclerView控件然后进行设置LayoutManger以及Adapter即可,和ListView的写法有点类似:
public class RecyclerViewTestActivity extends AppCompatActivity {
private RecyclerView recyclerView_one;
private RecyclerView.Adapter mAdapter;
private LinearLayoutManager mLayoutManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//开始设置RecyclerView
recyclerView_one=(RecyclerView)this.findViewById(R.id.recyclerView);
//设置固定大小
recyclerView_one.setHasFixedSize(true);
//创建线性布局
mLayoutManager = new LinearLayoutManager(this);
//垂直方向
mLayoutManager.setOrientation(OrientationHelper.VERTICAL);
//给RecyclerView设置布局管理器
recyclerView_one.setLayoutManager(mLayoutManager);
//创建适配器,并且设置
mAdapter = new TestRecyclerAdapter(this);
recyclerView_one.setAdapter(mAdapter);
}
}
4、自定义一个适配器来进行创建item view以及绑定数据
public class TestRecyclerAdapter extends RecyclerView.Adapter<TestRecyclerAdapter.ViewHolder>{
private LayoutInflater mInflater;
private String[] mTitles=null;
public TestRecyclerAdapter(Context context){
this.mInflater=LayoutInflater.from(context);
this.mTitles=new String[20];
for (int i=0;i<20;i++){
int index=i+1;
mTitles[i]="item"+index;
}
}
/**
* item显示类型
* @param parent
* @param viewType
* @return
*/
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view=mInflater.inflate(R.layout.item_recycler_layout,parent,false);
//view.setBackgroundColor(Color.RED);
ViewHolder viewHolder=new ViewHolder(view);
return viewHolder;
}
/**
* 数据的绑定显示
* @param holder
* @param position
*/
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.item_tv.setText(mTitles[position]);
}
@Override
public int getItemCount() {
return mTitles.length;
}
//自定义的ViewHolder,持有每个Item的的所有界面元素
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView item_tv;
public ViewHolder(View view){
super(view);
item_tv = (TextView)view.findViewById(R.id.item_tv);
}
}
}
这个自定义Adapter和我们在使用Listview时候的Adapter相比还是有点不太一样的,首先这边我们需要继承RecyclerView.Adaper类,然后实现两个重要的方法onBindViewHodler()以及onCreateViewHolder(),这边我们看出来区别,使用RecyclerView控件我们就可以把Item View视图创建和数据绑定这两步进行分来进行管理,用法就更加方便而且灵活了,并且我们可以定制打造千变万化的布局。同时这边我们还需要创建一个ViewHolder类,该类必须继承自RecyclerView.ViewHolder类,现在Google也要求我们必须要实现ViewHolder来承载Item的视图。
-
该Demo运行效果如下:
-
上面的例子我们这边比较简单使用LinearLayoutManager来实现了,其中布局是采用垂直布局的,当然我们还可以设置线性布局的方向为横向,只要如下设置即可:
mLayoutManager.setOrientation(OrientationHelper.HORIZONTAL);
运行效果如下:
那么另外两种内置的布局如下:
- 1、GridLayoutManger:使用如下设置:
GridLayoutManager girdLayoutManager=new GridLayoutManager(this,4);
recyclerView_one.setLayoutManager(girdLayoutManager);
运行效果如下:
- 2、StaggeredGridLayoutManager :使用如下设置:
StaggeredGridLayoutManager staggeredGridLayoutManager=new StaggeredGridLayoutManager(2,OrientationHelper.VERTICAL);
recyclerView_one.setLayoutManager(staggeredGridLayoutManager);
实现的是瀑布流的效果
四、RecyclerView分隔线实现(ItemDecoration):
- 大家肯定观察到上面的显示效果还是比较丑,例如就没有分隔线这个效果,下面我们一起来实现以下分隔线的效果。还记得前面的一个表格中有写关于RecyclerView的相关类:
RecyclerView.ItemDecoration | 给每一项Item视图添加子View,可以进行画分隔线之类的东西 |
---|
-
我们可以创建一个继承RecyclerView.ItemDecoration类来绘制分隔线,通过ItemDecoration可以让我们每一个Item从视觉上面相互分开来,例如ListView的divider非常相似的效果。当然像我们上面的例子ItemDecoration我们没有设置也没有报错哦,那说明ItemDecoration我们并不是强制需要使用,作为我们开发者可以设置或者不设置Decoration的。
-
实现一个ItemDecoration,系统提供的ItemDecoration是一个抽象类,内部除去已经废弃的方法以外,我们主要实现以下三个方法:
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(RectoutRect, View view, RecyclerView parent, State state) {
getItemOffsets(outRect,((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
parent);
}
}
-
又因为当我们RecyclerView在进行绘制的时候会进行绘制Decoration,那么会去调用onDraw和onDrawOver方法,那么这边我们其实只要去重写onDraw和getItemOffsets这两个方法就可以实现啦。然后LayoutManager会进行Item布局的时候,回去调用getItemOffset方法来计算每个Item的Decoration合适的尺寸
-
下面我们来具体实现一个Decoration:
TestDecoration.java
public class TestDecoration extends RecyclerView.ItemDecoration {
//采用系统内置的风格的分割线
private static final int[] attrs=newint[]{android.R.attr.listDivider};
private Drawable mDivider;
public TestDecoration(Context context) {
TypedArray typedArray=context.obtainStyledAttributes(attrs);
mDivider=typedArray.getDrawable(0);
}
/**
* 进行自定义绘制
* @param c
* @param parent
* @param state
*/
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
int top=parent.getPaddingTop();
intbottom=parent.getHeight()-parent.getPaddingBottom();
int childCount=parent.getChildCount();
for(int i=0;i<childCount;i++){
View child=parent.getChildAt(i);
RecyclerView.LayoutParams layoutParams=(RecyclerView.LayoutParams)child.getLayoutParams();
intleft=child.getRight()+layoutParams.rightMargin;
intright=left+mDivider.getIntrinsicWidth();
mDivider.setBounds(left,top,right,bottom);
mDivider.draw(c);
}
}
@Override
public void getItemOffsets(Rect outRect,View view, RecyclerView parent, RecyclerView.State state) {
outRect.set(0,0,mDivider.getIntrinsicWidth(),0);
}
}
- 我这边实例中采用系统主题(android.R.attr.listDivider)来设置成分隔线的,然后来获取尺寸,位置进行setBound(),绘制,接着通过outRect.set()来设置绘制整个区域范围,最后不要忘记往RecyclerView中设置该自定义的分割线:
//添加分割线
recyclerView_one.addItemDecoration(newTestDecoration(this));
-
运行效果大致如下:
-
上面的分割线效果只是垂直画了分割线,但是我们水平方向也要进行画分割线,那么我们下面对于自定义的Decoration进行改进:
AdvanceDecoration.java
/**
* 当前类注释:改进之后的自定义Decoration分割线
*/
public class AdvanceDecoration extends RecyclerView.ItemDecoration{
//采用系统内置的风格的分割线
private static final int[] attrs=newint[]{android.R.attr.listDivider};
private Drawable mDivider;
private int orientation;
public AdvanceDecoration(Contextcontext,int orientation) {
TypedArray typedArray=context.obtainStyledAttributes(attrs);
mDivider=typedArray.getDrawable(0);
typedArray.recycle();
this.orientation=orientation;
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
drawHDeraction(c,parent);
drawVDeraction(c,parent);
}
/**
* 绘制水平方向的分割线
* @param c
* @param parent
*/
private void drawHDeraction(Canvas c,RecyclerView parent){
int left=parent.getPaddingLeft();
intright=parent.getWidth()-parent.getPaddingRight();
int childCount=parent.getChildCount();
for(int i=0;i<childCount;i++){
View child=parent.getChildAt(i);
RecyclerView.LayoutParams layoutParams=(RecyclerView.LayoutParams)child.getLayoutParams();
inttop=child.getBottom()+layoutParams.bottomMargin;
intbottom=top+mDivider.getIntrinsicHeight();
mDivider.setBounds(left,top,right,bottom);
mDivider.draw(c);
}
}
/**
* 绘制垂直方向的分割线
* @param c
* @param parent
*/
private void drawVDeraction(Canvas c,RecyclerView parent){
int top=parent.getPaddingTop();
intbottom=parent.getHeight()-parent.getPaddingBottom();
int childCount=parent.getChildCount();
for(int i=0;i<childCount;i++){
View child=parent.getChildAt(i);
RecyclerView.LayoutParamsla youtParams