主要介绍内容:
- View的基础知识
- View的位置参数
- MotionEvent 和 TouchSlope
- VelocityTracker、GestureDetector 和 Scroller
- View的滑动
- 使用scrollTo/scrollBy
- 使用动画
- 改变布局参数
- 各种滑动方式的对比
- 弹性滑动
- 使用Scroller
- 通过动画
- 使用延时策略
- View的事件分发机制
- 点击事件的传递规则
- 事件分发的源码解析
- View的滑动冲突
- 常见的滑动冲突场景
- 滑动冲突的处理规则
- 滑动冲突的解决方式
View 的基础知识
View的位置参数
View的位置主要由它的四个顶点来决定,分别对应与View的四个属性:top、left、right、bottom,其中top是左上角纵坐标,left是左上角横坐标,right是右下角横坐标,而bottom是右下角纵坐标。需要注意的是,这些坐标都是相对于View的父容器来说的,因此它是一种相对坐标,View的坐标和父容器的关系如下图所示。在Android中,X轴和Y轴的正方向分别为右和下,这点不难理解,不仅仅是Android,大部分显示系统都是按照这个标准来定义坐标系的。
根据上面给出的图,我们很容易得出一个结论:
width = right - left
height = bottom - top
那么如何得到View的这四个参数呢?也非常简单,在View的源码中它们对应于mLeft、mRight、mTop 和 mBottom 这四个成员变量,具体获取方式如下所示:
left = getLeft()
right = getRight()
top = getTop()
bottom = getBottom
从Android 3.0开始,View增加了额外的几个参数:x、y、translationX 和 translationY,其中x和y是View左上角的坐标,而translationX和translationY是View左上角相对于父容器的偏移量。这几个参数也是相对于父容器的坐标,并且translationX和translationY的默认值是0,和View的四个基本的位置参数一样,View也为它们提供了get/set方法,这几个参数的换算关系如下所示。
x = left + translationX
y = top + translationY
需要注意的是,View在平移过程中,top和left表示的是原始左上角的位置信息,其值并不会发生改变,此时发生改变的是x、y、translationX 和 translationY这四个参数。
MotionEvent 和 TouchSlope
1、MotionEvent
在手指接触屏幕后所产生的一系列事件中,典型的事件类型有如下几种:
- ACTION_DOWN —— 手指刚接触屏幕
- ACTION_MOVE —— 手指在屏幕上移动
- ACTION_UP —— 手指从屏幕上松开的一瞬间
正常情况下,一次手指接触屏幕的行为会触发一系列点击事件,考虑如下几种情况:
- 点击屏幕后离开松开,事件序列为 DOWN -> UP
- 点击屏幕滑动一会后在松开,事件序列为 DOWN -> MOVE -> …. -> UP
上述两种情况是典型的事件序列,同时通过MotionEvent对象我们可以得到点击事件发生的 X 和 Y坐标。为此,系统提供了两组方法:getX/getY 和 getRawX/getRawY。它们的区别其实很简单,getX/getY 返回的是相当于当前View左上角的 x 和 y 坐标,而 getRawX/getRawY 返回的是相当于手机屏幕左上角的 x 和 y 坐标 如图:
2、TouchSlop
TouchSlop是系统所能识别出的被认为是滑动的最小距离,换句话说,当手指在屏幕上滑动时,如果两次滑动之间的距离小于这个常量,那么系统就不认为你是在进行滑动操作。原因很简单:滑动的距离太短,系统不认为它是滑动。这是一个常量,和设备有关,在不同设备上这个值可能是不同的,通过如下方式即可获取这个常量:ViewConfiguration.get(getContext()).getScaledTouchSlop(),这个常量有什么意义呢?当我们在处理滑动时,可以利用这个常量来做一些过滤,比如当两次滑动事件的滑动距离小于这个值,我们就可以认为为达到滑动距离的临界值,因此就可以认为它们不是滑动,这样做可以有更好的用户体验。其实如果细心的话,可以子啊源码中找到这个常量的定义,在 frameworks/base/core/res/res/values/config.xml 文件中,如下所示:这个 ” config_viewConfigurationTouchSlop”对应的就是这个常量的定义。
<dimen name = "config_viewConfigurationTouchSlop">8dp</dimen>
VelocityTracker、GestureDetector 和 Scroller
1、VelocityTracker
速度追踪,用于追踪手指在滑动过程中的速度,包括水平和竖直方向的速度,它的使用过程很简单,首先,在View的onTouchEvent方法中追踪当前事件的速度;
@Override
public boolean onTouchEvent(MotionEvent event) {
final int action = event.getAction();
VelocityTracker obtain = VelocityTracker.obtain();
obtain.addMovement(event);
switch (action) {
case MotionEvent.ACTION_MOVE:
obtain.computeCurrentVelocity(10000);
float xVelocity = obtain.getXVelocity();
float yVelocity = obtain.getYVelocity();
Log.e("当前追踪手指滑动过程中的速度为", "X = " + xVelocity + "====== Y = " + yVelocity);
break;
case MotionEvent.ACTION_UP:
obtain.clear();
obtain.recycle();
obtain = null;
}
return super.onTouchEvent(event);
}
在这一步中有两点需要注意:第一点,获取速度之前必须先计算速度,即 getXVelocity 和 getYVelocity 这两个方法前面必须要调用computeCurrentVelocity方法;第二点,这里的速度指一段事件内手指所滑动过的像素数,比如将事件间隔设为1000ms时,在 1s 内,手指在水平方向从左向右滑过 100 像素,那么水平速度就是 100。注意速度可以为负数,当手指从右往左滑动时或手指从下往上滑动时,返回的即为负值,这个需要理解一下。速度的计算可以用如下公式来表示:
速度 = (终点位置 - 起点位置) / 时间段
根据上面的公式在加上 Android 系统的坐标系,可以知道,手指逆着坐标系的正方向滑动,所产生的速度就为负值。另外,computeCurrentVelocity这个方法的参数表示的是一个时间单元或者说时间间隔,它的单位是毫秒(ms),计算速度时得到的速度就是在这个时间间隔内手指在水平或竖直方向上所滑动的像素数。*针对上面的例子,我们如果通过obtain.computeCurrentVelocity(100)来获取速度,那么得到的速度就是手指在 100ms 内所滑动的像素数。
最后,当不需要使用它的时候,需要调用clear方法来重置并回收内存;
obtain.clear();
obtain