RecyclerView是Android 5.0新特性——Material Design中的一个控件,它将ListView、GridView整合到一起,可以使用极少的代码在ListView、GridView和瀑布流等布局方式之间转换。RecyclerView整体使用的是插件式的方式,解耦度相比提高了不少,非常灵活。

  RecyclerView之所以叫RecyclerView,是因为它的特性:它不关心Item是否显示在正确的位置上;不关心Item间如何分隔;不关心增加与删除的动画效果,只关心如何回收和复用View。

RecyclerView可以实现的Item布局方式:

  • 类似ListView的样式(横、纵都可以实现)
  • 类似GridView的样式(横、纵都可以实现)
  • 瀑布流样式(交错布局)

RecyclerView中可能用到的类:

  • LayoutManager:用来管理RecyclerView中的Item的布局方式
  • ItemDecoration:用来绘制RecyclerView中Item之间的间隔
  • ItemAnimation:用来绘制RecyclerView中的各种动画
  • RecyclerView.ViewHolder:用来存放每个Item中的控件
  • RecyclerView.Adapter:RecyclerView的适配器类的父类

1、RecyclerView适配数据:

  RecyclerView适配数据的方法和ListView、GridView使用的BaseAdapter适配数据的方法不太相同,RecyclerView内部提供了一个ViewHolder用来盛放item中出现的控件,相当于BaseAdapter中我们自己定义的ViewHolder相同,从这可以看出,从RecyclerView开始,Android开始“逼”我们对Item进行回收和复用;RecyclerView内部还提供了一个Adapter,其中有三个抽象方法:

  • getItemCount():获取Item的个数
  • onCreateViewHolder():返回当前Item的ViewHolder
  • onBindViewHolder():向ViewHolder中适配数据

  说了这么多,下面贴一下RecyclerView的适配器类RecyclerAdaper中的代码:

public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.MyViewHolder> {
private Context context;
private List<String> data;
private LayoutInflater inflater; public RecyclerAdapter(Context context, List<String> data, boolean isStagger, OnRecyclerViewItemOperationListener listener) {
this.context = context;
this.data = data;
this.inflater = LayoutInflater.from(context);
} @Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.recycleritem_item, parent, false);
return new MyViewHolder(view);
} @Override
public void onBindViewHolder(final MyViewHolder holder, final int position) {
holder.tv.setText(data.get(position));
} @Override
public int getItemCount() {
return data.size();
} static class MyViewHolder extends RecyclerView.ViewHolder {
TextView tv; public MyViewHolder(View itemView) {
super(itemView);
tv = (TextView) itemView.findViewById(R.id.item_tv);
}
}
}

2、展示模式:

  如果是ListView或者GridView,那么直接设置适配器就可以显示数据了,而RecyclerView不行,因为RecyclerView是可以在ListView和GridView,甚至瀑布流之间进行任意切换的,因此我们还需要设置它的布局模式,这里就用到了LayoutManager类。LayoutManager是一个抽象类,我们最常用的子类有两个:LinearLayoutManager(适合线性布局,用于实现ListView的效果)和StaggeredGridLayoutManager(适合格子布局,用于实现GridView或瀑布流的效果)。

  我们可以通过RecyclerView对象的setLayoutManager()方法设置它的展示模式:

rv.setLayoutManager(new LinearLayoutManager(MainActivity.this, LinearLayoutManager.VERTICAL, false));

3、分隔线:

  这里说的分隔线是RecyclerView的Item之间的分隔线,其实我们大可以不用“正儿八经”的给RecyclerView设置分隔线,因为我们可以使用Item的margin来设置间距,简介实现分隔线的效果。

  RecyclerView也给我们提供了一个分隔线的抽象类——ItemDecoration,可以帮助我们实现分隔线,但是Android没有给我们提供这个类的子类,因此,我们需要自己去写。GitHub上有很多大神发布了一些分隔线的类,这里贴出其中一个DividerItemDecoration类来:

public class DividerItemDecoration extends RecyclerView.ItemDecoration {
// 系统默认的分隔条的Drawable资源的ID
private static final int[] ATTRS = new int[]{android.R.attr.listDivider};
public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL; // 绘制Item间间隔的Drawable
private Drawable mDivider;
// 方向(水平、数值)
private int mOrientation; public DividerItemDecoration(Context context, int orientation) {
final TypedArray a = context.obtainStyledAttributes(ATTRS);
// 获取系统提供的分隔条的Drawable对象
mDivider = a.getDrawable(0);
// 回收TypedArray所占用的控件
a.recycle();
setOrientation(orientation);
} public void setOrientation(int orientation) {
if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
throw new IllegalArgumentException("invalid orientation");
}
mOrientation = orientation;
} @Override
public void onDraw(Canvas c, RecyclerView parent) {
if (mOrientation == VERTICAL_LIST) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
} /**
* 如果设置为纵向列表的样式,则调用这个方法
*/
public void drawVertical(Canvas c, RecyclerView parent) {
// Item距离左边缘的距离
final int left = parent.getPaddingLeft();
// Item距离右边缘的距离
final int right = parent.getWidth() - parent.getPaddingRight();
// 获取Item的总数
final int childCount = parent.getChildCount();
// 开始绘制所有Item之间的分隔线
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
RecyclerView v = new RecyclerView(parent.getContext());
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
// Item距离上边缘的距离
final int top = child.getBottom() + params.bottomMargin;
// Item距离下边缘的距离
final int bottom = top + mDivider.getIntrinsicHeight();
// 分隔线可以看成是一个长方形,所以需要设置它的上下左右的位置
mDivider.setBounds(left, top, right, bottom);
// 开始绘制分隔线
mDivider.draw(c);
}
} public void drawHorizontal(Canvas c, RecyclerView parent) {
final int top = parent.getPaddingTop();
final int bottom = parent.getHeight() - parent.getPaddingBottom();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
final int left = child.getRight() + params.rightMargin;
final int right = left + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
} @Override
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
if (mOrientation == VERTICAL_LIST) {
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
} else {
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
}
}
}

  这样,我们只需要调用下面这行代码,就可以为RecyclerView设置分隔线了:

        // 设置RecyclerView的分隔线
rv.addItemDecoration(new DividerItemDecoration(MainActivity.this, DividerItemDecoration.VERTICAL_LIST));

  现在,我们倒回来看一下这个DividerItemDecoration类,其中提到了android.R.attr.listDivider这个属性,这个类中调用的是当前主题中设置的listDivider属性的值,我们可以通过修改主题中的这个属性来达到自定义分隔线的目的。我们只需要自己设计一个分隔线布局,然后在res/styles.xml文件中的AppTheme中添加下面这行代码,就可以实现自定义分隔线了:

<item name="android:listDivider">@drawable/divider_gradient</item>

4、动画:

  现在很多APP中都用到了RecyclerView,其中不乏有一些非常炫酷的动画,例如:向下滑动的时候使用动画添加Item、添加Item时候的动画、删除Item时候的动画等。GitHub上也有很多这类动画,大家可以找自己喜欢的动画来设置。

  这里用的是系统给我们提供的一种动画——DefaultItemAnimator,我们直接调用下面这行代码,就为RecyclerView设置好了动画。

        // 设置RecyclerView的动画效果
rv.setItemAnimator(new DefaultItemAnimator());

  这个动画只有添加/删除Item的时候的动画,没有下滑加载Item时候的动画。

5、点击和长按事件:

  RecyclerView中没有给我们提供OnClickListener、OnLongClickListener这类接口,因此,我们需要自己写,方法就是使用接口回调。我们可以在Adapter中设置一个接口,里面有点击和长按两个抽象方法,然后在onBindViewHolder()方法中设置Item的View的点击和长按事件,分别回调这两个抽象方法,然后在外界为Adapter对象设置这个接口即可。具体的Adapter的代码如下:

public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.MyViewHolder> {
private Context context;
private List<String> data;
private LayoutInflater inflater; private OnRecyclerViewItemOperationListener listener; public RecyclerAdapter(Context context, List<String> data, boolean isStagger, OnRecyclerViewItemOperationListener listener) {
this.context = context;
this.data = data;
this.inflater = LayoutInflater.from(context);
this.listener = listener;
} @Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.recycleritem_item, parent, false);
return new MyViewHolder(view);
} @Override
public void onBindViewHolder(final MyViewHolder holder, final int position) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 如果不使用这个方法,则获取添加/删除的Item的position会出错
int layoutPosition = holder.getLayoutPosition();
listener.onRecyclerViewItemClickListener(layoutPosition);
}
});
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
listener.onRecyclerViewItemLongClickListener(holder.getLayoutPosition());
return true;
}
});
holder.tv.setText(data.get(position));
} @Override
public int getItemCount() {
return data.size();
} static class MyViewHolder extends RecyclerView.ViewHolder {
TextView tv; public MyViewHolder(View itemView) {
super(itemView);
tv = (TextView) itemView.findViewById(R.id.item_tv);
}
} public interface OnRecyclerViewItemOperationListener {
void onRecyclerViewItemClickListener(int position); void onRecyclerViewItemLongClickListener(int position);
}
}

6、添加/删除Item:

  添加、删除操作主要就是修改适配器绑定的数据源中的数据,加一项或删一项。在RecyclerView中需要注意的是,如果我们为RecyclerView设置了动画,就不能调用Adapter对象的notifyDataSetChanged()方法去更新数据源,因为如果调用notifyDataSetChanged()方法,就没有了动画效果。我们需要调用notifyItemInserted()方法来更新添加Item后的数据源,调用notifyItemRemoved()方法来更新删除Item后的数据源。

  另外,在添加了新的Item之后,如果我们调用onBindViewHolder()方法参数中的position,就会出现新添加的Item的position不准确的问题,因此,我们需要使用holder.getLayoutPosition()方法来获取当前Item所在的位置。

  最后贴一下我做的一个RecyclerView的小DEMO中的截屏,然后贴源码地址:

      

      

  下面是码云上的源码地址,供大家参考。

DEMO地址

【Android - 控件】之MD - RecyclerView的使用的更多相关文章

  1. Android控件大全(三)——RecyclerView

    是时候用RecyclerView来替换ListView和GridView了 好处就不多说了,百度一搜一大把,来介绍下用法 先定义个适配器: public class BottomSheetAdapte ...

  2. Android控件RecyclerView的基本用法

    Android控件RecyclerView的基本用法 转 https://www.jianshu.com/p/e71a4b73098f   github: https://github.com/Cym ...

  3. RxJava RxBinding RxView 控件事件 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  4. [Android Pro] android控件ListView顶部或者底部也显示分割线

    reference to  :  http://blog.csdn.net/lovexieyuan520/article/details/50846569 在默认的Android控件ListView在 ...

  5. Android控件Gridview实现仿支付宝首页,Fragment底部按钮切换和登录圆形头像

    此案例主要讲的是Android控件Gridview(九宫格)完美实现仿支付宝首页,包含添加和删除功能:Fragment底部按钮切换的效果,包含四个模块,登录页面圆形头像等,一个小项目的初始布局. 效果 ...

  6. Android 控件架构及View、ViewGroup的测量

    附录:示例代码地址 控件在Android开发的过程中是必不可少的,无论是我们在使用系统控件还是自定义的控件.下面我们将讲解一下Android的控件架构,以及如何实现自定义控件. 1.Android控件 ...

  7. Android - 控件android:ems属性

    Android - 控件android:ems属性http://blog.csdn.net/caroline_wendy/article/details/41684255?utm_source=tui ...

  8. Android 控件知识点,

    一.Android控件具有visibility属性,可以取三个值:visible(默认值)可见,invisible(不可见,但仍然占据原有的位置和大小,可以看做是变得透明了),gone(空间不仅不可见 ...

  9. UIAutomator定位Android控件的方法

    UIAutomator各种控件定位的方法. 1. 背景 使用SDK自带的NotePad应用,尝试去获得在NotesList那个Activity里的Menu Options上面的那个Add note菜单 ...

  10. 从Android系统出发,分析Android控件构架

    从Android系统出发,分析Android控件构架 Android中所有的控件追溯到根源,就是View 和ViewGroup,相信这个大家都知道,但是大家也许会不太清楚它们之间的具体关系是什么,在A ...

随机推荐

  1. 【java基础之异常】死了都要try,不淋漓尽致地catch我不痛快!

    目录 1.异常 1.1 异常概念 1.2 异常体系 1.3 异常分类 1.4 异常的产生过程解析 2. 异常的处理 2.1 抛出异常throw 2.2 Objects非空判断 2.3 声明异常thro ...

  2. 中文预训练模型ERNIE2.0模型下载及安装

    2019年7月,百度ERNIE再升级,发布持续学习的语义理解框架ERNIE 2.0,及基于此框架的ERNIE 2.0预训练模型, 它利用百度海量数据和飞桨(PaddlePaddle)多机多卡高效训练优 ...

  3. Office中国在这个开个博客

    Office中国在这个开个博客,先来show一下我的网站 Office中国/Access中国  http://www.office-cn.net Office中国百科:   http://baike. ...

  4. 第六篇 视觉slam中的优化问题梳理及雅克比推导

    优化问题定义以及求解 通用定义 解决问题的开始一定是定义清楚问题.这里引用g2o的定义. \[ \begin{aligned} \mathbf{F}(\mathbf{x})&=\sum_{k\ ...

  5. Vue学习笔记(五)——配置开发环境及初建项目

    前言 在上一篇中,我们通过初步的认识,简单了解 Vue 生命周期的八个阶段,以及可以应用在之后的开发中,针对不同的阶段的钩子采取不同的操作,更好的实现我们的业务代码,处理更加复杂的业务逻辑. 而在这一 ...

  6. 常用的webpack优化方法

    1. 前言 关于webpack,相信现在的前端开发人员一定不会陌生,因为它已经成为前端开发人员必不可少的一项技能,它的官方介绍如下: webpack 是一个模块打包器.webpack的主要目标是将 J ...

  7. python学习之【第六篇】:Python中的字典及其所具有的方法

    1.前言 字典是python中唯一的映射类型,采用键值对(key-value)的形式存储数据.python对key进行哈希函数运算,根据计算的结果决定value的存储地址,因此,字典的key必须是可哈 ...

  8. 我们碰到了大麻烦,一个新来的传教士惹恼了上帝,上帝很愤怒,要求我们把圣经(bbe.txt)背熟,直至他说哪个单词,我们就要飞快的回答出这个单词在第几行第几个单词位置。听说你是个优秀的程序员,那么髟助我们完成这个不可能的任务吧

    编程任务:1.我们碰到了大麻烦,一个新来的传教士惹恼了上帝,上帝很愤怒,要求我们把圣经(bbe.txt)背熟,直至他说哪个单词,我们就要飞快的回答出这个单词在第几行第几个单词位置.听说你是个优秀的程序 ...

  9. 创建python的虚拟环境

    为什么需要虚拟环境?如果你现在用Django 1.10.x写了个网站,然后你的领导跟你说,之前有一个旧项目是用Django 0.9开发的,让你来维护,但是Django 1.10不再兼容Django 0 ...

  10. SpringSecurity系列之自定义登录验证成功与失败的结果处理

    一.需要自定义登录结果的场景 在我之前的文章中,做过登录验证流程的源码解析.其中比较重要的就是 当我们登录成功的时候,是由AuthenticationSuccessHandler进行登录结果处理,默认 ...