文/poberWong(简书作者)
原文链接:http://www.jianshu.com/p/7fdfea845937
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

正文:

RecyclerView的优势:

  • 它自带ViewHolder来实现View的复用机制,再也不用ListView那样在getView()里自己写了
  • 使用LayoutManager可以实现ListView,GridView以及流式布局的列表效果
  • 通过setItemAnimator(ItemAnimator animator)可以实现增删动画(懒的话,可以使用默认的ItemAnimator对象,效果也不错)
  • 控制item的间隔,可以使用addItemDecoration(ItemDecoration decor),不过里边的ItemDecoration是一个抽象类,需要自己去实现...

用法介绍:

  1. 导入RecyclerView的v7库:
    RecyclerView是一个android.support.v7库里的控件,因此在使用的时候我们需要在gradle配置文件里加上compile 'com.android.support:recyclerview-v7:22.2.1'来引入google官方的这个库
  2. xml布局中,使用常规的控件引入方式,来引入RecyclerView,如下:

     <android.support.v7.widget.RecyclerView
    android:id="@+id/recyclerview_content"
    style="?recyclerview_style"
    android:scrollbars="vertical"
    android:fadeScrollbars="true"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_marginBottom="-55dp" />
  3. 代码中的写法基本和ListView相差无几,但还是要重点说一下:

    1. 在实例化RecyclerView之后,我们需要使用setLayoutManager()给它设置布局管理器,其中的实参即就是LayoutManager,这里总共有两种LayoutManager:

      1. StaggeredGridLayoutManager,是我们之前提到的流式布局:
        它有一个构造方法StaggeredGridLayoutManager(int spanCount, int orientation),第一个是网格的列数,第二个参数是数据呈现的方向
        (如果是竖直,那么第一个参数的意义就是列数,反之为行数。而且第二个参数在StaggeredGridLayoutManager里也有同样名称的常量,请同学们自行采纳[这里还是建议大家使用库里自带的常量,因为他们一般都是整型值,这样可以避免各个类里所判别的常量值不一样而导致的其他问题]);
      2. GridLayoutManager,网格布局(流式布局应该是它的一个特殊情况):
        GridLayoutManager(Context context, int spanCount)
        GridLayoutManager(Context context, int spanCount, int orientation,
        boolean reverseLayout)
        需要说明的是,最后一个参数表示的是是否逆向布局(意思是将数据反向显示,原先从左向右,从上至下。设为true之后全部逆转)。
        小提示:在这两个LayoutManager中,默认的orientation为vertical,reverseLayout为false。对应的参数在GridLayoutManager中都有对应的方法来进行补充设置。而在StaggeredGridLayoutManager中所有的方法都针对reverseLayout做了判断,然而它并没有给出这个参数设定值的api。

      3. 线性布局, LinearLayoutManager
        LinearLayoutManager(Context context)构建一个默认布局方向为VERTICAL的RecyclerView,这种样式也是ListView默认的。
        LinearLayoutManager(Context context, int orientation, boolean reverseLayout)。发现没有,线性布局和网格布局几乎是一样的。只是少了一个spanCount参数。

    2. 和ListView一样,为RecyclerView设置Adapter

       class HomeAdapter extends RecyclerView.Adapter<HomeAdapter.MyViewHolder>
      { @Override
      public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
      {
      MyViewHolder holder = new MyViewHolder(LayoutInflater.from(
      HomeActivity.this).inflate(R.layout.item_home, parent,
      false));
      return holder;
      } @Override
      public void onBindViewHolder(MyViewHolder holder, int position)
      {
      holder.tv.setText(mDatas.get(position));
      } @Override
      public int getItemCount()
      {
      return mDatas.size();
      } class MyViewHolder extends ViewHolder
      {
      TextView tv; public MyViewHolder(View view)
      {
      super(view);
      tv = (TextView) view.findViewById(R.id.id_num);
      }
      }
      }

      如上,即就是Adapter的写法。其中的ViewHolder就是拿来负责View的回收和复用的,这样就不需要我们自己写完ViewHolder之后,还要在getView(int position, View convertView, ViewGroup parent)里一顿判断,一顿绑定,一顿find了。而且这里的ViewHolder成为了RecyclerView中必须继承的一部分,重写完后就需要放入 RecyclerView.Adapter< >这里来对基类的范型初始化。

    在这里,Recyclerview已经为你封装好了:

    getItemCount()就不必多说了,和ListView是一样的

    getItemViewType(int position)是用来根据position的不同来实现RecyclerView中对不同布局的要求。从这个方法中所返回的值会在onCreateViewHolder中用到。比如头部,尾部,等等的特殊itemView(这里说成ViewHolder比较好)都可以在这里进行判断。

    onCreateViewHolder(ViewGroup parent, int viewType)是用来配合写好的ViewHolder来返回一个ViewHolder对象。这里也涉及到了条目布局的加载。viewType则表示需要给当前position生成的是哪一种ViewHolder,这个参数也说明了RecyclerView对多类型ItemView的支持。

    onBindViewHolder(MyViewHolder holder, int position)专门用来绑定ViewHolder里的控件和数据源中position位置的数据。
    这里,会有人问,那么item的子控件findViewById 去哪儿了?我们把它交给了ViewHolder的构造方法(其他方法也可以),它的本质是在onCreateViewHolder方法里生成ViewHolder的时候执行的。

提升:

  • 代码重构:
    在上边看了这么多,有木有觉得,ViewHolder的功能并不是非常明确?它既负责了子控件的查询,又负责了子控件的装载工作。而布局加载和数据绑定却交给了Adapter......
    我们来看看掘金的做法:
    首先,它把Adapter和ViewHolder的功能以一种较为低耦合的方式进行了职能分离,让ViewHolder里所有的逻辑代码全部都只出现在ViewHolder中。我们现在就对前边提到的代码进行重构:

    ViewHodler:

    class MyViewHolder extends ViewHolder{
    TextView tv;
    public MyViewHolder(Context context, View view){
    super(view);
    tv = (TextView) view.findViewById(R.id.id_num); //当然,我们也可以在这里使用view来对RecyclerView的一个item进行事件监听,也可以使用
    //tv等子控件来实现item的子控件的事件监听。这也是我之所以要传context的原因之一呢~
    ......
    } public static MyViewHolder newInstance(Activity context, ViewGroup parent){
    View view = LayoutInflater.from(
    context).inflate(R.layout.item_home, parent,false);
    return new MyViewHolder(context, view);
    }
    } //你没看错,数据绑定也被整合进来了,
    //将adapter里的数据根据position获取到后传进来。当然,也可以根据具体情况来做调整。
    public void onBinViewHolder(String data){
    tv.setText(data);//既然这里也有子控件,那么这里也可以做item子控件的事件监听喽
    }

    RecyclerView

    class HomeAdapter extends RecyclerView.Adapter<MyViewHolder>{
    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType){//如需要,还要对viewType做判断
    return MyViewHolder.newInstance(this, parent)
    } @Override
    public void onBindViewHolder(MyViewHolder holder, int position){
    holder.onBindViewHolder(mDatas.get(position));
    } @Override
    public int getItemCount(){
    return mDatas.size();
    }
    }
  • 抽取一个条目点击事件,让它更像ListView:

    class HomeAdapter extends RecyclerView.Adapter<MyViewHolder>{
    private OnItemClickListener mOnItemClickListener; public void setOnItemClickLitener(OnItemClickLitener mOnItemClickLitener)
    {
    this.mOnItemClickLitener = mOnItemClickLitener;
    } @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType){//如需要,还要对viewType做判断
    return MyViewHolder.newInstance(this, parent)
    } @Override
    public void onBindViewHolder(MyViewHolder holder, int position){
    holder.onBindViewHolder(mDatas.get(position)); //如果设置了回调,则设置点击事件
    if (mOnItemClickLitener != null) {
    viewHolder.itemView.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
    mOnItemClickLitener.onItemClick(viewHolder.itemView, i);
    }
    });
    }
    } @Override
    public int getItemCount(){
    return mDatas.size();
    } public interface OnItemClickLitener {
    void onItemClick(View view, int position);
    }
    }

    接口调用

      mAdapter.setOnItemClickLitener(new OnItemClickLitener() {
    @Override
    public void onItemClick(View view, int position) {
    ......
    }
    });

External

上边提到了

控制item的间隔,可以使用addItemDecoration(ItemDecoration decor),不过里边的ItemDecoration是一个抽象类,需要自己去实现...

这个问题,那我们来实际解决一下:

  • 这是羊神实现的一个子类
package com.zhy.sample.demo_recyclerview;

/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* limitations under the License.
*/ import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.State;
import android.util.Log;
import android.view.View; /**
* This class is from the v7 samples of the Android SDK. It's not by me!
* <p/>
* See the license above for details.
*/
public class DividerItemDecoration extends RecyclerView.ItemDecoration { 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; private Drawable mDivider; private int mOrientation; public DividerItemDecoration(Context context, int orientation) {
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
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) {
Log.v("recyclerview - itemdecoration", "onDraw()"); if (mOrientation == VERTICAL_LIST) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
} } public void drawVertical(Canvas c, RecyclerView parent) {
final int left = parent.getPaddingLeft();
final int right = parent.getWidth() - parent.getPaddingRight(); final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
android.support.v7.widget.RecyclerView v = new android.support.v7.widget.RecyclerView(parent.getContext());
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int top = child.getBottom() + params.bottomMargin;
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实例添加分割线

    addItemDecoration(new DividerItemDecoration(this,
    DividerItemDecoration.VERTICAL_LIST));
  • 系统默认的分割线往往达不到我们产品和设计师的要求,怎么办呢?

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

通过以上xml属性,将系统的listDivider设为自己画出来的分割线,只需要放在你对应activity的主题下即可。
(不会画的,请自行google,关键字:android shape

Introducing RecyclerView(二)的更多相关文章

  1. Introducing RecyclerView(一)

    RecyclerView 是Android L版本中新添加的一个用来取代ListView的SDK,它的灵活性与可替代性比listview更好.接下来通过一系列的文章讲解如何使用RecyclerView ...

  2. android RecyclerView (二) ItemDecoration 详解

    RecyclerView 已经推出了一年多了,日常开发中也已经彻底从 ListView 迁移到了 RecyclerView,但前两天有人在一个安卓群里面问了个关于最顶上的 item view 加蒙层的 ...

  3. Andorid上拉加载更多的几种实现方式

    1.前言 Andriod中上拉加载更多的效果随处可见,因为一次性要展现的数据太多信息量太大的话,给用户的体验就很差(加载慢,界面卡顿.流量消耗大等),而加载更多可以控制每次加载条目的数量以达到快速加载 ...

  4. RecyclerView解密篇(二)

    在上一篇(RecyclerView解密篇(一))文章中简单的介绍了RecyclerView的基本用法,接下来要来讲讲RecyclerView的更多用法,要实现不同的功能效果,大部分都还是在于Recyc ...

  5. RecyclerView的使用(二)

    上篇博客讲了如何导入RecyclerView . 本章将告诉小伙伴们怎么使用RecyclerView : 第一步:在布局中使用RecyclerView并且绑定控件: 第二步:自定义Adapter:(继 ...

  6. Android RecyclerView单击、长按事件:基于OnItemTouchListener +GestureDetector标准实现(二),封装抽取成通用工具类

     Android RecyclerView单击.长按事件:基于OnItemTouchListener +GestureDetector标准实现(二),封装抽取成通用工具类 我写的附录文章2,介绍了 ...

  7. Android RecyclerView使用详解(二)

    在上一篇(RecyclerView使用详解(一))文章中简单的介绍了RecyclerView的基本用法,接下来要来讲讲RecyclerView的更多用法,要实现不同的功能效果,大部分都还是在于Recy ...

  8. RecyclerView下拉刷新上拉加载(二)

    listview下拉刷新上拉加载扩展(一) http://blog.csdn.net/baiyuliang2013/article/details/50252561 listview下拉刷新上拉加载扩 ...

  9. Android用RecyclerView实现的二维Excel效果组件

    excelPanel 二维RecyclerView.不仅可以加载历史数据,而且可以加载未来的数据.   包括在您的项目中 excelPanel 二维RecyclerView.不仅可以加载历史数据,而且 ...

随机推荐

  1. shell脚本实例(2)

    1.传给脚本一个参数:目录,输出该目录中文件最大的,文件名和文件大小 #!/bin/bash if [ $# -ne 1 -o ! -d $1 ];then echo "Args is er ...

  2. Microsoft SQL Server Product Samples:Database

    从SQL Server 2005 之后示例数据都为AdventureWorks,需要的通过codeplex网站下载.这样设计的目的应该在于是生产库行不必要的用户以及权限分配. 从以下网址访问http: ...

  3. 第22条:理解NSCopying协议

    如果想自定义类支持拷贝操作,那就要实现NSCopying协议(而不是复写copy方法)或 NSMutableCopying的协议. 不可变版本的拷贝: NSCopying协议,该协议只有一个方法: - ...

  4. IOS之swift第一课基础代码

    import Foundation //import Foundation 导入模块,专业术语也是导入 包,库的 意思. var str = "Hello World" //声明一 ...

  5. freemark页面中获取list循环中的counter

    如何在freemark页面中获取到当前list循环的counter 直接上代码 <#list lists as x> <#assign j=x?counter> ${j} // ...

  6. Java实战之02Hibernate-03Session中的常用方法

    九.Session中的常用方法 1.save方法 都是临时态————>持久态 2.persist方法 作用: 持久化临时态对象. 与save方法的区别: 开始了事务:persist和save没有 ...

  7. List分页显示

    //对List集合 数据进行分页  int pageNum = page.getPageNo();// 当前显示第几页 int perPageNum = page.getPageSize(); // ...

  8. CAF(C++ actor framework)使用随笔(projection 用法)(一)

    最近干活在写毕设,用到了CAF,看了文档,发现了一些小坑,自己摸索写点随笔.(CAF的github网站 https://github.com/actor-framework/actor-framewo ...

  9. Json操作

    <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <m ...

  10. starling 中 的特效

    一. 最好用同一张图片进行缩放变形等处理后组合,这样可以每帧一draw