1.MotionEvent和TouchSlop
手指触碰屏幕会产生一些事件:
MotionEvent.ACTION_DOWN--手指刚触碰到屏幕
MotionEvent.ACTION_MOVE--手指在屏幕上移动
MotionEvent.ACTION_UP--手指从屏幕上离开的一瞬间
TouchSlop代表系统所能识别的最小滑动距离,如果小于这个距离就不认为是滑动操作
获取方式:
ViewConfiguration.get(getContext()).getScaledTouchSlop();
2.VelocityTracker
用于追踪手指在滑动过程中的速度,包括了水平速度和垂直速度。首先要在onTouchEvent方法中添加当前事件给速度追踪器:
VelocityTracker tracker = VelocityTracker.obtain();
tracker.addMovement(event);
然后需要设置速度单位,一般是像素/s、像素/500ms等,这个时间可以自己设置:
tracker.computeCurrentVelocity(500);
接着就可以得到水平、垂直两个方向上的速度:
int xVelocity = (int) tracker.getXVelocity();
int yVelocity = (int) tracker.getYVelocity();
最后还有很重要的操作就是回收:
tracker.clear();
tracker.recycle();
3.GestureDetector
上面说到手势事件在MotionEvent中提供了三个基本的事件,如果我们想触发双击、拖动、长按等事件就要用到GestureDetector:
private GestureDetector detector = new GestureDetector(getContext(), new GestureDetector.OnGestureListener() {
@Override
public boolean onDown(MotionEvent e) {//触碰屏幕瞬间
return false;
}
@Override
public void onShowPress(MotionEvent e) {}//轻触屏幕且未松开或拖动
@Override
public boolean onSingleTapUp(MotionEvent e) {//触碰后松开
return false;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return false;//按下屏幕并拖动
}
@Override
public void onLongPress(MotionEvent e) {}//长按
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
return false;//快速滑动且松开
}
});
然后在onTouchEvent方法中添加事件:
detector.onTouchEvent(event);
如果还需要监听双击:
detector.setOnDoubleTapListener(new GestureDetector.OnDoubleTapListener() {
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
return false;//严格的单击行为,后面不可能再紧跟着另一个单击,即这不可能是双击中的一次单击
}
@Override
public boolean onDoubleTap(MotionEvent e) {
return false;//双击。和onSingleTapConfirmed不可能共存
}
@Override
public boolean onDoubleTapEvent(MotionEvent e) {
return false;
}
});
一般情况下我们不去实现GestureDetector的几个接口方法,除非特别需要监听双击、长按等特殊事件
4.View点击事件的分发机制
即当一个MotionEvent事件产生了之后,系统需要把这个事件传递给一个具体的View,
而这个传递的过程就是分发过程。这个过程由三个方法共同完成:
public boolean dispatchTouchEvent(MotionEvent e)
用来进行事件分发,如果事件可以传递给当前View,那么此方法一定会被调用,
返回结果受当前View的onTouchEvent方法和下级View的dispatchTouchEvent方法
影响,表示是否消耗当前事件
public boolean onInterceptTouchEvent(MotionEvent e)
在dispatchTouchEvent方法内部调用,用来判断是否拦截某个事件,如果当前View
拦截了就在当前View中处理事件,否则传递给子View处理。注意此方法只在ViewGroup
中有,因为只有他存在子View,而且它默认不拦截任何事件,都传给子View去处理
public boolean onTouchEvent(MotionEvent e)
在dispatchTouchEvent方法中调用,用来处理点击事件,返回结果表示是否消耗
当前事件,如果不消耗那么当前View将无法接收下一个事件
三者的关联:
@override
public boolean dispatchTouchEvent(MotionEvent e) {
boolean consume = false;
if (onInterceptTouchEvent(e)) {//是否拦截当前事件
consume = onTouchEvent(e);//拦截当前事件并在当前View中处理
} else {
consume = childView.dispatchTouchEvent(e);//不拦截当前事件并传递给子View
}
return consume;//是否消耗当前事件,不消耗则无法接收下一个事件
}
另外再来说说OnTouchListener、OnClickListener和onTouchEvent方法的联系:
如果一个View对象设置了OnTouchListener,那么就会回调onTouch方法,如果onTouch方法返回false,则当前View的onTouchEvent方法会被调用,否则不会被调用,由此可见OnTouchListener的优先级高于onTouchEvent。如果当前View对象设置了OnClickListener且重写了onTouchEvent方法,当你点击当前View对象时是不会执行onClick方法中的代码的,而是执行onTouchEvent方法中的逻辑,这个时候想要执行onClick方法就需要在onTouchEvent方法中加一句performClick()来并发执行onClick方法,可见OnClickListener的优先级要低于onTouchEvent。所以三者的优先级关系是:OnTouchListener>onTouchEvent>OnClickListener