谨以文章记录学习历程,如有错误还请指明。
RecyclerView 简介
首先,可以理解 RecyclerView 是 ListView 的升级版,更加灵活,同时由于封装了 ListView 的部分实现,导致其使用更简单,结构更清晰。
从名字 RecyclerView 来看,它直接提供了回收复用的功能,虽然 ListView 我们也可以自己实现 ViewHolder 以及 convertView 进行优化,但是在 RecyclerView 中,它直接封装了 ViewHolder 的回收复用,也就是说 RecyclerView 将 ViewHolder 标准化,我们不再需要面向 view ,而是直接面向 ViewHolder 编写实现我们需要的 Adapter,这样一来,逻辑结构就变得非常清晰。
当然,说到 RecyclerView 的优点,就不得不提它的 插拔式 的体验,高度解耦:
- 布局(显示方式):可通过LayoutManager(LinearLayoutManager,GridLayoutManager,StaggeredGridLayoutManager )设置;
- 分割线:通过 ItemDecoration 实现
- Item 增删动画:通过 ItemAnimator
- ViewHolder 的创建和绑定:通过实现 Adapter
除此之外,还需要额外提一下关于点击事件的问题,RecyclerView 本身是不提供点击、长按事件的,而隔壁的 ListView 稳稳支持。对此,可能刚接触 RecyclerView 的同学会疯狂吐槽,怎么作为升级版的 RecyclerView 在这一点上还不如旧版呢?
情况真的是这样么?
显然不是。
ListView 中对于点击事件的处理,其实是有很大弊端的,它的 setOnItemClickListener() 方法是为子项注册点击事件,这就导致只能识别到这一整个子项,对于子项中的组件比如按钮就束手无策了。为此,RecyclerView 直接放弃了这个为子项注册点击事件的监听方法,所有点击事件都有具体 View 去注册,好处显而易见,我可以按需为组件注册点击事件,不存在点击不到的组件。
基础知识补充
如果读者从未接触过 ListView 和 RecyclerView,那么可能对前面提到的 ViewHolder、Adapter等一头雾水,为此在这里补充一些基础知识。
- LayoutManager:RecyclerView 会根据 Layout Manager 提供的视图来填充自身,常用的布局管理器有LinearLayoutManager(线性布局管理器)、GridLayoutManager(网格布局管理器)、StaggeredGridLayoutManager (瀑布流布局管理器)等。
- ViewHolder:列表中的视图由 ViewHolder 实例展示。 ViewHolder 用于对控件的实例进行缓存,负责显示子项。例如,如果列表显示了音乐集合,那么每个 ViewHolder 可能代表一个专辑。
- Adapter:ViewHolder 对象由 Adapter 管理。Adapter 按需创建 ViewHolder,并为其绑定数据。绑定意味着根据 Adapter 中的位置为子视图填充对应的数据。
下面我们就介绍一些关于RecyclerView 的用法。
RecyclerView 用法
基础用法
首先,由于 RecyclerView 属于 v7-支持库,因此我们在使用之前需要先添加依赖:
dependencies {
implementation 'com.android.support:recyclerview-v7:27.1.1'
}
接下来,RecyclerView 的核心使用流程如下:
mRecyclerView = findView(R.id.id_recycler_view);
//设置布局管理器
mRecyclerView.setLayoutManager(mLayoutManager);
//设置adapter
mRecyclerView.setAdapter(mAdapter)
//设置Item增加、移除动画
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
//添加分割线
mRecyclerView.addItemDecoration(new DividerItemDecoration(
getActivity(), DividerItemDecoration.HORIZONTAL_LIST));
可能用惯了 ListView ,第一次接触 RecyclerView 的同学会觉得,ListView 只需要一个 Adapter 就足够了,怎么 RecyclerView 的使用这么繁琐。
其实恰恰正是由于 RecyclerView 的高度解耦,本身只提供基本的回收和复用,而其他特性都允许我们自己定制,因此我们可以通过它来灵活地实现各种 复杂花哨 的效果。可以说,RecyclerView 以增加部分代码的代价来增加其灵活性和扩展性。
下面就来介绍一下 如何通过 RecyclerView 轻松实现一个普通列表:
MainActivity.java
public class MainActivity extends AppCompatActivity {
private RecyclerView mRecyclerView;
private MyAdapter mMyAdapter;
private LinearLayoutManager mLayoutManager;
private List<String> list;
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initData();
mRecyclerView = findViewById(R.id.recycler_view);
mMyAdapter = new MyAdapter(list);
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setAdapter(mMyAdapter);
}
private void initData() {
list = new ArrayList<>();
for (int i = 0; i <= 20; i++) {
list.add("Item " + i);
}
}
}
MyAdapter.java
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
//数据源
private List<String> mList;
public MyAdapter(List<String> list) {
mList = list;
}
//返回item个数
@Override
public int getItemCount() {
return mList.size() ;
}
//创建ViewHolder
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new NormalHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false));
}
//填充视图
@Override
public void onBindViewHolder(@NonNull final MyAdapter.ViewHolder holder, final int position) {
holder.mView.setText(mList.get(position));
}
public class ViewHolder extends RecyclerView.ViewHolder {
public TextView mView;
public ViewHolder(View itemView) {
super(itemView);
mView = itemView.findViewById(R.id.text_view);
}
}
}
MainActivity 的布局文件,activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:divider="#FFFF0000"
android:dividerHeight="10dp"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout >
子项布局,Item.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/text_view"
android:layout_width="match_parent"
android:layout_height="120dp"
android:gravity="center"
android:background="#c0c7c759"
android:textSize="20sp"
android:text="1" />
</FrameLayout>
通过上述代码,就轻轻松松实现了一个列表,效果如下:
可以看到,RecyclerView 的实现结构很清晰,且内部直接封装了 ViewHolder,使得我们不再需要像 ListView 一样自己实现 ViewHolder+convertView 去优化,我们只需要面向 ViewHolder 变成即可,关于复用优化的部分 RecyclerView 已经帮我们做好了。
分割线 - ItemDecoration
虽然上面我们实现了一个 列表界面,但是!!这实在是丑的没法看了,我们随便拉出来一个手机里的app,里面的列表颜值都秒杀它,拿微信界面为例:
很明显,不同模块间有明显的分割空白,而相同模块不同功能也会有一个分割线,整体看起来结构很清晰。
为了给我们的列表添加分割线的效果,我们就需要了解一下 `RecyclerView.addItemDecoration()
方法,该方法接收一个RecyclerView.ItemDecoration 类型参数,其本身是一个抽象类,这就表明我们可以通过继承这个类,编码实现我们所需要的 复杂花哨 的分割线效果。
下面,我们先来了解一下 ItemDecoration 这个类,源码如下:
public abstract static class ItemDecoration {
//使用此方法绘制的任何内容都将在绘制子项视图之前绘制,从而出现在视图之下。
public void onDraw(Canvas c, RecyclerView parent, State state) {
onDraw(c, parent);
}
@Deprecated
public void onDraw(Canvas c, RecyclerView parent) {
}
//使用此方法绘制的任何内容都将在绘制子项视图之后绘制,从而出现在视图之上。
public void onDrawOver(Canvas c, RecyclerView parent, State state) {
onDrawOver(c, parent);
}
@Deprecated
public void onDrawOver(Canvas c, RecyclerView parent) {
}
@Deprecated
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
outRect.set(0, 0, 0, 0);
}
//为每个Item设置一定的偏移量
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
parent);
}
}
可以看到,这个类实际上是很简单的,可供使用的只有三个方法,而其中 onDraw() 方法和 onDrawOver() 方法