Android5.x新控件介绍(三)

Android 5.x 新控件

作者:李旺成
时间:2016年4月18日


接上篇 Android5.x新控件介绍(二)

这是 Android 5.x 中的新控件介绍的最后一篇,在这一篇中主要介绍 RecyclerView,该系列文章以讲解新特性和简单使用为主。

简介

RecyclerView 是 android-support-v7-21版本中新增的一个 Widget,看看一句话介绍:

  • 能够在有限的窗口中展示大数据集合的灵活视图
  • ListView 的升级版本,更加先进和灵活
  • 通过方便的视图复用轻松实现自定义高效的视图集
  • 它就是一个容器,可以有效的重用和滚动

上面从不同角度指出了 RecyclerView 的一些特点,我觉得可以简单的从其命名上来理解:
Recycler:反复循环器,再循环器。

对,就是循环,这个概念有没有很熟悉的感觉。回想下 ListView、GridView等,这些 AdapterView,它们就是利用视图回收技术来展示大量数据的控件;RecyclerView 与它们有什么区别,或者说其优势在哪里?

先看看官方介绍:

RecyclerView 类
继承自 ViewGroup,这个情理之中意料之外,为什么这么说?

情理之中,是指 RecyclerView 它是个容器,那么肯定会直接或间接继承自 ViewGroup。
意料之外,上面提到的 ListView 都是继承自 AdapterView,而 RecyclerView 又何其有类似的功能 —— 视图复用。但是他竟然没有继承自 AdapterView,从这一点来看,RecyclerView 的改动应该不小,完全不按以前那一套来了。

好了,闲话不多说,看看 RecyclerView 的相关类:

RecyclerView 相关类

这里直接列了出来,不是说它们都很常用,不要紧张,常用到的没这么多。

大致说一下 RecyclerView 的实现思路:

RecyclerView原理

上图只是为了说明一个问题:RecyclerView 只负责 Receiver,其他的功能都交给其他专门的类去处理,比如:Adapter 专门负责数据绑定(PS:话说,ListView 也有Adapter,哈哈,举个例子而已)。有没有一点单一职责原则的感觉。

可以看出 RecyclerView 的设计更利于解耦,更灵活,可操作性更高。

下面来看看怎么用。

简单使用

先看看效果:

RecyclerView 示例

一个简单的 Demo,演示了 RecyclerView 的简单使用,下面看看所涉及到的几个类。

几个关键类

Adapter

这个与 ListView 的 Adapter 功能类似,可以托管数据集合,为每一项Item创建视图并且绑定数据。
先看官方文档:

RecyclerView 的 Adapter 类

继承自 Object,与 AdapterView 所使用的 BaseAdapter 没有多少关系。但是不用急,看看它提供的方法,和 BaseAdapter 提供了不少类似的。

关于具体用法,在下面介绍吧!

ViewHolder

Google 建议 在 ListView 的 Adapter 中使用 ViewHolder,但是没有严格的限制,使用与否(还有用对与否)都没有什么标准,完全看开发者的意愿。

ViewHolder ,主要用来将数据和布局 item 进行绑定。所有对视图的操作都需要通过 ViewHolder 来进行,这是强制性的。

看下官方的介绍:

RecyclerView 的 ViewHolder 类

LayoutManager

LayoutManager,这个是新概念,在 ListView 中从没听说过;其作用就是负责Item 视图的布局的显示管理。

先看下官方介绍:
LayoutManager 类

它有三个实现类,看下它们的继承结构:
LayoutManager的继承结构

  • LinearLayoutManager:水平或者垂直的 Item 视图
  • GridLayoutManager:网格 Item 视图
  • StaggeredGridLayoutManager:交错的网格 Item 视图(瀑布流,明白了吧)

这几个类负责 RecyclerView 中 Item 的排布,具体使用在下面介绍。

ItemDecoration

ItemDecoration 用于设置条目的分割线,在 ListView 中通过 android:divider 属性来为条目设置分割线。RecyclerView 可不管这些了,都交给“专人”(专门的类)负责。这样灵活了,效果也更丰富,也稍微麻烦了(呵呵,哪有两全其美的设计)。

看下官方介绍:

ItemDecoration 类

上面使用红色框圈出来的 onDrawXxx() 方法,可想而知,你想要什么样式/效果,自己画去。

ItemAnimator

还记得给 ListView 的 Item 添加动画吗?全部都得自己弄,现在好了,RecyclerView Item 的添加/删除动画都交给 ItemAnimator。

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、设置 LayoutManager

1
2
3
4
LinearLayoutManager 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
55
public 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的数量
@Override
public int getItemCount() {
return mDataList.size();
}

// 将数据与 View 控件进行绑定
@Override
public void onBindViewHolder(ViewHolder holder, final int position) {
holder.mTextView.setText(mDataList.get(position));
if (mListener != null) {
holder.mTextView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mListener.onItemClick(v, position);
}
});
}
}

// 创建新 View,被 LayoutManager 所调用
@Override
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
63
public 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;
}

@Override
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);
}
}

@Override
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
3
mDIYDecoration = new DIYDecoration(RecyclerViewActivity.this, OrientationHelper.HORIZONTAL);

mContentRV.addItemDecoration(mDIYDecoration);

7、更新数据
ListView 中使用 Adapter 更新数据提供了 notifyDataSetChanged(),而 RecyclerView 的 Adapter 提供了更多,更细粒度的更新数据的方法:

更新数据

基本上都可以见名知义,直接看看用法吧:

1
2
3
4
5
6
7
8
9
mDataList.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 的基本使用上进行扩展,主要有如下几个方面:

  1. 下拉刷新上拉加载更多
  2. 万能Adapter
  3. 多View Type
  4. AddHeader和AddFooter
  5. Drag
  6. 动画
  7. 嵌套 ViewPager/Gridview

这里只是说一下有这么几个扩展的方向,不在这里展开介绍了,计划以后专门出几篇来介绍。

小结

关于 Android 5.x 的新控件的介绍就到这里了,都只是简单的介绍了下使用方法。

对于其他的控件这样介绍过之后使用应该基本没问题了,但是对于 RecyclerView 的使用,这里只是个引子。关于 RecyclerView 的进阶内容打算用几篇(暂定三篇)来专门介绍,这里就不做展开了。

下一篇就是 Android 6.x 的相关介绍了,未完待续…

项目地址

GitHub

Android学习之RecyclerView
使用StaggeredGridLayoutManager实现瀑布流效果
关于RecyclerView的卡顿问题,对谷歌非常失望
RecyclerView使用介绍
RecyclerView技术栈
RecyclerView完全解析,让你从此爱上它(二十八)

坚持原创技术分享,您的支持将鼓励我继续创作!