作者:李旺成
时间:2016年4月18日
这是 Android 5.x 中的新控件介绍的最后一篇,在这一篇中主要介绍 RecyclerView,该系列文章以讲解新特性和简单使用为主。
简介
RecyclerView 是 android-support-v7-21版本中新增的一个 Widget,看看一句话介绍:
- 能够在有限的窗口中展示大数据集合的灵活视图
- ListView 的升级版本,更加先进和灵活
- 通过方便的视图复用轻松实现自定义高效的视图集
- 它就是一个容器,可以有效的重用和滚动
上面从不同角度指出了 RecyclerView 的一些特点,我觉得可以简单的从其命名上来理解:
Recycler:反复循环器,再循环器。
对,就是循环,这个概念有没有很熟悉的感觉。回想下 ListView、GridView等,这些 AdapterView,它们就是利用视图回收技术来展示大量数据的控件;RecyclerView 与它们有什么区别,或者说其优势在哪里?
先看看官方介绍:
继承自 ViewGroup,这个情理之中意料之外,为什么这么说?
情理之中,是指 RecyclerView 它是个容器,那么肯定会直接或间接继承自 ViewGroup。
意料之外,上面提到的 ListView 都是继承自 AdapterView,而 RecyclerView 又何其有类似的功能 —— 视图复用。但是他竟然没有继承自 AdapterView,从这一点来看,RecyclerView 的改动应该不小,完全不按以前那一套来了。
好了,闲话不多说,看看 RecyclerView 的相关类:
这里直接列了出来,不是说它们都很常用,不要紧张,常用到的没这么多。
大致说一下 RecyclerView 的实现思路:
上图只是为了说明一个问题:RecyclerView 只负责 Receiver,其他的功能都交给其他专门的类去处理,比如:Adapter 专门负责数据绑定(PS:话说,ListView 也有Adapter,哈哈,举个例子而已)。有没有一点单一职责原则的感觉。
可以看出 RecyclerView 的设计更利于解耦,更灵活,可操作性更高。
下面来看看怎么用。
简单使用
先看看效果:
一个简单的 Demo,演示了 RecyclerView 的简单使用,下面看看所涉及到的几个类。
几个关键类
Adapter
这个与 ListView 的 Adapter 功能类似,可以托管数据集合,为每一项Item创建视图并且绑定数据。
先看官方文档:
继承自 Object,与 AdapterView 所使用的 BaseAdapter 没有多少关系。但是不用急,看看它提供的方法,和 BaseAdapter 提供了不少类似的。
关于具体用法,在下面介绍吧!
ViewHolder
Google 建议 在 ListView 的 Adapter 中使用 ViewHolder,但是没有严格的限制,使用与否(还有用对与否)都没有什么标准,完全看开发者的意愿。
ViewHolder ,主要用来将数据和布局 item 进行绑定。所有对视图的操作都需要通过 ViewHolder 来进行,这是强制性的。
看下官方的介绍:
LayoutManager
LayoutManager,这个是新概念,在 ListView 中从没听说过;其作用就是负责Item 视图的布局的显示管理。
先看下官方介绍:
它有三个实现类,看下它们的继承结构:
- LinearLayoutManager:水平或者垂直的 Item 视图
- GridLayoutManager:网格 Item 视图
- StaggeredGridLayoutManager:交错的网格 Item 视图(瀑布流,明白了吧)
这几个类负责 RecyclerView 中 Item 的排布,具体使用在下面介绍。
ItemDecoration
ItemDecoration 用于设置条目的分割线,在 ListView 中通过 android:divider 属性来为条目设置分割线。RecyclerView 可不管这些了,都交给“专人”(专门的类)负责。这样灵活了,效果也更丰富,也稍微麻烦了(呵呵,哪有两全其美的设计)。
看下官方介绍:
上面使用红色框圈出来的 onDrawXxx() 方法,可想而知,你想要什么样式/效果,自己画去。
ItemAnimator
还记得给 ListView 的 Item 添加动画吗?全部都得自己弄,现在好了,RecyclerView Item 的添加/删除动画都交给 ItemAnimator。
ItemAnimator 就是负责处理数据添加或者删除时候的动画效果的。
看下官方介绍:
提供了很多方法,这里就不全部贴出来了,幸好 ItemAnimator 提供了默认实现,如果你不打算自定义的话,那用默认的即可。
下面介绍具体使用。
使用示例
1、导入兼容包
RecyclerView 是兼容包中提供的,要使用,首先需要导入兼容包:1
compile 'com.android.support:recyclerview-v7:23.2.0'
注意,不同版本可能稍有差别,例如:条目根布局设置为 much_parent,早期的版本和目前的版本是有区别的。
2、在 Layout 中使用
RecyclerView 在布局中的使用与 ListView 类似,就把它当作 ListView 即可。看代码:1
2
3
4<android.support.v7.widget.RecyclerView
android:id="@+id/rv_content"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
这里只是简单使用,所以没有去尝试使用更多的属性了。
3、设置 LayoutManager1
2
3
4LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setOrientation(mOrientation); // 可以设置LinearLayoutManager.VERTICAL/LinearLayoutManager.HORIZONTAL
// 设置布局管理器
mContentRV.setLayoutManager(layoutManager);
横向布局与纵向布局在示例 Demo 中可以通过开关来切换,自己 Down 下 Demo 去试试吧!
4、设置 ItemAnimator
这里直接用默认的来演示了:1
mContentRV.setItemAnimator(new DefaultItemAnimator());
5、自定义 Adapter
这个与 ListView 的 Adapter 稍有不同,具体看注释吧:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55public class RecyclerAdapter1 extends RecyclerView.Adapter<RecyclerAdapter1.ViewHolder> {
private List<String> mDataList;
private OnRVItemClickListener mListener;
public RecyclerAdapter1(List<String> dataList) {
mDataList = dataList;
}
// 设置 Item 点击监听
public void setOnItemClickListener(OnRVItemClickListener listener) {
mListener = listener;
}
// 自定义的 ViewHolder,持有每个 Item 的所有 View 控件
// 必须继承自 RecyclerView.ViewHolder
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView mTextView;
public ViewHolder(View itemView) {
super(itemView);
mTextView = (TextView) itemView;
}
}
// 获取Item的数量
public int getItemCount() {
return mDataList.size();
}
// 将数据与 View 控件进行绑定
public void onBindViewHolder(ViewHolder holder, final int position) {
holder.mTextView.setText(mDataList.get(position));
if (mListener != null) {
holder.mTextView.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mListener.onItemClick(v, position);
}
});
}
}
// 创建新 View,被 LayoutManager 所调用
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = View.inflate(parent.getContext(),
android.R.layout.simple_list_item_1, null);
ViewHolder holder = new ViewHolder(view);
return holder;
}
}
6、设置分割线
首先,定义出分割线,直接看代码,都有注释:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63public class DIYDecoration extends RecyclerView.ItemDecoration {
// 采用系统内置的风格的分割线
private static final int[] attrs = new int[]{android.R.attr.listDivider};
private Drawable mDivider;
private int orientation;
public DIYDecoration(Context context, int orientation) {
TypedArray typedArray = context.obtainStyledAttributes(attrs);
mDivider = typedArray.getDrawable(0);
typedArray.recycle();
this.orientation = orientation;
}
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
drawHDeraction(c, parent);
drawVDeraction(c, parent);
}
/**
* 绘制水平方向的分割线
*/
private void drawHDeraction(Canvas c, RecyclerView parent) {
int left = parent.getPaddingLeft();
int right = parent.getWidth() - parent.getPaddingRight();
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
int top = child.getBottom() + layoutParams.bottomMargin;
int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
/**
* 绘制垂直方向的分割线
*/
private void drawVDeraction(Canvas c, RecyclerView parent) {
int top = parent.getPaddingTop();
int bottom = parent.getHeight() - parent.getPaddingBottom();
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
int left = child.getRight() + layoutParams.rightMargin;
int right = left + mDivider.getIntrinsicWidth();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
if (OrientationHelper.HORIZONTAL == orientation) {
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
} else {
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
}
}
}
(参考自:RecyclerView完全解析,让你从此爱上它(二十八))
然后,为 RecyclerView 设置分割线:1
2
3mDIYDecoration = new DIYDecoration(RecyclerViewActivity.this, OrientationHelper.HORIZONTAL);
mContentRV.addItemDecoration(mDIYDecoration);
7、更新数据
ListView 中使用 Adapter 更新数据提供了 notifyDataSetChanged(),而 RecyclerView 的 Adapter 提供了更多,更细粒度的更新数据的方法:
基本上都可以见名知义,直接看看用法吧:1
2
3
4
5
6
7
8
9mDataList.add(0, "DIY-ITEM:NEW");
if (mAdapter1 != null) {
mAdapter1.notifyItemInserted(0);
}
mDataList.remove(0);
if (mAdapter1 != null) {
mAdapter1.notifyItemRemoved(0);
}
在 RecyclerView 中要做局部刷新那就很简单了,RecyclerView 还提供了一些很实用的方法(LayoutManager 中提供的):
- findFirstVisibleItemPosition() :返回当前第一个可见Item的position
- findFirstCompletelyVisibleItemPosition() :返回当前第一个完全可见Item的position
- findLastVisibleItemPosition() :返回当前最后一个可见Item的position
- findLastCompletelyVisibleItemPosition() :返回当前最后一个完全可见Item的position
这些东西就不再这里详述了,方法名已经给出了它的作用,有兴趣可以自己去试试。
进阶
所谓进阶,就是在 RecyclerView 的基本使用上进行扩展,主要有如下几个方面:
- 下拉刷新上拉加载更多
- 万能Adapter
- 多View Type
- AddHeader和AddFooter
- Drag
- 动画
- 嵌套 ViewPager/Gridview
这里只是说一下有这么几个扩展的方向,不在这里展开介绍了,计划以后专门出几篇来介绍。
小结
关于 Android 5.x 的新控件的介绍就到这里了,都只是简单的介绍了下使用方法。
对于其他的控件这样介绍过之后使用应该基本没问题了,但是对于 RecyclerView 的使用,这里只是个引子。关于 RecyclerView 的进阶内容打算用几篇(暂定三篇)来专门介绍,这里就不做展开了。
下一篇就是 Android 6.x 的相关介绍了,未完待续…
项目地址
Android学习之RecyclerView
使用StaggeredGridLayoutManager实现瀑布流效果
关于RecyclerView的卡顿问题,对谷歌非常失望
RecyclerView使用介绍
RecyclerView技术栈
RecyclerView完全解析,让你从此爱上它(二十八)