RecyclerView
核心要点
RecyclerView
是什么?
a flexible view for providing
a limited window into
a large data set
ListView
的局限
- 只有纵向列表一种布局
- 没有支持动画的 API
- 接口设计和系统不一致
setOnItemClickListener()
setOnItemLongClickListener()
setSelection()
- 没有强制实现
ViewHolder
- 性能不如
RecyclerView
RecyclerView
的优势
- 默认支持
Linear
,Grid
,Staggered Grid
三种布局 - 友好的
ItemAnimator
动画API - 强制实现
ViewHolder
- 解耦的架构设计
- 相比
ListView
更好的性能
LayoutManager
支持的布局
RecyclerView
的重要组件
RecyclerView Demo
ViewHolder
究竟是什么?
- View holder 和 item view 是什么关系?一对一?一对多?多对多?
- View holder 解决的是什么问题?
- View holder 的 ListView item view 的复用有什么关系
没有实现view hodler
的getView()
1 | public class SimpleListViewAdapter extends BaseAdapter{ |
没有复用item view
实现了view holder
的getView()
1 | public class SimpleListViewAdapter extends BaseAdapter{ |
Item view
和view holder
一一对应
View holder
最佳实践
1 | static class UserViewHolder extends RecyclerView.ViewHolder{ |
RecyclerView
缓存机制
ListView
缓存图示一
ListView
缓存图示二
RecyclerView
缓存图示一
RecyclerView
缓存图示二
ViewCacheExtension Example
- 广告卡片
- 每一页一共有4个广告
- 这些广告短期内不会发生变化
- 每次滑入一个广告卡片,一般情况下都需要重新绑定
Cache
只关心position
,不关心view type
RecycledViewPool
只关心view type
,都需要重新绑定- 在
ViewCacheExtension
里保持4个广告Card缓存
注意:列表中 item/广告的 impression
统计
ListView
通过getView()
统计RecyclerView
通过onBindViewHolder()
统计?可能错误!- 通过
onViewAttachedToWindow()
统计
RecyclerView
性能优化策略
在onBindViewHolder
里设置点击监听?
onBindViewHodler
里设置点击监听器会导致重复创建对象
1 | public class SimpleAdapter extends RecyclerView.Adapter{ |
改成在onCreateViewHolder
里设置点击监听!
View
-ViewHolder
-View.OnClickListener
三者一一对应
1 | public class SimpleAdapter extends RecyclerView.Adapter{ |
使用LinearLayoutManager.setInitialPrefetchItemCount()
- 用户滑动到横向滑动的
item RecyclerView
的时候,由于需要创建更复杂的RecyclerView
以及多个子view
,可能会导致页面卡顿 - 由于
RenderThread
的存在,RecyclerView
会进行prefetch
LinearLayoutManager.setInitialPrefetchItemCount
(横向列表初次显示时可见的item个数)- 只有
LinearLayoutManager
有这个API - 只有嵌套在内部的
RecyclerView
才会生效
- 只有
RecyclerView.setHasFixedSize()
1 | //伪代码 |
如果
Adapter
的数据变化不会导致RecyclerView
的大小变化 –》RecyclerView.setHasFixedSize(true)
多个 RecyclerView
共用 RecycledViewPool
共用 RecycledViewPool
代码
1 | RecyclerView.RecycledViewPool recycledViewPool = new RecyclerView.RecycledViewPool(); |
DiffUtil
- DiffUtil is a utility class that can calculate the difference between two lists and output a list of update operations that converts the first list into the second one.
- 局部更新方法
notifyItemXXX()
不适用于所有情况 notifyDataSetChange()
会导致整个布局重绘,重新绑定所有ViewHolder
,而且会失去可能的动画效果DiffUtil
适用于整个页面需要刷新,但是有部分数据可能相同的情况
1 | public abstract static class Callback{ |
DiffUtil.Callback
逻辑
1 | public class UserDiffCallback extends DiffUtil.Callback{ |
1 | public class UserDiffCallback extends DiffUtil.Callback{ |
1 | public class ShowcaseRVAdapter extends RecyclerView.Adapter<ShowcaseRvAdapter.UserViewHolder>{ |
1 | public class ShowcaseRVAdapter extends RecyclerView.Adapter<ShowcaseRvAdapter.UserViewHolder>{ |
在列表很大的时候异步计算diff
- 使用
Thread/Handler
将DiffResult
发送到主线程 - 使用
RxJava
将calculateDiff
操作放到后台线程 - 使用 Google 提供的
AsyncListDiffer(Executor)/ListAdapter
AsyncListDiffer / ListAdapter
代码示例 - https://developer.android.com/reference/androidx/recyclerview/widget/AsyncListDiffer
- https://developer.android.com/reference/androidx/recyclerview/widget/ListAdapter
为什么ItemDecoration
可以绘制分割线?
Overlay Divider 代码
1 | public class OverlayDivider extends RecyclerView.ItemDecoration{ |
ItemDecoration 还可以做什么?
- Drawing dividers between items
- Highlights
- Visual grouping boundaries