RecyclerView是从support-v7库开始引入的,用于取代 ListView 。
相比于 ListView, RecyclerView更加灵活、高效和无限的可扩展性。

环境准备

  • Windows/Mac
  • Android Studio 1.4
  • Android SDK
  • 科学上网(开发人员必备)

首先,我们需要引入依赖库:

compile 'com.android.support:appcompat-v7:22.2.1'
compile 'com.android.support:recyclerview-v7:22.2.1'

简单的 ListView 列表

我们首先来实现一个简单的 listview 列表效果:

因为此处用到了 design 库的新控件CardView,所以我们还需要加入如下依赖库:

compile 'com.android.support:cardview-v7:22.2.1'

首先创建一个 RecyclerView 的新项目,修改我们的 activity_main.xml 代码如下:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    android:id="@+id/drawer_layout"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/rc_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">
        </android.support.v7.widget.RecyclerView>

</FrameLayout>

RecyclerView.Adapter

RecyclerView 的适配器实现与以往的 ListView 稍有不同,需要继承它自身特有的 RecyclerView.Adapter,并且RecyclerView 必须通过自带的 RecyclerView.ViewHolder 来构建 Item。

adapter 基本代码如下:

public class RecyclerViewApdater extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    public RecyclerViewApdater() {
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    ...
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    ...
    }

    @Override
    public int getItemCount() {
        return count;
    }
}

根据代码我们可以发现和ListView不同的是,我们重写了 onCreateViewHolder(ViewGroup parent, int viewType)onBindViewHolder(RecyclerView.ViewHolder holder, int position)。根据名字我们可知,这两个方法,一个是创建数据,一个是绑定数据。其实这两个方法干的事情和在 ListViewgetView方法里面做的事情一样。但是现在布局和数据分离的方式明显结构清晰多了。

onCreateViewHolder

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list, parent, false);
        return new CardViewHolder(view);
    }

onCreateViewHolder 方法的返回的参数是一个 RecyclerView.ViewHolder,这个东西类似我们使用 ListView的时候自定义的模板类ViewHolder。关于RecyclerView.ViewHolder,官方文档的介绍是 用于描述 recyclerview 里面的项目视图和元数据。如何理解这句话?通俗的讲,就是用来存储数据和布局视图的工具类。

    public static class CardViewHolder extends RecyclerView.ViewHolder {

        public CardView mCardView;
        public TextView mTextView;

        public CardViewHolder(View itemView) {
            super(itemView);
            mCardView = (CardView) itemView.findViewById(R.id.view);
            mTextView = (TextView) itemView.findViewById(R.id.tv_card);
        }
    }

RecyclerView.ViewHolder的构造函数需要传入一个View作为参数。这个view就是每一个item显示的布局文件。我们接受到这个 view 后可以对这个view进行一些操作,比如findViewById出所有的子控件。这个类还封装了一些其他方法供我们使用,具体可以参考官网文档 RecyclerView.ViewHolder

回到onCreateViewHolder(ViewGroup parent, int viewType),发现参数有两个,我们主要关注第二个int viewType,这个参数是由getItemViewType(int position)这个方法返回的,所以我们可以通过设置type来定制出我们需要的不同 Item。

onBindViewHolder

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        CardViewHolder mCardViewHolder = (CardViewHolder)holder;
        mCardViewHolder.mTextView.setText("Hello CardView " + position);
    }

这个方法有两个参数 RecyclerView.ViewHolder holderint position。一个就是我们的视图数据,一个是当前 item 的位置。

onCreateViewHolderonBindViewHolder 这两个方法是轮流调用的,先调用 onCreateViewHolder 生产Item,然后调用 onBindViewHolder 来对视图进行数据处理或布局变化。当滚动的时候只会调用onBindViewHolder显示数据。也就是说RecyclerView会自动去缓存布局并且复用。

关于 RecyclerView 换成机制的问题,可以参考 http://lujun.co/2015/06/29/对recyclerview的一点探讨/

RecyclerViewApdater的问完整代码如下:

public class RecyclerViewApdater extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    int count = 0;

    public RecyclerViewApdater() {
    }

    public RecyclerViewApdater(int size) {
        count = size;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list, parent, false);
        return new CardViewHolder(view);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        CardViewHolder mCardViewHolder = (CardViewHolder)holder;
        mCardViewHolder.mTextView.setText("Hello CardView " + position);
    }

    public boolean isHeaderPosition(int position){
        return position == 0;
    }

    @Override
    public int getItemCount() {
        return count;
    }

    public static class CardViewHolder extends RecyclerView.ViewHolder {

        public CardView mCardView;
        public TextView mTextView;

        public CardViewHolder(View itemView) {
            super(itemView);
            mCardView = (CardView) itemView.findViewById(R.id.view);
            mTextView = (TextView) itemView.findViewById(R.id.tv_card);
        }
    }

    public static class CardViewHeaderHolder extends RecyclerView.ViewHolder {

        public CardViewHeaderHolder(View itemView) {
            super(itemView);
        }
    }
}

LayoutManager

使用RecyclerView一定要通过setLayoutManager来设置布局类别。目前 support v7库提供了三种布局供我们使用。

  • LinearLayoutManager 线性布局,比如ListView效果我们就需要使用这种
  • GridLayoutManager 九宫格布局,类似GridView
  • StaggeredGridLayoutManager 瀑布流效果

因为我们需要实现ListView效果,所以我们代码如下:

public class MainActivity extends AppCompatActivity {

    private FrameLayout drawerlayout;
    private android.support.v7.widget.RecyclerView rccontent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        this.rccontent = (RecyclerView) findViewById(R.id.rc_content);
        this.drawerlayout = (FrameLayout) findViewById(R.id.drawer_layout);

        setSupportActionBar(toolbar);
        getSupportActionBar().setDisplayShowTitleEnabled(false);
        getSupportActionBar().setHomeButtonEnabled(true);

        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
       // GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 2);
       // StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(3, 1);
        RecyclerViewApdater recyclerViewApdater = new RecyclerViewApdater(20);
        rccontent.setLayoutManager(linearLayoutManager);
        rccontent.setAdapter(recyclerViewApdater);
    }
}

onItemClickListener

我们会发现RecyclerView并没有提供onItemClickListener事件监听,如果我们需要在Activity里面监听 Item 的点击事件。可以通过接口回调的方式来实现。

RecyclerView.Adapter里面添加接口

    public interface onItemClickLintener{
        public void onItemClick(View v, int position);
    }

添加接口方法

    private static onItemClickLintener mLintener = null;

    public void setOnItemClickLintener(onItemClickLintener mItemClick){
        this.mLintener = mItemClick;
    }

RecyclerView.ViewHolder中接受事件

    public static class CardViewHolder extends RecyclerView.ViewHolder {

        public CardView mCardView;
        public TextView mTextView;

        public CardViewHolder(final View itemView) {
            super(itemView);
            mCardView = (CardView) itemView.findViewById(R.id.view);
            mTextView = (TextView) itemView.findViewById(R.id.tv_card);

            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    if (mLintener != null) {
                        mLintener.onItemClick(view, getAdapterPosition());
                    }
                }
            });

        }
    }

通过RecyclerViewApdater来使用

        RecyclerViewApdater recyclerViewApdater = new RecyclerViewApdater(20);
        recyclerViewApdater.setOnItemClickLintener(new RecyclerViewApdater.onItemClickLintener() {
            @Override
            public void onItemClick(View v, int position) {
                Log.d("demo", "------ " + position);
            }
        });