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. Python的深浅拷贝

    Python的深浅拷贝 深浅拷贝 1. 赋值,对于list, set, dict来说, 直接赋值. 其实是把内存地址交给变量并不是复制一份内容 list1 = [']] list2 = list1 p ...

  2. 《JavaScript设计模式与开发实践》-- 迭代器模式

    详情个人博客:https://shengchangwei.github.io/js-shejimoshi-diedaiqi/ 迭代器模式 1.定义 迭代器模式: 是指提供一种方法顺序访问一个聚合对象中 ...

  3. .net core 3.0 在过滤器读取request.body 里的请求,controller[FromBody]读取不到参数,解决办法

    1,注入IHttpContextAccessor httpContex 2,var req = _httpContext.HttpContext.Request; //  这句很重要,开启读取 否者下 ...

  4. 【XSY2131】【BZOJ1857】【SCOI2010】传送带

    Description 题目描述: 在一个二维平面上有两条传送带,每一条传送带可以看成是一条线段.两条传送带分别为线段AB和线段CD.小y在AB上的移动速度为P,在CD上的移动速度为Q,在平面上的移动 ...

  5. 深入了解 Java Resource && Spring Resource

    在Java中,为了从相对路径读取文件,经常会使用的方法便是: xxx.class.getResource(); xxx.class.getClassLoader().getResource(); 在S ...

  6. 1、第一个TensorFlow程序

    import tensorflow as tf import os os.environ[' #去掉警告,将警告级别提升 a = tf.constant(2) #定义一个常量 b = tf.const ...

  7. js清除节点内容(改变标签元素)

    <!DOCTYPE HTML><html> <head>        <meta http-equiv="Content-Type" c ...

  8. 从壹开始 [ Ids4实战 ] 之五 ║ 多项目集成统一认证中心的思考

    前言 哈喽大家好,好久都没有写文章了,这次又重新开始写技术文章了,半年前我还是一直保持每周都写文章的,后来是为了响应群友的号召,开始踏上了录制视频(https://www.bilibili.com/v ...

  9. m98 lsc rp-- 赛

    lsc 这次又烧rp了! T1随机化艹spj 本机测试输出字符串长度没有低于1W的,考完发现凉凉 但是lemon又救了我的*命,垃圾lsc又烧rp了!

  10. php imagick设置图片圆角的方法

    php imagick设置图片圆角的方法 <pre>header('Content-Type: image/png'); $image = new Imagick('http://stat ...