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:
效果如下:
实现代码如下:
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。继承该类需要复写三个方法:
- public int getItemCount():获得item数目
- public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType):创建一个ViewHolder
- 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抽象方法介绍:
- public void onDraw(Canvas c, RecyclerView parent, State state):绘制分割线
- public void onDrawOver(Canvas c, RecyclerView parent, State state):绘制分割线,该方法后于onDraw,一般实现一个即可
- 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
更多相关文章
- 一些摘抄
- android中JSON数据的读写方法
- Android(安卓)之窗口小部件高级篇--App Widget 之 RemoteViews
- 在EditText中插入表情图片 (CharacterStyle&SpannableString)
- Android(安卓)触摸事件大全
- Android中使用google Analytics
- SQLite数据库(2):ANDROID工程中的使用
- android基础知识---重写软键盘回车的点击事件
- Android(安卓)中的TabHost控件的使用