对于RecyclerView
的学习,主要是须要掌握如下几点:html
Adapter
LayoutManager
ItemAnimator
ItemDecorator
ItemTouchHelper
要理解整个RecyclerView
的思想,有一个视频是必定要看的:RecyclerView ins and outs - Google I/O 2016。今天,咱们就经过这个视频,把上面所学到的东西串联起来。git
RecyclerView
RecyclerView
诞生的目的就是为了替代ListView
,咱们先总结一下在使用ListView
过程中所遇到的问题:程序员
Item
须要编写不少的代码 在使用ListView
的时候,有经验的程序员必定会告诉你在getView
中要这么写,若是忘了,那么会产生很严重的性能问题。if (convertView == null) {
//经过LayoutInflator生成convertView,并产生一个ViewHolder,经过setTag关联起来.
} else {
//经过getTag获取ViewHolder,进行更新操做.
}
复制代码
Item
有焦点时,Item
的子控件就没法获取到焦点;而若是子控件抢夺了焦点,那么Item
的点击事件又不能响应,这个相信你们都遇到过。API
ListView
中提供了不少的API
,可是这些API
又和View
的一些API
重复了,例如咱们能够给ListView
设置setOnItemClickListener
,也能够在getView
中给某个View
设置setOnClickListener
,这就让人很疑惑,到底应当选用哪一个。ListView
中进行添加、删除、移动等操做的时候,若是但愿加上动画,那么是很困难的,根本缘由是咱们是经过Adapter
通知ListView
进行更新,然而ListView
根本就无法肯定究竟是哪些View
发生了变化。ListView
在布局是规整的列表的时候能知足大多数人的使用,然而若是想要实现像瀑布流这种复杂的布局,而且保证View
可以复用,那么须要编写不少的代码。若是以前有了解过RecyclerView
的基本用法,那么你会发现,对于上述这些问题,它都给出了本身的解决方案:github
ViewHolder
,提供了onCreateViewHolder
和onBindViewHolder
这两个方法,把建立View
和绑定View
的操做分离开。onItemClickListener
,以及一些重复的API
。Adapter
中增长了notifyItemChanged()
等方法,让咱们能够指定变化的类型和范围,而且提供了setItemAnimator()
方法,让开发者可以方便地定义添加、删除、移动的动画。LayoutManager
当中,并预制了瀑布流布局。了解了这些,咱们就能知道RecyclerView
能帮咱们解决什么问题,也就能更好地理解它为何要这么设计,下面就开始进入真正的RecyclerView
的学习。缓存
RecyclerView
架构RecyclerView
体系包含三大组件:
LayoutManager
:position the view
ItemAnimator
:animate the view
Adapter
:provide the view
这三大组件各司其职,而RecyclerView
负责管理,就组成了整个RecyclerView
的架构。bash
LayoutManager
LayoutManager
须要负责如下几部分的工做:架构
Position
它负责View
的摆放,能够是线性、宫格、瀑布流式或者任意类型,而RecyclerView
不知道也不关心这些,这是LayoutManager
的职责。Scroll
对于滚动事件的处理,RecyclerView
负责接收事件,可是最终仍是由LayoutManager
进行处理滚动后的逻辑,由于只有它在知道View
具体摆放的位置。Focus traversal
当焦点转移致使须要一个新的Item
出如今可视区域中时,也是由LayoutManager
处理的。Adapter
Adapter
须要负责如下几部分的工做:app
View
和ViewHolder
,后者做为整个复用机制的跟踪单元。Item
和ViewHolder
进行绑定,并存储相关的信息。RecyclerView
数据变化,支持局部的更新,在提升效率的同时也有效地支持了动画。Item
点击事件的处理。ViewHolder
的生命周期LayoutManager
请求RecyclerView
提供指定position
的View
ViewHolder
是和View
相绑定的,同时它也是整个复用框架的跟踪单元。在RecyclerView
体系中,对ViewHolder
采用了二级缓存,分为Cache
和Recycled Pool
,当LayoutManager
向RecyclerView
请求位于某个Position
的View
时,Recycled View
会先去Cache
中寻找,若是找到,那么直接返回;若是找不到,那么再去Recycled Pool
中寻找,下面就是整个寻找过程的几种状况:框架
Cache
这种状况下,不会调用Adapter
的onCreateViewHolder
或者onBindViewHolder
方法:
Cache
不存在,Recycled Pool
也不存在 这种状况下,会调用Adapter
的onCreateViewHolder
方法,让它提供一个对应viewType
的ViewHolder
,咱们在其中创建ViewHolder
和View
之间的关联。
Cache
不存在,Recycled Pool
存在 这种状况下,会回调Adapter
的onBindViewHolder
方法,咱们在其中使用当前的数据集合来更新ViewHolder
所绑定的itemView
的状态。
LayoutManager
找到对应位置的View
LayoutManager
经过addView
方法把以前找到的View
添加进RecyclerView
,RecyclerView
经过onViewAttachToWindow(VH viewHolder)
方法,通知Adapter
这个viewHolder
所关联的itemView
已经被添加到了布局当中, ide
LayoutManager
请求RecyclerView
移除某一个位置的View
当LayoutManager
发现再也不须要某一个position
的View
时,它会通知RecyclerView
,RecyclerView
经过onViewDetachFromWindow(VH viewHolder)
通知Adapter
和它绑定的itemView
被移出了。同时,RecyclerView
判断它是否可以被缓存,假设可以被缓存,那么它会先被放到Cache
当中,在Cache
中又会判断它内部是否有须要转移到Recycled Pool
中的ViewHolder
,在放入以后回收池后,经过onViewRecycled(VH viewHolder)
方法通知Adapter
它被回收了。
在上面的普通的状况中,onViewDetachFromWindow(VH viewHolder)
是当即被回调的。然而在实际当中,因为咱们须要对View
的添加、删除作一些过分动画,这时候,咱们须要等待ItemAnimator
进行完动画操做以后,才作detach
和recycle
的逻辑,这一过程对于LayoutManager
是不可见的。
ViewHolder
的销毁在通常状况下,咱们不会去销毁ViewHolder
,而是把它放入到缓存当中,除非出现如下两种状况。
ViewHolder
所绑定的itemView
当前状态异常在放入Recycled Pool
时,会去检查itemView
的状态是否正常。这一操做的目的主要是为了不出现诸如此类的状况:当前itemView
正在执行动画,此时它可能呈现半透明的状态,若是此时把它放入到回收池中,那么当另外一个位置的position
须要复用它时就可能会出现问题。 当出现上面的状况后,Recycled Pool
会先经过Adapter
的onFailedToRecycled(VH viewHolder)
告诉它咱们如今出现了异常的状况,由Adapter
的实现者经过返回值来决定是否仍然要把它放入到Recycled Pool
,默认是返回false
,也就是不放入,那么这个ViewHolder
就会被销毁了。
Recycled Pool
中已经没有足够的空间Recycled Pool
的空间并非无限大的,所以,若是没有足够的空间存放要被回收的ViewHolder
,那么它也会被销毁。
notifyItemRangeChanged(0, getItemCount())
方法,这时候为了进行渐出渐进的动画,那么咱们就须要建立两倍的
ViewHolder
,出现这种状况时通常有两种解决方法:
Item
pool.setMaxRecycledViews(type, count)
改变回收池的大小。ItemAnimator
对于Item
的动画,主要有如下几种状况:
Fade In
Fade Out
Translate
Cross Fade
RecyclerView
对于动画的处理采用了Predictive
的方式,除了当前已经在RecyclerView
布局中的View
(实线框部分),它还须要知道在屏幕意外的信息(虚线框部分),这样在H
被删除的时候,它才可以对J-K
进行上移动画,并把原来不在屏幕内的L
上移到可视范围以内。
ChildHelper
和AdapterHelper
ChildHelper
对于ChildHelper
的做用是:Provide a virtual children list to layoutmanager
,下面咱们就首先看一下为何须要它。
咱们看下面这种状况,假如LayoutManager
想要移除一个View
,而ItemAnimator
又但愿给这一移除的操做增长一个动画,那么这时候就会产生冲突,到底应该怎么办,为此,RecyclerView
经过ChildHelper
来把它们隔离开。
当RecyclerView
收到LayoutManager
要求改变布局的请求时,它并非直接去更改ViewGroup
,而是让ChildHelper
和ItemAnimator
去协调,并由它来操做ViewGroup
。
0,1,2,3
,此时咱们移除了
position=0
的
Item
,这时候假如删除的动画尚未完成,那么
LayoutManager
和
RecyclerView
的
getChildAt(0)
返回值将会不一样,由于在
LayoutManager
并不清楚
ChildHelper
的存在,在它看来,
position=0
的
Item
已经被移除了。
layoutManager.getChildAt(0); //return 1;
recyclerView.getChildAt(0); //return 0;
复制代码
AdapterHelper
而AdapterHelper
所解决的问题和ChildHelper
相似,ChildHelper
是处理View
的,而AdapterHelper
用来跟踪ViewHolder
的,其做用为:
Tracks ViewHolder positions
Virtual Adapter for LayoutManager
提及来可能比较抽象,咱们用下面这种图理解一下,当咱们移动某个Item
而且它的onLayout
方法尚未完成,那么Adapter
和Layout
的postion
是不相同的:
ItemDecoration
ItemDecoration
用来在RecyclerView
的Canvas
上进行额外的绘制操做,咱们不只能够在单个Item
(例如给每一个Item
添加分割线)的Canvas
上进行绘制,也能够在整个RecyclerView
的Canvas
上进行绘制,此外,咱们还能够指定Item
之间的间隔:
Custom Drawing on RecyclerViews Canvas
Add offset to View bounds
Have multiple ItemDecoration
须要注意的点:
Do not try to access to adapter
Keep necessary information in viewHolder
General onDraw rules apply
recyclerView.getChildViewHolder(View view)
参考文章:Android RecyclerView 使用彻底解析 体验艺术般的控件。
RecycledViewPool
RecyclerViewPool
用来缓存那些回收的View
,这些缓存不只能够提供给单个RecyclerView
使用,还能够提供和别的自定义控件共享。
Sanctuary for reserve ViewHolders
Can be shared between RecyclerViews or Custom ViewGroups
PerActivity Context
ItemTouchHelper
以前使用ListView
的时候,若是须要支持侧滑删除、拖动排序这种操做,那么咱们通常用引入一些开源库,如今RecyclerView
已经帮咱们提供了实现的接口,经过重写ItemTouchHelper
的方法,就能够实现上面提到的那些操做。
Drag & Drop
Swipe to dismiss
参考文章:RecyclerView 进阶:使用 ItemTouchHelper 实现拖拽和侧滑删除
Tips
onBind Position != final
,use holder.getAdapterPostion()
若是咱们像下面这样,在onBindViewHolder
中绑定了监听:public void onBindViewHolder(final ViewHolder, final int position) {
holder.itemView.setOnClickListener(new View.onClickListener) {
@Override
public void onClick(View view) {
removeAtPostion(position);
}
}
}
复制代码
因为Item
会被添加、删除、移动,所以,咱们在onBindViewHolder
中得到位置,并不必定是当前的位置,例如像下面这样:
onBindViewHolder(holder, 5);
notifyItemMoved(5, 15);
holder.itemView.callOnClick();
复制代码
那么就会获得错误的位置,这时候应当使用holder.getAdapterPostion()
来保证可以获得预期的结果。
Payloads
经过onBindViewHolder
中的List payloads
,咱们能够指定在bind
的时候只更新某一部分的信息,而不是所有更新。onCreate means create
在onCreateViewHolder
中,始终应当返回一个新的ViewHolder
,而不是返回一个缓存的ViewHolder
。Adapter position and Layout position
就像咱们前面在AdapterHelper
中讨论的那样,在某些时刻,Adapter Position
和Layout Position
并不相等,咱们应当根据状况选择须要使用哪一个,Adapter Position
数据所处的位置,而Layout Position
则对应当前**View
的所处的位置**。