简介

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:

效果如下:

实现代码如下:

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实现不同效果

效果图如下:

代码如下:

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

更多相关文章

  1. 一些摘抄
  2. android中JSON数据的读写方法
  3. Android(安卓)之窗口小部件高级篇--App Widget 之 RemoteViews
  4. 在EditText中插入表情图片 (CharacterStyle&SpannableString)
  5. Android(安卓)触摸事件大全
  6. Android中使用google Analytics
  7. SQLite数据库(2):ANDROID工程中的使用
  8. android基础知识---重写软键盘回车的点击事件
  9. Android(安卓)中的TabHost控件的使用

随机推荐

  1. 7、从头学Android之TextView控件
  2. 解析华清远见自主研发Cortex-A9开源Andro
  3. Android之数据统计TalkingData集成
  4. Android中的AppWidget
  5. Android(安卓)技能树
  6. Android尺寸单位
  7. Android初始化语言 (init.*.rc、init.con
  8. android 获取系统分辨率
  9. 传智播客的安卓基础视频-20151228-Androi
  10. Android连接上google