Android RecyclerView使用详解

简介

RecyclerView是Google在android-supportv7包中推出的一个新的控件,该控件的主要作用是用于替代ListView、GridView,相比较于这两种控件RecyclerView有以下有点:

  • RecyclerView内部封装了ViewHolder,更加方便Item的复用
  • 提供了一种插拔式的体验,高度解耦,异常灵活,增加控件的扩展性。

RecyclerView的灵活性可以体现在以下几点

  • 通过RecyclerView.setLayoutManager()方法添加布局管理器
  • 通过RecyclerView.addItemDecoration()方法添加item分割线
  • 通过RecyclerView.setItemAnimator()方法添加item增删动画

LayoutManager布局管理器介绍

  • LinearLayoutManager:线性布局
  • GridLayoutManager:网格布局
  • StaggeredGridLayoutManager:瀑布流布局

RecyclerView使用

导入类库

  • 对于使用eclipse开发用户,需要导入sdk/extras/android/support/v7目录下recyclerview类库
  • 对于Android studio开发用户,直接通过修改build.gradle添加类库即可

通过RecyclerView实现ListView:

效果如下:
Android RecyclerView使用详解_第1张图片

实现代码如下:

package com.example.zhangke.recyclerviewdemo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    private RecyclerView mRecyclerView;
    private ArrayList mDatas;
    private RecyclerViewAdapter mAdapter;
    private StaggeredGridLayoutAdapter mStagAdapter;
    private RecyclerView.ItemDecoration mItemDecoration;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mRecyclerView = (RecyclerView) findViewById(R.id.recycleview);
        initData();

        //垂直listview
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        //设置分割线
        mItemDecoration = new DividerItemDecoration(this, DividerItemDecoration.VERTICAL);
        mRecyclerView.addItemDecoration(mItemDecoration);
        // 设置增删动画
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());

        mAdapter = new RecyclerViewAdapter(this, mDatas);

        //设置点击事件
        mAdapter.setOnItemClickListener(new RecyclerViewAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {
                Toast.makeText(MainActivity.this, "onItemClick " + position, Toast.LENGTH_SHORT).show();
            }
        });

        mAdapter.setOnLongItemClickListener(new RecyclerViewAdapter.OnLongItemClickListener() {
            @Override
            public void onLongItemClick(View view, int position) {
                Toast.makeText(MainActivity.this, "onLongItemClick " + position, Toast.LENGTH_SHORT).show();
            }
        });

        //设置Adapter
        mRecyclerView.setAdapter(mAdapter);

    }

    /**
     * 初始化数据
     */
    private void initData() {
        mDatas = new ArrayList();
        for (int i = 0; i < 100; i++) {
            mDatas.add("item " + i);
        }

    }


    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        switch (item.getItemId()) {
            case R.id.add:
                mAdapter.addItem(1, "item" + 1);
                break;
            case R.id.remove:
                mAdapter.removeItem(1);
                break;

        }
        return true;
    }
}

RecyclerView.Adapter说明

同ListView一样,RecyclerView现实数据需要通过Adapter实现,Google已给我们提供了RecyclerView.Adapter的内部抽象类,所以必须通过继承该类实现一个Adapter。继承该类需要复写三个方法:

  1. public int getItemCount():获得item数目
  2. public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType):创建一个ViewHolder
  3. public void onBindViewHolder(final RecyclerViewHolder holder, int position):绑定Viewholder即对Item进行具体操作

注意:由于Adapter会在内部封装一个ViewHolder,所以我们还要自定义一个ViewHolder。

RecyclerView item点击事件

对于RecyviewView,系统并没有提供item点击事件,所以在自定义Adapter时,通常会自己定义Item的点击事件的接口以实现item点击。

具体代码如下:

package com.example.zhangke.recyclerviewdemo;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.text.Layout;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import java.util.ArrayList;

/**
 * Created by zhangke on 16/6/15.
 */
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.RecyclerViewHolder> {


    private Context mContext;
    private ArrayList mDatas;
    private OnItemClickListener mOnItemClickListener;
    private OnLongItemClickListener mOnLongItemClickListener;

    public RecyclerViewAdapter(Context context, ArrayList datas) {
        this.mContext = context;
        this.mDatas = datas;
    }


    @Override
    public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        RecyclerViewHolder holder = new RecyclerViewHolder(View.inflate(mContext, R.layout.item, null));
        return holder;
    }

    @Override
    public void onBindViewHolder(final RecyclerViewHolder holder, int position) {
        holder.mTextView.setText(mDatas.get(position));
        if (mOnItemClickListener != null) {
            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int layoutPosition = holder.getLayoutPosition();
                    mOnItemClickListener.onItemClick(v, layoutPosition);
                }
            });
        }
        if (mOnLongItemClickListener != null) {
            holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    int layoutPosition = holder.getLayoutPosition();
                    mOnLongItemClickListener.onLongItemClick(v, layoutPosition);
                    return true;
                }
            });
        }
    }

    @Override
    public int getItemCount() {
        return mDatas.size();
    }

    /**
     * viewholder
     */
    class RecyclerViewHolder extends RecyclerView.ViewHolder {

        private TextView mTextView;

        public RecyclerViewHolder(View itemView) {
            super(itemView);

            mTextView = (TextView) itemView.findViewById(R.id.textview);
        }
    }

    /**
     * 添加
     * @param position
     * @param item
     */
    public void addItem(int position, String item){
        mDatas.add(position, item+position);
        notifyItemInserted(position);
    }

    /**
     * 删除
     * @param position
     */
    public void removeItem(int position){
        mDatas.remove(position);
        notifyItemRemoved(position);
    }

    /**
     * item点击事件
     */
    public interface OnItemClickListener {
        /**
         * item点击事件
         *
         * @param view
         * @param position
         */
        void onItemClick(View view, int position);
    }

    public interface OnLongItemClickListener {
        /**
         * item长按点击事件
         *
         * @param view
         * @param position
         */
        void onLongItemClick(View view, int position);
    }


    public void setOnItemClickListener(OnItemClickListener listener) {
        this.mOnItemClickListener = listener;
    }

    public void setOnLongItemClickListener(OnLongItemClickListener listener) {
        this.mOnLongItemClickListener = listener;
    }
}

ItemDecoration分割线

在之前已经提到,RecyclerView有addItemDecoration()的方法,该方法的作用是为RecyclerView添加item分割线,为此我们不难想象RecyclerView默认是没有分割线的。但是google官方并没有提供具体ItemDecoration的方法,仅仅是定义了一个ItemDecoration的抽象类,所以要想实现分割线效果,必须要自己实现一个ItemDecoration。

ItemDecoration抽象方法介绍:

  1. public void onDraw(Canvas c, RecyclerView parent, State state):绘制分割线
  2. public void onDrawOver(Canvas c, RecyclerView parent, State state):绘制分割线,该方法后于onDraw,一般实现一个即可
  3. public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state):用于计算item分割线的偏移量

以下是通过ItemDecoration实现ListView和GridView的分割线:

ListView分割线:

package com.example.zhangke.recyclerviewdemo;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;

/**
 * item分割线
 * 

* Created by zhangke on 16/6/15. */ public class DividerItemDecoration extends RecyclerView.ItemDecoration { /** * 分割线 */ private static int[] ATTRS = new int[]{android.R.attr.listDivider}; /** * 水平分割线 */ public static final int HORIZONTAL = LinearLayoutManager.HORIZONTAL; /** * 垂直分割线 */ public static final int VERTICAL = LinearLayoutManager.VERTICAL; /** * 上下文 */ private Context mContext; /** * 分割线 */ private Drawable mDivider; private int mOrientation; /** * @param context 上下文 * @param orientation 分割线方向 */ @TargetApi(Build.VERSION_CODES.LOLLIPOP) public DividerItemDecoration(Context context, int orientation) { this.mContext = context; // TypedArray typedArray = mContext.obtainStyledAttributes(ATTRS); // mDivider = typedArray.getDrawable(0); // typedArray.recycle(); mDivider = context.getDrawable(R.drawable.divider_view); setOrientation(orientation); } /** * 设置分割线方向 * * @param orientation */ private void setOrientation(int orientation) { if (orientation != HORIZONTAL && orientation != VERTICAL) { throw new IllegalArgumentException("参数异常"); } this.mOrientation = orientation; } @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { if (mOrientation == HORIZONTAL) { drawHorizontal(c, parent, state); } else { drawVertical(c, parent, state); } super.onDraw(c, parent, state); } /** * 绘制垂直方向分割线 * * @param c * @param parent * @param state */ private void drawVertical(Canvas c, RecyclerView parent, RecyclerView.State state) { int left = parent.getPaddingLeft(); int right = parent.getWidth() - parent.getPaddingRight(); int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { View childView = parent.getChildAt(i); RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) childView.getLayoutParams(); int top = childView.getBottom() + layoutParams.bottomMargin + Math.round(ViewCompat.getTranslationY(childView)); int bottom = top + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } /** * 绘制水平方向分割线 * * @param c * @param parent * @param state */ private void drawHorizontal(Canvas c, RecyclerView parent, RecyclerView.State state) { int top = parent.getPaddingTop(); int bottom = parent.getHeight() - parent.getPaddingBottom(); int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { View childView = parent.getChildAt(i); RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) childView.getLayoutParams(); int left = childView.getRight() + layoutParams.rightMargin + Math.round(ViewCompat.getTranslationX(childView)); int right = left + mDivider.getIntrinsicWidth(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } /** * 设置分割线宽偏移量: * 通过RecyclerView源码可知:getItemOffset方法会在measureChild方法中调用,我们通过给方法的参数outRect * 设置left、top、right、bottom,设置的值会被增加到childView的padding值中。 * * @param outRect * @param view * @param parent * @param state */ @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { if (mOrientation == HORIZONTAL) { outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0); } else { outRect.set(0, 0, 0, mDivider.getIntrinsicHeight()); } } }

GridView分割线:

package com.example.zhangke.recyclerviewdemo;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;

/**
 * item分割线
 * 

* Created by zhangke on 16/6/15. */ public class DividerGridItemDecoration extends RecyclerView.ItemDecoration { /** * 分割线 */ private static int[] ATTRS = new int[]{android.R.attr.listDivider}; /** * 水平分割线 */ public static final int HORIZONTAL = LinearLayoutManager.HORIZONTAL; /** * 垂直分割线 */ public static final int VERTICAL = LinearLayoutManager.VERTICAL; /** * 上下文 */ private Context mContext; /** * 分割线 */ private Drawable mDivider; private int mOrientation; private int spanCount; /** * @param context 上下文 * @param orientation 分割线方向 */ @TargetApi(Build.VERSION_CODES.LOLLIPOP) public DividerGridItemDecoration(Context context, int orientation) { this.mContext = context; // TypedArray typedArray = mContext.obtainStyledAttributes(ATTRS); // mDivider = typedArray.getDrawable(0); // typedArray.recycle(); mDivider = context.getDrawable(R.drawable.divider_view); setOrientation(orientation); } /** * 设置分割线方向 * * @param orientation */ private void setOrientation(int orientation) { if (orientation != HORIZONTAL && orientation != VERTICAL) { throw new IllegalArgumentException("参数异常"); } this.mOrientation = orientation; } @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { drawHorizontal(c, parent, state); drawVertical(c, parent, state); } /** * 绘制垂直方向分割线 * * @param c * @param parent * @param state */ private void drawVertical(Canvas c, RecyclerView parent, RecyclerView.State state) { int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { View child = parent.getChildAt(i); RecyclerView.LayoutParams params = (GridLayoutManager.LayoutParams) child.getLayoutParams(); int left = child.getLeft() - params.leftMargin; int right = child.getRight()+ params.rightMargin; int top = child.getBottom() + params.bottomMargin; int bottom = top + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } /** * 绘制水平方向分割线 * * @param c * @param parent * @param state */ private void drawHorizontal(Canvas c, RecyclerView parent, RecyclerView.State state) { int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { View child = parent.getChildAt(i); RecyclerView.LayoutParams params = (GridLayoutManager.LayoutParams) child.getLayoutParams(); int left = child.getRight() + params.rightMargin; int right = left + mDivider.getIntrinsicWidth(); int top = child.getTop() - params.topMargin; int bottom = child.getBottom() + params.bottomMargin; mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } /** * 设置分割线宽偏移量: * 通过RecyclerView源码可知:getItemOffset方法会在measureChild方法中调用,我们通过给方法的参数outRect * 设置left、top、right、bottom,设置的值会被增加到childView的padding值中。 */ @Override public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) { if (isLastColum(itemPosition, parent)) { //最后一列 outRect.set(0, 0, 0, mDivider.getIntrinsicHeight()); } else if (isLastRow(itemPosition, parent)) { //最后一行 outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0); } else { outRect.set(0, 0, mDivider.getIntrinsicWidth(), mDivider.getIntrinsicHeight()); } } /** * 是否是最后一行 * * @param itemPosition * @param parent * @return */ private boolean isLastRow(int itemPosition, RecyclerView parent) { int spanCount = getSpanCount(parent); RecyclerView.LayoutManager layoutManager = parent.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) { int childCount = parent.getAdapter().getItemCount(); int lastRowCount = childCount % spanCount; if (lastRowCount == 0) { return false; } else if(itemPosition >= (childCount - lastRowCount)){ return true; } } return false; } /* * 是否是最后一列 */ private boolean isLastColum(int itemPosition, RecyclerView parent) { RecyclerView.LayoutManager layoutManager = parent.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) { int spanCount = getSpanCount(parent); if ((itemPosition + 1) % spanCount == 0) { return true; } } return false; } private int getSpanCount(RecyclerView parent) { RecyclerView.LayoutManager layoutManager = parent.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) { GridLayoutManager lm = (GridLayoutManager) layoutManager; int spanCount = lm.getSpanCount(); return spanCount; } return 0; } }

通过LayoutManager实现不同效果

效果图如下:
Android RecyclerView使用详解_第2张图片

代码如下:

package com.example.zhangke.recyclerviewdemo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    private RecyclerView mRecyclerView;
    private ArrayList mDatas;
    private RecyclerViewAdapter mAdapter;
    private StaggeredGridLayoutAdapter mStagAdapter;
    private RecyclerView.ItemDecoration mItemDecoration;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mRecyclerView = (RecyclerView) findViewById(R.id.recycleview);
        initData();

        //垂直listview
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        //设置分割线
        mItemDecoration = new DividerItemDecoration(this, DividerItemDecoration.VERTICAL);
        mRecyclerView.addItemDecoration(mItemDecoration);
        // 设置增删动画
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());

        mAdapter = new RecyclerViewAdapter(this, mDatas);

        //设置点击事件
        mAdapter.setOnItemClickListener(new RecyclerViewAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {
                Toast.makeText(MainActivity.this, "onItemClick " + position, Toast.LENGTH_SHORT).show();
            }
        });

        mAdapter.setOnLongItemClickListener(new RecyclerViewAdapter.OnLongItemClickListener() {
            @Override
            public void onLongItemClick(View view, int position) {
                Toast.makeText(MainActivity.this, "onLongItemClick " + position, Toast.LENGTH_SHORT).show();
            }
        });

        //设置Adapter
        mRecyclerView.setAdapter(mAdapter);

    }

    /**
     * 初始化数据
     */
    private void initData() {
        mDatas = new ArrayList();
        for (int i = 0; i < 100; i++) {
            mDatas.add("item " + i);
        }

    }


    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (mItemDecoration != null) {
            mRecyclerView.removeItemDecoration(mItemDecoration);
        }
        switch (item.getItemId()) {
            case R.id.add:

                mAdapter.addItem(1, "item" + 1);
                break;

            case R.id.remove:
                mAdapter.removeItem(1);
                break;

            case R.id.showVertical:
                //垂直listview
                mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
                mItemDecoration = new DividerItemDecoration(this, DividerItemDecoration.VERTICAL);
                mRecyclerView.addItemDecoration(mItemDecoration);
                mAdapter = new RecyclerViewAdapter(this, mDatas);
                mAdapter.setOnItemClickListener(new RecyclerViewAdapter.OnItemClickListener() {
                    @Override
                    public void onItemClick(View view, int position) {
                        Toast.makeText(MainActivity.this, "onItemClick " + position, Toast.LENGTH_SHORT).show();
                    }
                });

                mAdapter.setOnLongItemClickListener(new RecyclerViewAdapter.OnLongItemClickListener() {
                    @Override
                    public void onLongItemClick(View view, int position) {
                        Toast.makeText(MainActivity.this, "onLongItemClick " + position, Toast.LENGTH_SHORT).show();
                    }
                });

                mRecyclerView.setAdapter(mAdapter);
                break;

            case R.id.showHorizontal:
                //水平listview
                mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
                mItemDecoration = new DividerItemDecoration(this, DividerItemDecoration.HORIZONTAL);
                mRecyclerView.addItemDecoration(mItemDecoration);
                mAdapter = new RecyclerViewAdapter(this, mDatas);
                mRecyclerView.setAdapter(mAdapter);
                break;

            case R.id.showGridHorizontal:
                // 水平gridview
                mRecyclerView.setLayoutManager(new GridLayoutManager(this, 4, LinearLayoutManager.HORIZONTAL, false));
                mItemDecoration = new DividerGridItemDecoration(this, DividerGridItemDecoration.HORIZONTAL);
                mRecyclerView.addItemDecoration(mItemDecoration);
                mAdapter = new RecyclerViewAdapter(this, mDatas);
                mRecyclerView.setAdapter(mAdapter);
                break;

            case R.id.showGridVertical:
                // 垂直gridview
                mRecyclerView.setLayoutManager(new GridLayoutManager(this, 4));
                mItemDecoration = new DividerGridItemDecoration(this, DividerGridItemDecoration.VERTICAL);
                mRecyclerView.addItemDecoration(mItemDecoration);
                mAdapter = new RecyclerViewAdapter(this, mDatas);
                mRecyclerView.setAdapter(mAdapter);
                break;

            case R.id.showStaggle:
                // 瀑布流
                mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(3, LinearLayoutManager.VERTICAL));
                mStagAdapter = new StaggeredGridLayoutAdapter(this, mDatas);
                mRecyclerView.setAdapter(mStagAdapter);
                break;
        }
        return true;
    }
}

源代码:
https://github.com/kerwin1321/Study/tree/master/MaterialDesign/RecyclerView

你可能感兴趣的:(Android,MaterialDesign,android)