比如有这样一个常见的 app 功能,一个跟随手指移动的按钮,常见如屏幕悬浮球、拖拽手柄等。
实现这样的需求,必然要处理 view 的 onTouch 事件,大概的一个代码模板就是这样:
|
|
借助这个例子说明几点问题,这段代码主要是获取并处理 event 事件的 Y 值。其中有几点需要注意的是:
第一,onTouch 方法返回值必须为 true,表示当前触摸事件已被消费,拦截后续事件传递;
第二,click 事件。由于第一点中 onTouch 返回值为 true,view 的 click 事件自然也被拦截,那么就需要手动处理 click 事件。可以通过判断 event 偏移量来实现;
第三,一个完善的跟随手指移动的功能,通常少不了快速滑动的功能,判定滑动速度,需要用到专业的 GestureDetector 类。当然这里没有写,因为使用这个类后,相关逻辑都要搬出 onTouch 事件,到 Gesture 中处理。桥接代码类似:
|
|
第四,使用 getRawX 与 getRawY 方法而不是 getX 与 getY 方法。这才是我想强调的最重要的一点。前者表示获取 touch event 事件产生的屏幕坐标值,对应于屏幕坐标系;后者表示相对于父容器 View 的坐标值,对应于 View 所处父容器的位置。看似这两种坐标系函数都可以使用,计算偏差而已,实际上后者的使用会有问题。
还拿最开始那段代码示例,同一段手势向下的操作,这是 getY 的打印日志:
|
|
这是 getRawY 的打印日志:
|
|
发现区别了吗?getY 值不稳定,相邻值前后大小变来变去;getRawY 值则是线性的一个方向上的变化,符合实际手势的操作行为。
具体反应在跟随手指移动的 View 上面,会发现使用 getY 函数产生 View 抖动特别明显,效果很差。而使用 getRawY 方法不会导致 View 抖动,比较顺畅。
所以,理论上使用 motionEvent 获取 touch 事件的坐标值,不论使用什么方法都能计算正确,但是由于 getX 与 getY 函数结果的不稳定性,只能选择基于屏幕坐标系的 getRawX 与 getRawY 函数。这是实践中特别需要的一个细节问题。