这篇文章已同步到 ListView 5种滑动模式解析全在这里了,提供更好的阅读体验
前段时间在使用 ListView 的过程中,需要对一个子 Item 优化横向 Bannar 的滑动体验,于是借此机会,深入了解了一下 ListView 滑动的一些知识,来探究一下,一个 View 滑动,究竟需要做哪些事情。
滑动模式基本介绍
ListView 的滑动模式使用变量 mTouchMode 来表示,分为以下几种模式:
mTouchMode | 注释 | 解析 | 备注 |
---|---|---|---|
TOUCH_MODE_REST | Indicates that we are not in the middle of a touch gesture | 标识当前未处于滑动手势中 | 用于重置当前滑动状态 |
TOUCH_MODE_DOWN | Indicates we just received the touch event and we are waiting to see if the it is a tap or a scroll gesture | 标识仅仅是接到了触摸事件,还需要进一步判断是点按事件还是滑动手势 | |
TOUCH_MODE_TAP | Indicates the touch has been recognized as a tap and we are now waiting to see if the touch is a longpress | 标识触摸事件已经被识别为点按事件,还需要进一步判断是不是长按事件 | |
TOUCH_MODE_DONE_WAITING | Indicates we have waited for everything we can wait for, but the user’s finger is still down | 标识我们已经等了很久,但是用户的手势还是处于down状态 | |
TOUCH_MODE_SCROLL | Indicates the touch gesture is a scroll | 标识触摸手势是滑动事件 | |
TOUCH_MODE_FLING | Indicates the view is in the process of being flung | 标识当前View是在“甩”的过程中 | |
TOUCH_MODE_OVERSCROLL | Indicates the touch gesture is an overscroll - a scroll beyond the beginning or end. | 标识手势是一个越界滑动越界滑动是指滑动超出了内容区域的首尾 | 这种状态下,AbsListView 会在 DOWN 事件时拦掉事件,交给自己的 onTouchEvent 处理,事件不会继续往子 View 传递 |
TOUCH_MODE_OVERFLING | Indicates the view is being flung outside of normal content bounds and will spring back. | 标识当前View被“甩”出了正常滑动区域,即将会弹回来 | 这种状态下,AbsListView 会在 DOWN 事件时拦掉事件,交给自己的 onTouchEvent 处理,事件不会继续往子 View 传递 |
ListView 滑动模式有这么多,其实都是基于控件自身设计和交互上的考虑。我们会发现上表中出现的滑动模式,前四个模式,标识 ListView 当前处于的状态,并没有”滑“起来,而后四个模式,却在实际中有对应的场景:
TOUCH_MODE_SCROLL
:最简单的场景,对应于 ListView 跟随我们的手指上下滑动TOUCH_MODE_FLING
:列表太长时,我们想要快速浏览,手指快速向下(上)滑动,手机离开屏幕伴有一定的加速度TOUCH_MODE_OVERSCROLL
:手机滑动列表已经超出内容的可滑动区域后,还继续滑动,表示 ListView 没有更多的内容了TOUCH_MODE_OVERFLING
:Fling 之后,滑动还没停下来的时候超出内容的滑动区域,继续滑动的状态,原生默认的效果是波纹动画,也是标示 ListView 没有更多可以滑动的内容了FAST_SCROLL
:除了以上的模式之外,还有一个模式 ,对应的场景是,我们拖动滑动标识器(scroll thumb)快速定位到 ListView 的某个位置,FAST_SCROLL 模式的事件处理在整个以上模式的最前边,是独立于这些模式的,我们后边单独分析
以上就是我们这次要讲得 5 种滑动模式
这里有个模式变化表,仅供参考,单次滑动屏幕,(中间不抬手指)
模式变化 | 说明 |
---|---|
TOUCH_MODE_REST -> TOUCH_MODE_DOWN -> TOUCH_MODE_OVERSCROLL | (不可能,无法直接从 DOWN->OVERSCROLL,因为 mScrollY 必须经过 onOverScrolled 之后才能有值) |
TOUCH_MODE_REST -> TOUCH_MODE_DOWN -> 按下,长按,TAP | 普通点击,长按 |
TOUCH_MODE_REST -> TOUCH_MODE_DOWN -> TOUCH_MODE_SCROLL -> TOUCH_MODE_REST | 普通的滑动模式 |
TOUCH_MODE_REST -> TOUCH_MODE_DOWN -> TOUCH_MODE_SCROLL -> TOUCH_MODE_OVERSCROLL-> TOUCH_MODE_OVERFLING -> TOUCH_MODE_FLING -> TOUCH_MODE_REST | 初始化 -> 触摸屏幕 -> 接触滑动 -> 越界 -> 越界FLING -> FLING -> 重置 |
TOUCH_MODE_REST -> TOUCH_MODE_DOWN -> TOUCH_MODE_SCROLL -> TOUCH_MODE_OVERSCROLL -> TOUCH_MODE_REST | 初始化 -> 触摸屏幕 -> 接触滑动 -> 越界 -> 松手 -> 弹回重置 |
TOUCH_MODE_REST -> TOUCH_MODE_DOWN -> TOUCH_MODE_SCROLL -> TOUCH_MODE_OVERSCROLL -> TOUCH_MODE_SCROLL -> TOUCH_MODE_REST | 初始化 -> 触摸屏幕 -> 接触滑动 -> 越界 -> 往回滑动 -> 松手 |
TOUCH_MODE_REST -> TOUCH_MODE_DOWN -> TOUCH_MODE_FLING | (不可能,无法直接从 DOWN->FLING,因为 fling 需要加速度,必须要经过 move) |
TOUCH_MODE_REST -> TOUCH_MODE_DOWN -> TOUCH_MODE_SCROLL -> TOUCH_MODE_FLING -> TOUCH_MODE_OVERFLING -> TOUCH_MODE_REST | 初始化 -> 触摸屏幕 -> 接触滑动 -> 松手FLING -> 越界FLING -> 停止 |
TOUCH_MODE_REST -> TOUCH_MODE_DOWN -> TOUCH_MODE_SCROLL -> TOUCH_MODE_FLING -> TOUCH_MODE_OVERFLING -> TOUCH_MODE_FLING -> TOUCH_MODE_REST | 初始化 -> 触摸屏幕 -> 接触滑动 -> 松手FLING -> 越界FLING -> FLING -> 停止 |
TOUCH_MODE_REST -> TOUCH_MODE_DOWN -> TOUCH_MODE_SCROLL -> TOUCH_MODE_FLING -> TOUCH_MODE_REST | 初始化 -> 触摸屏幕 -> 接触滑动 -> FLING -> 到达边缘(当前 overScrollMode 被禁用) |
从哪里开始
滑动是与用户连接非常密切的交互方式,而感知用户屏幕行为最重要的一个途径就是 MotionEvent 事件,所以要了解 ListView 的滑动模式,我们首先就需要把目光放到 ListView 处理屏幕触摸事件的两个方法
android.widget.AbsListView#onInterceptTouchEvent()
android.widget.AbsListView#onTouchEvent()
ListView 继承自 ViewGroup,所以事件分发的过程会经历以下三个方法(并不是每次事件流都必走,顺序也不是从上到下的)
android.view.ViewGroup#dispatchTouchEvent()
android.widget.AbsListView#onInterceptTouchEvent</