在群里经常听到有人在讨论到RecyclerView,说的神乎其神,还有人说用了RecyclerView之后都不想用ListView了,是不是有那么神奇呢?做为一个最菜的小菜鸟也要去了解下才好。
关于RecyclerView
RecyclerView 和CardView等都是Android 5.0版本中新添加的控件,RecyclerView 是一个用来取代ListView的SDK,它的灵活性与可替代性比listview更好。很多人都认为,RecyclerView是ListView、GridView的升级版,不仅具有了ListView和GridView的功能,还用很简单的方法实现了瀑布流。但是RecyclerView作为新的控件,与ListView和GridView有着很大的区别,有着自己的特性:
RecyclerView只负责如何回收和复用Item,其他的都交给相应的管理器去负责,如Item的显示交给布局管理器LayoutManager负责处理;将Item之间的间隔交由ItemDecoration负责;动画效果交给ItemAnimator负责处理。
如何使用RecyclerView
1.添加依赖
在AS的build.gradle中添加依赖,然后同步一下就好:
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:recyclerview-v7:23.1.0'
}
2.编写xml文件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<android.support.v7.widget.RecyclerView
android:id="@+id/rv"
android:layout_below="@+id/tv_com"
android:layout_width="match_parent"
android:dividerHeight="4dp"
android:layout_height="match_parent"/>
</RelativeLayout>
顺便写一个简单item布局文件,只是测试而已,非常简单
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rl_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#f1f1f1"
>
<TextView
android:id="@+id/tv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"
android:gravity="center"
android:textColor="#646464"
android:textSize="18sp" />
</RelativeLayout>
3.类似ListView一样,我们同样也需要写一个Adapter,值得注意的是RecyclerView的Adapter不是继承BaseAdapter,而是继承了RecyclerView.Adapter< VH>并且通过一个泛型参数强制要求使用ViewHolder。因此我们的Adapter如下:
public class CommonRecyclerViewAdapter extends RecyclerView.Adapter<CommonRecyclerViewAdapter.MyViewHolder> {
private Context context;
private List<String> list;
public CommonRecyclerViewAdapter(Context context, List<String> list) {
this.context = context;
this.list = list;
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//绑定item布局
return new MyViewHolder(LayoutInflater.from(context).inflate(R.layout.item_rcv,null));
}
//进行view和数据源的绑定
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.tv.setText(list.get(position));
}
//返回数据的总条目数量
@Override
public int getItemCount() {
return list.size();
}
//ViewHolder
public class MyViewHolder extends RecyclerView.ViewHolder{
TextView tv;
RelativeLayout layout;
public MyViewHolder(View itemView) {
super(itemView);
tv = (TextView) itemView.findViewById(R.id.tv);
layout=(RelativeLayout)itemView.findViewById(R.id.rl_item);
}
}
}
简单的适配器就这样完成了,下面只要我们的activity和Adapter关联起来就完成了。
4.在Activity中与Adapter绑定实现ListView功能。
public class CommonRecyclerViewActivity extends Activity implements View.OnClickListener {
private RecyclerView rv;
private List<String> list;
private Activity activity;
private CommonRecyclerViewAdapter adapter;
int position=0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_common_recycler_view);
initData();
rv = (RecyclerView) findViewById(R.id.rv);
//RecyclerView布局管理器设置
/**
* LinearLayoutManager 线性管理器,支持横向、纵向。
GridLayoutManager 网格布局管理器
StaggeredGridLayoutManager 瀑布就式布局管理器
*/
//listview
rv.setLayoutManager(new LinearLayoutManager(this));
//设置Adapter
adapter = new CommonRecyclerViewAdapter(this,list);
rv.setAdapter(adapter);
}
private void initData() {
list = new ArrayList<>();
for (int i = 0; i < 101; i++) {
list.add("RecyclerView Item "+i+" 000000000");
}
}
看,一个简单的listview的效果出来了,请忽视界面的丑陋,现在只是做一个功能性的演示而已。
5.GridView效果
单单从这里也看不出来RecyclerView比ListView有多大的优势,但是如果我们要切换到GridView的样式中就简单多,前面1-3相关代码均不需要做什么改动,只要改变4Activity中的布局管理器代码就好
//其他代码不变,只需改变布局管理器就好
//rv.setLayoutManager(new LinearLayoutManager(this));
//表示4列的GridView
rv.setLayoutManager(new GridLayoutManager(this,4));
下面我们来看下效果
效果是达到了,尼玛的,没有分割线就是丑,下次一定要把分割线搞出来。
上面只是很常规的ListView和GridView效果,我们还能利用RecyclerView做横向滑动的ListView和GridView效果,做起来也简单,还是只改变管理器的布局代码就好。
横向的GridView和ListView
//横向滑动的4列GridView,如果要横向滑动的ListView是不是也很简单呢?是的,只需将横向的列数设置为1就好。
rv.setLayoutManager(new GridLayoutManager(this,4,GridLayoutManager.HORIZONTAL,false));
6.完美的瀑布流
实现的上面的功能我们已经开始有一点点佩服RecyclerView了,但是他还有一个功能就是完美地实现了瀑布流的布局。
我们需要变化的是两个地方,一个是Adapter,通过Adapter控制瀑布流的大小,具体代码如下;
public class StaggeredRecyclerViewAdapter extends RecyclerView.Adapter<StaggeredRecyclerViewAdapter.MyViewHolder> {
private Context context;
private List<String> list;
private int[] num = {1,2,3,2,2,6};
public StaggeredRecyclerViewAdapter(Context context, List<String> list) {
this.context = context;
this.list = list;
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new MyViewHolder(LayoutInflater.from(context).inflate(R.layout.item_rcv, null));
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
ViewGroup.LayoutParams lp = holder.tv.getLayoutParams();
//设置不同大小的布局
lp.height = 600/num[position%num.length];
holder.tv.setLayoutParams(lp);
holder.tv.setText(list.get(position));
//设置不同的颜色便于区分
if (position % 3 == 0)
holder.layout.setBackgroundColor(Color.GREEN);
else if (position % 3 == 1)
holder.layout.setBackgroundColor(Color.RED);
else
holder.layout.setBackgroundColor(Color.BLUE);
}
@Override
public int getItemCount() {
return list.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
TextView tv;
RelativeLayout layout;
public MyViewHolder(View itemView) {
super(itemView);
tv = (TextView) itemView.findViewById(R.id.tv);
layout = (RelativeLayout) itemView.findViewById(R.id.rl_item);
}
}
}
然后在Activity中简单地改变下布局管理器
rv.setAdapter(new StaggeredRecyclerViewAdapter(this,list));
rv.setLayoutManager(new StaggeredGridLayoutManager(4,StaggeredGridLayoutManager.VERTICAL));
看,完美的瀑布流出来了:
到此,最简单的RecyclerView的应用已经差不多了,之前上面说到没有分割线看起来不怎么好看,所以在网上看了下关于分割线的资料。系统给我们提供了一个关于分割线的抽象类(ItemDecoration.java),但是没有给出任何一个实现类。我在网上找到了一个关于RecyclerView分割线的实现类,还不错,网上很多地方都看见有,至于真正的作者是谁就不知道了。附上源码以供学习:
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;
}
//在子view绘制之前绘制,一般来说onDraw和onDrawOver只需复写其中一个就好
@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);
android.support.v7.widget.RecyclerView v = new android.support.v7.widget.RecyclerView(parent.getContext());
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);
}
}
//在子view绘制完成后绘制
public void onDrawOver(Canvas c, RecyclerView parent, State state) {
onDrawOver(c, parent);
}
//可以通过outRect.set()为每个Item设置一定的偏移量,主要用于绘制Decorator
@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);
}
}
}
7.RecyclerView中添加分割线
//只要在设置布局管理器的时候设置即可
rv.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL_LIST));
对于RecyclerView的简单的学习先到这里,如有错误,欢迎提出并指正。