前言
Linus Benedict Torvalds : RTFSC – Read The Funning Source Code
概述
A flexible view for providing a limited window into a large data set.
前面一章我们已经理解了RecyclerView的特性和简单的使用。总体来说可以上手写代码了。这章开始我们将会更加深入的学习RecyclerView。
ItemTouchHelper
This is a utility class to add swipe to dismiss and drag & drop support to RecyclerView. 翻译:这是一个工具类来给予 RecyclerView 添加拖动排序与滑动删除的支持。
简单使用
首先得要创建一个 ItemTouchHelper.Callback 的子类。这个接口主要是用于监听 “move” 和 “swipe” 事件。还有控制view被选中的状态以及重写默认动画的功能。
|
|
isLongPressDragEnabled(): Returns whether ItemTouchHelper should start a drag and drop operation if an item is long pressed.
isItemViewSwipeEnabled(): Returns whether ItemTouchHelper should start a swipe operation if a pointer is swiped over the View.
getMovementFlags(…): Should return a composite flag which defines the enabled move directions in each state(idle, swiping, dragging). 这函数可以指定支持的拖动和滑动的方向。在设定完支持的方向后通过makeMovementFlags(…)函数来构造返回的flag。
onMove(…): Called when ItemTouchHelper wants to move the dragged item from its old position to the new position.当拖动项目从一个旧的地方到新的地方时被调用。
onSwiped(…): Called when a ViewHolder is swiped by the user.当项目被滑动时调用。
onMove()和onSwiped()是用于通知底层数据的更新。用法主要是通过接口传给外部去调用。
在上面class TagTouchHelperCallback
类中传入我们的接口:
通过继承接口来实现我们的方法功能:
通过调用notifyItemRemoved()
和notifyItemMoved()
函数 告知Adapter发生了改变。
最后再将创建好的Helper绑定给我们的RecyclerView:
设定拖动事件
有时候我们需要点击某个固定的控件才开始拖动而不是马上就拖动,这样我们需要学习设定一个拖动的事件。
首先我们需要一个监听类:
这里最关键的是参数viewHolder
,它要传递接下来移动的控件到底是哪一个。
然后我们在Adapter
的拖动控件里增加监听按键回调并且在你想要的时机调用onStartDrag
事件:
最后就是调用ItemTouchHelper
通知拖动开始:
拖动动画
我们有时候需要在拖动动画或者停止动画上做些自定义,这时候主要需要的就是得知事件监听了。
在继承ItemTouchHelper.Callback
后我们重载两个函数:
onSelectedChanged:当item选中事件有更改的时候会发送通知。
clearView:当拖动或者滑动动画结束时进行通知。
通过这两个函数大家可以自定义拖动或者滑动的动画效果了。
LayoutManager原理
LayoutManager主要作用是,测量和摆放RecyclerView中itemView,以及当itemView对用户不可见时循环复用处理。 通过设置Layout Manager的属性,可以实现水平滚动、垂直滚动、方块表格等列表形式。其内部类Properties包含了所需要的大部分属性。
我们知道RecyclerView
的本质就是是一个ViewGroup
,既然是一个View
当然就包含了onMeasure()
、onLayout()
、onDraw()
这三个方法。在RecyclerView
中,onMeasure()
和onLayout()
的工作统一交给了LayoutManager
来完成,来达到更改LayoutManager
就能够实现多种不同的效果。
onMeasure
在RecyclerView
中onMeasure()的调用可以看到:
其中mLayout
就是我们的LayoutManager
。
但是在LayoutManager里面却又看到的是调回RecyclerView的defaultOnMeasure(...)
函数:
里面的注释真是呵呵了。chooseSize()
方法是直接根据测量值和模式返回了最适大小。
接下来就是调用各个子控件的onMeasure:setMeasuredDimension()
:This method must be called by onMeasure(int, int) to store the measured width and measured height. Failing to do so will trigger an exception at measurement time.
onLayout
在RecyclerView
中onLayout()的调用可以看到:
在onLayout
函数里我们看到跟着调用链一直下去最后还是到了dispatchLayoutStep2()
这个关键函数,里面最值得注意的就是onLayoutChildren(...)
这个函数。同样它是在我们定义的LayoutManager里面被定义的。
这里我们拿LinearLayoutManager.onLayoutChildren
为例:
里面函数很长,最核心的算法在前面注释里已经解释了,这里不做阐述。核心的函数fill(…):
fill(...)
:作用就是根据当前状态决定是应该从缓存池中取itemview
填充 还是应该回收当前的itemview
。layoutChunk()
:负责从缓存池 recycler 中取 itemview,并调用View.addView()
将获取到的 ItemView 添加到 RecyclerView 中去,并调用 itemview 自身的 layout 方法去布局 item 位置。
onDraw
|
|
在这里除了调用系统的onDraw外还调用了mItemDecorations.get(i).onDraw()的draw,它是用来画分割线的。
缓存机制
RecyclerView 自带了一套很完整的缓存机制,用于复用item的便利。
我们首先要关注的是一个叫Recycler
的内部类:
这里用了三级缓存来创建了一个大型的缓存器。
第一级缓存
当仍然依赖于RecyclerView的item(例如滑出视图但没被移除)在被标记移除的时候会被添加到mAttachedScrap
中,在不再依赖是就会添加进mCachedViews
中。
第二级缓存
ViewCacheExtension
:当RecyclerView从第一级缓存中找不到时就会进入第二级缓存找,但这个ViewCacheExtension
是个抽象类,主要方法需要开发者自己创建。
第三级缓存
mRecyclerPool
:RecyclerView最核心的缓存池。RecyclerView 缓存的是 ViewHolder。而 ViewHolder 里面包含了一个 View 这也就是为什么在写 Adapter 的时候 必须继承一个固定的 ViewHolder 的原因。
|
|
作为一个比较值得看的缓存机制,直接贴代码,当作范例来读吧,不做过多解释。
总结
通过粗浅的分析了源码,这里学习的是怎么去思考一个view布局,还学到了比较优秀的缓存机制。 RecyclerView 固然比较优秀,但我们也可以模仿着为未来我们可能会写到自己的view做基础。