我们常用的列表控件一般有2种,ListView和GridView,现在Android开发者又多了一个选择,那就是RecyclerView,从名字上面可以看出来,这是一个循环复用视图。RecyclerView出现其实已经有一段时间了。现在就来学习一下吧!
整体上看RecyclerView架构,提供了一种插拔式的体验,高度的解耦,异常的灵活,通过设置它提供的不同LayoutManager,ItemDecoration , ItemAnimator实现令人瞠目的效果。
RecyclerView项目结构如下:
layout布局:
子视图布局:
运行效果图:
DividerItemDecoration
调用代码
然后就有和listview的divider分隔线了。这个简单就不上图了。继续看Grib格子,DividerGridItemDecoration.java
调用方法
效果图如下:
如果你觉得到这里就完了就错了,还有更精彩的。我们先把每个元素的边距设置20dp,然后设置一个背景颜色#abcdef,adapter里面的数据调到100个。
运行效果图:
到了这里还是没有感觉到什么强大之处,因为GridView也可以完成这个效果,对吧。接下来看动画。注意,这里更新数据集不是用adapter.notifyDataSetChanged()而是 notifyItemInserted(position)与notifyItemRemoved(position)
我们就随便点击一个item就自动添加一个元素吧。说到这个事件就不得不说recyclerview的个性,人家不支持onItemClickListener,要实现子控件点击事件只能自己去实现。那怎么整呢?我们知道任何view可以可以添加click事件,既然如此就简单了,我们把每一个view都注册click事件不就行了嘛。下面是修改完成之后的源码,事件是基于最外层的Linearlayout视图的。
效果图:
我这边没上GIF动画,不然点击6会自动添加一个视图,点击8会自动减少一个视图。好了,就到这里吧!最后把修改过的资源贴出来
整体上看RecyclerView架构,提供了一种插拔式的体验,高度的解耦,异常的灵活,通过设置它提供的不同LayoutManager,ItemDecoration , ItemAnimator实现令人瞠目的效果。
RecyclerView项目结构如下:

- 你想要控制其显示的方式,请通过布局管理器LayoutManager
- 你想要控制Item间的间隔(可绘制),请通过ItemDecoration
- 你想要控制Item增删的动画,请通过ItemAnimator
- 你想要控制点击、长按事件,请自己写(擦,这点尼玛。)
package com.androiddemo;
import java.util.ArrayList;
import java.util.List;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.Adapter;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.android.dev.base.BaseActivity;
import com.android.dev.util.Tools;
public class RecyclerActivity extends BaseActivity {
RecyclerView recyclerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recycler);
recyclerView = (RecyclerView) findViewById(R.id.id_recyclerview);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(new MainAdapter());
}
class MainAdapter extends Adapter {
private List mDatas;
public MainAdapter() {
super();
// TODO Auto-generated constructor stub
mDatas = new ArrayList();
for (int i = 0; i < 10; i++) {
mDatas.add("索引:" + (int) (Math.random() * 100) / 100f);
Tools.sleep(1);// 休眠
}
}
@Override
public int getItemCount() {
// TODO Auto-generated method stub
return mDatas.size();
}
@Override
public void onBindViewHolder(MainHolder holder, int position) {
// TODO Auto-generated method stub
holder.textView.setText(mDatas.get(position));
}
@Override
public MainHolder onCreateViewHolder(ViewGroup group, int viewType) {
// TODO Auto-generated method stub
View view = getLayoutInflater().inflate(R.layout.recycler_item,
group, false);
MainHolder holder = new MainHolder(view);
return holder;
}
}
class MainHolder extends ViewHolder {
TextView textView;
public MainHolder(View view) {
super(view);
// TODO Auto-generated constructor stub
textView = (TextView) view.findViewById(R.id.id_text);
}
}
}
layout布局:
子视图布局:
运行效果图:

直线显示
recyclerview可以通过addItemDecoration来添加分隔线,我们先从最简单的直线入手。DividerItemDecoration
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.util.Log;
import android.view.View;
/**
* This class is from the v7 samples of the Android SDK. It's not by me!
*
* 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);
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));
然后就有和listview的divider分隔线了。这个简单就不上图了。继续看Grib格子,DividerGridItemDecoration.java
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.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.LayoutManager;
import android.support.v7.widget.RecyclerView.State;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.View;
/**
*
* @author zhy
*
*/
public class DividerGridItemDecoration extends RecyclerView.ItemDecoration {
private static final int[] ATTRS = new int[] { android.R.attr.listDivider };
private Drawable mDivider;
public DividerGridItemDecoration(Context context) {
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
a.recycle();
}
@Override
public void onDraw(Canvas c, RecyclerView parent, State state) {
drawHorizontal(c, parent);
drawVertical(c, parent);
}
private int getSpanCount(RecyclerView parent) {
// 列数
int spanCount = -1;
LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager) {
spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
spanCount = ((StaggeredGridLayoutManager) layoutManager)
.getSpanCount();
}
return spanCount;
}
public void drawHorizontal(Canvas c, RecyclerView parent) {
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.getLeft() - params.leftMargin;
final int right = child.getRight() + params.rightMargin
+ mDivider.getIntrinsicWidth();
final int top = child.getBottom() + params.bottomMargin;
final int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
public void drawVertical(Canvas c, RecyclerView parent) {
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 top = child.getTop() - params.topMargin;
final int bottom = child.getBottom() + params.bottomMargin;
final int left = child.getRight() + params.rightMargin;
final int right = left + mDivider.getIntrinsicWidth();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
private boolean isLastColum(RecyclerView parent, int pos, int spanCount,
int childCount) {
LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager) {
if ((pos + 1) % spanCount == 0)// 如果是最后一列,则不需要绘制右边
{
return true;
}
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
int orientation = ((StaggeredGridLayoutManager) layoutManager)
.getOrientation();
if (orientation == StaggeredGridLayoutManager.VERTICAL) {
if ((pos + 1) % spanCount == 0)// 如果是最后一列,则不需要绘制右边
{
return true;
}
} else {
childCount = childCount - childCount % spanCount;
if (pos >= childCount)// 如果是最后一列,则不需要绘制右边
return true;
}
}
return false;
}
private boolean isLastRaw(RecyclerView parent, int pos, int spanCount,
int childCount) {
LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager) {
childCount = childCount - childCount % spanCount;
if (pos >= childCount)// 如果是最后一行,则不需要绘制底部
return true;
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
int orientation = ((StaggeredGridLayoutManager) layoutManager)
.getOrientation();
// StaggeredGridLayoutManager 且纵向滚动
if (orientation == StaggeredGridLayoutManager.VERTICAL) {
childCount = childCount - childCount % spanCount;
// 如果是最后一行,则不需要绘制底部
if (pos >= childCount)
return true;
} else
// StaggeredGridLayoutManager 且横向滚动
{
// 如果是最后一行,则不需要绘制底部
if ((pos + 1) % spanCount == 0) {
return true;
}
}
}
return false;
}
@Override
public void getItemOffsets(Rect outRect, int itemPosition,
RecyclerView parent) {
int spanCount = getSpanCount(parent);
int childCount = parent.getAdapter().getItemCount();
if (isLastRaw(parent, itemPosition, spanCount, childCount))// 如果是最后一行,则不需要绘制底部
{
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
} else if (isLastColum(parent, itemPosition, spanCount, childCount))// 如果是最后一列,则不需要绘制右边
{
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
} else {
outRect.set(0, 0, mDivider.getIntrinsicWidth(),
mDivider.getIntrinsicHeight());
}
}
}调用方法
recyclerView.setAdapter(new MainAdapter()); recyclerView.setLayoutManager(new GridLayoutManager(this, 4));// 格子排列 recyclerView.addItemDecoration(new DividerGridItemDecoration(this));
效果图如下:

如果你觉得到这里就完了就错了,还有更精彩的。我们先把每个元素的边距设置20dp,然后设置一个背景颜色#abcdef,adapter里面的数据调到100个。

运行效果图:

到了这里还是没有感觉到什么强大之处,因为GridView也可以完成这个效果,对吧。接下来看动画。注意,这里更新数据集不是用adapter.notifyDataSetChanged()而是 notifyItemInserted(position)与notifyItemRemoved(position)
public void addItem(String text, int position) {
mDatas.add(text);
notifyItemInserted(position);
}
public void removeItem(int position) {
mDatas.remove(position);
notifyItemRemoved(position);
}我们就随便点击一个item就自动添加一个元素吧。说到这个事件就不得不说recyclerview的个性,人家不支持onItemClickListener,要实现子控件点击事件只能自己去实现。那怎么整呢?我们知道任何view可以可以添加click事件,既然如此就简单了,我们把每一个view都注册click事件不就行了嘛。下面是修改完成之后的源码,事件是基于最外层的Linearlayout视图的。
import java.util.ArrayList;
import java.util.List;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.Adapter;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.TextView;
import com.android.dev.base.BaseActivity;
import com.android.dev.base.MLog;
import com.android.dev.util.Tools;
import com.androiddemo.other.DividerGridItemDecoration;
public class RecyclerActivity extends BaseActivity implements
OnItemClickListener {
RecyclerView recyclerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recycler);
recyclerView = (RecyclerView) findViewById(R.id.id_recyclerview);
// recyclerView.setLayoutManager(new LinearLayoutManager(this));//线性排列
// recyclerView.addItemDecoration(new
// DividerItemDecoration(this,DividerItemDecoration.VERTICAL_LIST));
final MainAdapter adapter = new MainAdapter();
recyclerView.setAdapter(adapter);
adapter.setItemClickListener(new OnItemClickListener() {
@Override
public void onItemClickListener(int position) {
// TODO Auto-generated method stub
MLog.makeText("点击:" + position);
if (position == 6) {
adapter.addItem("添加6", position);
}
if (position == 8) {
adapter.removeItem(8);
}
}
});
// recyclerView.setLayoutManager(new GridLayoutManager(this, 4));// 格子排列
// recyclerView.addItemDecoration(new DividerGridItemDecoration(this));
recyclerView.setLayoutManager(new StaggeredGridLayoutManager(6,
StaggeredGridLayoutManager.HORIZONTAL));// 瀑布流
recyclerView.addItemDecoration(new DividerGridItemDecoration(this));
recyclerView.setItemAnimator(new DefaultItemAnimator());// 子视图动画
}
class MainAdapter extends Adapter {
private List mDatas;
private OnItemClickListener itemClickListener;
public MainAdapter() {
super();
// TODO Auto-generated constructor stub
mDatas = new ArrayList();
for (int i = 0; i < 100; i++) {
mDatas.add("索引:" + i);
Tools.sleep(1);// 休眠
}
}
public void setItemClickListener(OnItemClickListener itemClickListener) {
this.itemClickListener = itemClickListener;
}
public void addItem(String text, int position) {
mDatas.add(text);
notifyItemInserted(position);
}
public void removeItem(int position) {
mDatas.remove(position);
notifyItemRemoved(position);
}
@Override
public int getItemCount() {
// TODO Auto-generated method stub
return mDatas.size();
}
@Override
public void onBindViewHolder(MainHolder holder, int position) {
// TODO Auto-generated method stub
holder.textView.setText(mDatas.get(position));
holder.parent.setTag(position);
}
@Override
public MainHolder onCreateViewHolder(ViewGroup group, int viewType) {
// TODO Auto-generated method stub
View view = getLayoutInflater().inflate(R.layout.recycler_item,
group, false);
view.setOnClickListener(listener);
MainHolder holder = new MainHolder(view);
return holder;
}
OnClickListener listener = new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
if (itemClickListener != null)
itemClickListener.onItemClickListener(Integer.parseInt(v
.getTag().toString()));
}
};
}
public interface OnItemClickListener {
public void onItemClickListener(int position);
}
class MainHolder extends ViewHolder {
View parent;
TextView textView;
public MainHolder(View view) {
super(view);
// TODO Auto-generated constructor stub
this.parent = view;
textView = (TextView) view.findViewById(R.id.id_text);
}
}
@Override
public void onItemClick(AdapterView> parent, View view, int position,
long id) {
// TODO Auto-generated method stub
}
}
效果图:

我这边没上GIF动画,不然点击6会自动添加一个视图,点击8会自动减少一个视图。好了,就到这里吧!最后把修改过的资源贴出来
收藏的用户(0) X
正在加载信息~
推荐阅读
最新回复 (0)
站点信息
- 文章2313
- 用户1336
- 访客11757802
每日一句
Life is short; Live it!
人生苦短,活出精彩。
人生苦短,活出精彩。
新会员