VelocityTracker 速度追踪
//创建VelocityTracker对象并加入追踪对象
VelocityTracker velocityTracker = VelocityTracker.obtain();
velocityTracker.addMovement(event);
//设置追踪时间间隔(此处为1000ms)
velocityTracker.computeCurrentVelocity(1000);
//获取横向速度
int xVelocity = (int) velocityTracker.getXVelocity();
//获取纵向速度
int yVelocity = (int) velocityTracker.getYVelocity();
//不使用时重置并回收内存
velocityTracker.clear();
velocityTracker.recycle();
GestureDetector 手势检测
使用
首先创建一个GestureDetector对象并实现OnGestureListener接口,或根据需要实现OnDoubleTapListener接口从而能够监听双击行为
GestureDetector mGestureDetector = new GestureDetector(this);
//解决长按屏幕后无法拖动的现象
mGestureDetector.setIsLongpressEnabled(false);
接着在待监管目标View的onTouchEvent方法中添加如下方法
boolean consume = mGestureDetector.onTouchEvent(event);
return consume;
然后可以有选择的实现接口中的方法
方法 | 说明 |
---|---|
onDown | 手指轻触的一瞬间,由一个ACITON_DOWN触发 |
onShowPress | 手指轻触,尚未松开或拖动,由一个ACTION_DOWN触发 |
onSingleTapUp | 手指轻触屏幕后松开(单击行为),伴随一个ACTION_UP触发 |
onScroll | 手指按下屏幕并拖动(拖动行为),由一个ACITON_DOWN和多个ACTION_MOVE触发 |
onLongPress | 长按 |
onFiling | 用户按下触摸屏、快速滑动后松开(快速滑动行为),由一个ACTION |
方法 | 说明 |
---|---|
onDoubleTap | 双击,由两次连续单击行为组成 |
onSingleTapConfirmed | 严格的单击行为 |
onDoubleTapEvent | 发生了双击行为,ACTION_DOWN/MOVE/UP都会触发此回调 |
Scroller 弹性滑动对象
改变布局参数
//eg:
MarginLayoutParamas paramas = (MarginLayoutParams) mButton1.getLayoutParamas();
paramas.width += 100;
paramas.leftMargin += 100;
mButton1.requestLayout();
//或者mButton1.setLayoutParams(params);
共同思想:将一次大的滑动分成若干次小的滑动,并在一个时间段内完成
Scroller scroller = new Scroller(mContext);
private void smoothScrollTo(int destX, int destY) {
···
scroller.startScroll(···);
invalidate();
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
//判断条件中的方法会根据时间的流逝计算出当前的scrollX和scrollY的值
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
}
}
三大方法:
//点击事件分发过程三大方法关系伪代码分析
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean consume = false; //事件未被消费
if (onInterceptTouchEvent(ev)) {
consume = onTouchEvent(ev);
} else {
consume = child.dispatchTouchEvent(ev);
}
return consume;
}
顶级View(ViewGroup)对点击事件的分发过程
//check for interception
final boolean intercepted;
if(actionMasked == MOtionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT != 0);
//FLAG_DISALLOW_INTERCEPT一旦设置后,标记位不为零,disallowIntercept赋值为true
//进入else,在else中intercept返回true
//ViewGroup将无法拦截除了ACTION_DOWN之外的其他点击事件
if(!disallowIntercept) {
//如果disallowIntercept为false才会进入此代码块
//即子View不请求该标记位为不能拦截事件
intercept = onInterceptTouchEvent(ev);
ev.setAction(action);
} else {
intercept = false;
}
} else {
//如果ViewGroup拦截了DOWN事件,mFirstTouchTarget = 0
//非ACTION_DOWN的后续系列事件默认交给ViewGroup处理,即不会调用onInterceptTouchEvent
intercept = true
}
(1)外部拦截法(推荐)
重写父容器的onInterceptTouchEvent方法,父容器根据自己的需要进行事件拦截。
public boolean onInterceptTouchEvent(MotionEvent event) {
boolean intercepted = false;
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACION_DOWN: {
intercepted = false;
break;
}
case MotionEvent.ACTION_MOVE: {
if (父容器需要拦截当前点击事件) {
intercept = true;
} else {
intercept = false;
}
break;
}
case MotionEvent.ACTION_UP: {
intercept = false;
break;
}
default:
break;
}
//赋值记录位置
mLastXIntercept = x;
mLastYIntercept = y;
return intercept;
}
(2)内部拦截法
父容器不拦截任何事件,所有事件都传递给子元素,如果子元素需要此事件就直接消耗掉,否则就交给父容器处理。重写子容器的dispatchTouchEvent方法
public boolean dispatchTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch(event.getAction()){
case MotionEvent.ACTION_DOWN: {
parent.requestDisallowIntereptTouchEvent(true);
break;
}
case MotionEvent.ACITON_MOVE: {
int deltaX = x - LastX;
int deltaY = y - LastY;
if (父容器需要此类点击事件) {
parent.requestDisallowIntereptTouchEvent(false);
}
break;
}
case MotionEvent.ACTION_UP: {
break;
}
default:
break;
}
//赋值记录位置
mLastX = x;
mLastY = y;
//保持原始的事件分发逻辑
return super.dispatchTouchEvent()
}