RecyclerView
核心要点
RecyclerView
是什么?
A flexible view for providing
a limited window into
a large data set
ListView
的局限
- 只有纵向列表一种布局
- 没有支持动画的 API
- 接口设计和系统不一致
setOnItemClickListener()
setOnItemLongClickListener()
setSelection()
- 没有强制实现
ViewHodler
- 性能不如
RecyclerView
RecyclerView
的优势
- 默认支持
Linear
,Grid
,Staggered Grid
三种布局 - 友好的
ItemAnimator
动画API - 强制实现
ViewHolder
- 解耦的架构设计
- 相比
ListView
更好的性能
LayoutManager
支持的布局
RecyclerView
的重要组件
RecyclerView Demo
ViewHolder究竟是什么?
View holder 和 item view 是什么关系?一对一?一对多?多对多?
View holder 解决的是什么问题?
View hodler 的 ListView item view 的复用有什么关系
没有实现view hodler
的getView()
1 | public class SimpleListViewAdapter extends BaseAdapter{ |
实现了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