RecyclerView完全解析

(一) 前言

话说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基本实现

1.导入 android-support-v7-recyclerview.jar 包。

下载地址:http://down.51cto.com/data/2164646

2.新建布局,引入RecyclerView控件:

 1 <?xmlversionxmlversion="1.0" encoding="utf-8"?>
2 <LinearLayoutxmlns:androidLinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
3 android:orientation="vertical"
4 android:layout_width="match_parent"
5 android:layout_height="match_parent">
6
7 <include layout="@layout/common_top_bar_layout"/>
8
9 <android.support.v7.widget.RecyclerView
10 android:id="@+id/recyclerView_one"
11 android:layout_width="match_parent"
12 android:layout_height="match_parent"
13 android:scrollbars="vertical"
14 />
15
16 </LinearLayout>

3.在Activity中获取RecyclerView控件然后进行设置LayoutManger以及Adapter即可,和ListView的写法有点类似:

 1 /**
2 * 当前类注释:RecyclerView使用实例测试demo
3 * 项目名:FastDev4Android
4 * 包名:com.chinaztt.fda.test
5 */
6 public class RecyclerViewTestActivity extends BaseActivity {
7 private LinearLayout top_bar_linear_back;
8 private TextView top_bar_title;
9 private RecyclerView recyclerView_one;
10 private RecyclerView.Adapter mAdapter;
11 private LinearLayoutManager mLayoutManager;
12 @Override
13 protected void onCreate(BundlesavedInstanceState) {
14 super.onCreate(savedInstanceState);
15 setContentView(R.layout.recyclerview_test_layout);
16 top_bar_linear_back=(LinearLayout)this.findViewById(R.id.top_bar_linear_back);
17 top_bar_linear_back.setOnClickListener(new CustomOnClickListener());
18 top_bar_title=(TextView)this.findViewById(R.id.top_bar_title);
19 top_bar_title.setText("RecyclerView使用实例");
20 //开始设置RecyclerView
21 recyclerView_one=(RecyclerView)this.findViewById(R.id.recyclerView_one);
22 //设置固定大小
23 recyclerView_one.setHasFixedSize(true);
24 //创建线性布局
25 mLayoutManager = newLinearLayoutManager(this);
26 //垂直方向
27 mLayoutManager.setOrientation(OrientationHelper.VERTICAL);
28 //给RecyclerView设置布局管理器
29 recyclerView_one.setLayoutManager(mLayoutManager);
30 //创建适配器,并且设置
31 mAdapter = newTestRecyclerAdapter(this);
32 recyclerView_one.setAdapter(mAdapter);
33 }
34 class CustomOnClickListener implements View.OnClickListener{
35 @Override
36 public void onClick(View v) {
37 RecyclerViewTestActivity.this.finish();
38 }
39 }
40 }

4.自定义一个适配器来进行创建item view以及绑定数据:

 1 /**
2 * 当前类注释:RecyclerView 数据自定义Adapter
3 * 项目名:FastDev4Android
4 * 包名:com.chinaztt.fda.adapter.base
5 */
6 public class TestRecyclerAdapter extends RecyclerView.Adapter<TestRecyclerAdapter.ViewHolder>{
7 private LayoutInflater mInflater;
8 private String[] mTitles=null;
9 public TestRecyclerAdapter(Context context){
10 this.mInflater=LayoutInflater.from(context);
11 this.mTitles=new String[20];
12 for (int i=0;i<20;i++){
13 int index=i+1;
14 mTitles[i]="item"+index;
15 }
16 }
17 /**
18 * item显示类型
19 * @param parent
20 * @param viewType
21 * @return
22 */
23 @Override
24 public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
25 Viewview=mInflater.inflate(R.layout.item_recycler_layout,parent,false);
26 //view.setBackgroundColor(Color.RED);
27 ViewHolder viewHolder=new ViewHolder(view);
28 return viewHolder;
29 }
30 /**
31 * 数据的绑定显示
32 * @param holder
33 * @param position
34 */
35 @Override
36 public void onBindViewHolder(ViewHolder holder, int position) {
37 holder.item_tv.setText(mTitles[position]);
38 }
39
40 @Override
41 public int getItemCount() {
42 return mTitles.length;
43 }
44
45 //自定义的ViewHolder,持有每个Item的的所有界面元素
46 public static class ViewHolder extends RecyclerView.ViewHolder {
47 public TextView item_tv;
48 public ViewHolder(View view){
49 super(view);
50 item_tv = (TextView)view.findViewById(R.id.item_tv);
51 }
52 }
53 }

这个自定义Adapter和我们在使用Listview时候的Adapter相比还是有点不太一样的,首先这边我们需要继承RecyclerView.Adaper类,然后实现两个重要的方法onBindViewHodler()以及onCreateViewHolder(),这边我们看出来区别,使用RecyclerView控件我们就可以把Item View视图创建和数据绑定这两步进行分来进行管理,用法就更加方便而且灵活了,并且我们可以定制打造千变万化的布局。同时这边我们还需要创建一个ViewHolder类,该类必须继承自RecyclerView.ViewHolder类,现在Google也要求我们必须要实现ViewHolder来承载Item的视图。

该Demo运行效果如下:

上面的例子我们这边比较简单使用LinearLayoutManager来实现了,其中布局是采用垂直布局的,当然我们还可以设置线性布局的方向为横向,只要如下设置即可:

 mLayoutManager.setOrientation(OrientationHelper.HORIZONTAL);  

运行效果如下:

2.GridLayoutManger:使用如下设置:

 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

 /**
* 当前类注释:自定义实现一个Decoration分隔线
* 项目名:FastDev4Android
* 包名:com.chinaztt.fda.widget
*/
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分割线
* 项目名:FastDev4Android
* 包名:com.chinaztt.fda.widget
*/
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=(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) {
if(OrientationHelper.HORIZONTAL==orientation){
outRect.set(0, 0,mDivider.getIntrinsicWidth(), 0);
}else {
outRect.set(0, 0, 0,mDivider.getIntrinsicHeight());
}
}
}

改良之后的自定义分割器的构造函数中新增一个int参数,用来表示横向还是纵向布局,这样我们可以分别来绘制分割线。具体使用方法:

 recyclerView_one.addItemDecoration(newAdvanceDecoration(this,OrientationHelper.VERTICAL));

运行比较效果如下:

(1) 横向

(2) 纵向

(五) RecyclerView高级用户(监听事件处理)

我们知道在ListView使用的时候,该控件给我们提供一个onItemClickListener监听器,这样当我们的item发生触发事件的时候,会回调相关的方法,以便我们方便处理Item点击事件。对于RecyclerView来讲,非常可惜的时候,该控件没有给我们提供这样的内置监听器方法,不过我们可以进行改造实现。我们先来看一下之前我们写得TestRecyclerAdapter中的onCreateViewHolder()方法中的代码:

 public ViewHolder onCreateViewHolder(ViewGroupparent, int viewType) {
Viewview=mInflater.inflate(R.layout.item_recycler_layout,parent,false);
//这边可以做一些属性设置,甚至事件监听绑定
//view.setBackgroundColor(Color.RED);
ViewHolder viewHolder=newViewHolder(view);
return viewHolder;
}

该方法创建一个ViewHolder,其中承载的就是每一项Item View视图,那么我们可以在view创建出来之后给它进行添加相应的属性或者监听方法,例如:背景颜色,大小,以及点击事件。既然可以这样解决,OK,我们给View添加一个onClickListener监听器,然后点击的时候回调onClick()方法。同时我们需要自定义一个类似于onItemClickListener()的监听器来处理。

 /**
* 自定义RecyclerView 中item view点击回调方法
*/
interface OnRecyclerItemClickListener{
/**
* item view 回调方法
* @param view 被点击的view
* @param position 点击索引
*/
void onItemClick(View view, intposition);
}

然后声明以及Adapter初始化的时候传入进去:

 public TestRecyclerAdapter(Contextcontext,OnRecyclerItemClickListener onRecyclerItemClickListener){
.......
this.onRecyclerItemClickListener=onRecyclerItemClickListener;
}

然后我们在onClick回调方法中调用OnRecyclerItemClickListener接口的方法。

 view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(onRecyclerItemClickListener!=null){
onRecyclerItemClickListener.onItemClick(view, (int)view.getTag());
}
}
});

上面的onItemClick中第二个参数的position,采用view.getTag的方法获取,那么我们就需要在onBindViewHolder()方法中设置一个tag了。

 public voidonBindViewHolder(ViewHolder holder, int position) {
holder.item_tv.setText(mTitles[position]);
holder.itemView.setTag(position);
}

最后我们在外部使用一下接口:

 mAdapter = new TestRecyclerAdapter(this, new TestRecyclerAdapter.OnRecyclerItemClickListener() {
@Override
public void onItemClick(View view,int position) {
Toast.makeText(RecyclerViewTestActivity.this, "点击了第"+position+"项", Toast.LENGTH_SHORT).show();
}
});

运行结果如下:

(六) RecyclerView数据添加删除处理

讲了以上RecyclerView中各种用户,处理之后,现在我们来看一下当数据发生变化之后的处理。例如我们在使用ListView的时候,当数据发生变化的时候可以通过notifyDatasetChange()来刷新界面。对于RecyclerView控件来讲,给我们提供更加高级的使用方法notifyItemInserted(position)和notifyItemRemoved(position)

我们可以在TestRecyclerAdapter中添加数据新增和数据删除的方法如下:

 //添加数据
public void addItem(String data, intposition) {
mTitles.add(position, data);
notifyItemInserted(position);
}
//删除数据
public void removeItem(String data) {
int position = mTitles.indexOf(data);
mTitles.remove(position);
notifyItemRemoved(position);
}

然后我们在Activity中进行调用即可:

 //添加数据
mAdapter.addItem("additem",5);
//删除数据
mAdapter.removeItem("item4");

在运行之前我们不要忘记RecyclerView给提供了动画设置,我这边就直接采用了默认动画,设置方法如下:

 //添加默认的动画效果
recyclerView_one.setItemAnimator(new DefaultItemAnimator());

最终运行效果如下图:

(七) RecyclerView总结

到此为止就完成我们RecyclerView控件使用的第一讲内容,其中包括控件的基本介绍,基本使用,高级用法(自定义间隔符,加入点击监听事件以及Item添加删除动画处理)。 总体来讲RecyclerView控件是非常不错,尤其在布局以及数据绑定,动画方面,除了系统内置的三种布局方式之外,我们还可以定制出我们自己的布局 管理器。同时当item数据发生变化的时候还给我们提供非常炫的效果。相信大家在今天这一讲之后,会越来越爱上RecyclerView控件的使用,从此 可以抛弃ListView和GridView啦.

本次具体实例注释过的全部代码已经上传到FastDev4Android项目中了。同时欢迎大家去Github站点进行clone或者下载浏览:

https://github.com/jiangqqlmj/FastDev4Android 同时欢迎大家star和fork整个开源快速开发框架项目~下一讲我们会通过一个具体实例来自定义实现一个广告条控件实例。

Android RecyclerView完全解析的更多相关文章

  1. Android RecyclerView 使用解析

    RecyclerView出现已经有一段时间了,相信大家肯定不陌生了,大家能够通过导入support-v7对其进行使用.  据官方的介绍,该控件用于在有限的窗体中展示大量数据集.事实上这样功能的控件我们 ...

  2. (转载) Android RecyclerView 使用完全解析 体验艺术般的控件

    Android RecyclerView 使用完全解析 体验艺术般的控件 标签: Recyclerviewpager瀑布流 2015-04-16 09:07 721474人阅读 评论(458) 收藏  ...

  3. Android RecyclerView单击、长按事件标准实现:基于OnItemTouchListener + GestureDetector

     Android RecyclerView单击.长按事件:基于OnItemTouchListener + GestureDetector标准实现 Android RecyclerView虽然拥有L ...

  4. [Android]RecyclerView的简单演示样例

    去年google的IO上就展示了一个新的ListView.它就是RecyclerView. 下面是官方的说明,我英语能力有限,只是我大概这么理解:RecyclerView会比ListView更具有拓展 ...

  5. 【FastDev4Android框架开发】RecyclerView完全解析之下拉刷新与上拉加载SwipeRefreshLayout(三十一)

    转载请标明出处: http://blog.csdn.net/developer_jiangqq/article/details/49992269 本文出自:[江清清的博客] (一).前言: [好消息] ...

  6. Android RecyclerView 实现支付宝首页效果

    Android RecyclerView 实现支付宝首页效果 [TOC] 虽然我本人不喜欢支付宝的,但是这个网格本身其实还是不错的,项目更新中更改了一个布局为网格模式,类似支付宝.(估计是产品抄袭的= ...

  7. Android Service完全解析,关于服务你所需知道的一切(下)

    转载请注册出处:http://blog.csdn.net/guolin_blog/article/details/9797169 在上一篇文章中,我们学习了Android Service相关的许多重要 ...

  8. Android Service完全解析,关于服务你所需知道的一切(上)

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/11952435 相信大多数朋友对Service这个名词都不会陌生,没错,一个老练的A ...

  9. [转] Android Volley完全解析(一),初识Volley的基本用法

    版权声明:本文出自郭霖的博客,转载必须注明出处.   目录(?)[-] Volley简介 下载Volley StringRequest的用法 JsonRequest的用法   转载请注明出处:http ...

随机推荐

  1. Linux开源模块迁移概述暨交叉编译跨平台移植总结--从《嵌入式Linux驱动模板简洁和工程实践》

    本文摘录<嵌入式Linux驱动模板简洁和工程实践>一本书"开发和调试技术". Linux强大的是,有那么多的开源项目可以使用.通常非常需要可以通过寻找相关的源模块被定义 ...

  2. 硬盘安装Archlinux「2013-12-26」

    按照Archlinux的中文WIKI安装完成,最后安装引导失败.原因未知. 折腾的脑袋好大,本来都要放弃了,幸好在贴吧发帖求助,吧友@atmouse耐心热心的帮助 最后重启成功启动.帖子地址:http ...

  3. CruiseControl.Net全面实现持续集成

    使用CruiseControl.Net全面实现持续集成   持续集成想必大家很多人都听说过,甚至都实践过,最近我又一次亲历了一次持续集成,现将我的经验分享给大家.关于持续集成的理论在本文概不涉及,本文 ...

  4. android 布局属性大全---初学者必备

    Android功能强大,界面华丽,但是众多的布局属性就害苦了开发者,下面这篇文章结合了网上不少资料,花费本人一个下午搞出来的,希望对其他人有用. 第一类:属性值为true或false android: ...

  5. DBMS_LOB包的使用

    DBMS_LOB包的使用 1.  dbms_lob.append( dest_lob  IN  OUT  NOCOPY  BLOB, src_lob  IN  BLOB) dbms_lob.appen ...

  6. c#生成word文档

    参考:http://blog.163.com/zhouchunping_99/blog/static/7837998820085114394716/ 生成word文档 生成word文档 view pl ...

  7. 微软 PowerShell Script Explorer

    微软 PowerShell Script Explorer 满血复活,正式发布 一年前的今天,微软在其Windows PowerShell官方博客声明中止 ‘Script Explorer’ 应用程序 ...

  8. const 还是 static readonly

    到底是 const 还是 static readonly   真的一样? const 和 static readonly 常在程序中用来声明常量,调用方法也没有什么不同,他们真的一样吗?我们可以做个试 ...

  9. Sparse Filtering

    Sparse Filtering 当前很多的特征学习(feature learning)算法需要很多的超参数(hyper-parameter)调节, Sparse Filtering则只需要一个超参数 ...

  10. Jenkins中关于一些插件的使用

    Jenkins中关于一些插件的使用方法 最近在为公司搭建CI平台过程中,以及在具体项目实施过程中使用过的一些插件的具体用法: 1. ant插件 这个插件可能是我们最为经常使用的,若构建脚本是使用bui ...