…
}
有了上面这个工具类,我们在自定义控件处理滑动手势等判断时就可以很方便的判断出临界值等问题,不用我们再去自己测试定义一个近似的值来代替。
特别注意: ViewConfiguration还有一个在support包中的兼容类ViewConfigurationCompat,使用时请注意一下。
【工匠若水 http://blog.youkuaiyun.com/yanbober 未经允许严禁转载,请尊重作者劳动成果。私信联系我】
Scroller加强版OverScroller回弹工具类
================================
之前有篇博客说到了Scroller的源码浅析,其实Scroller在API 1就出现了,而这里要说的Scroller加强版OverScroller在API 9才出现,所以功能指定比之前的Scroller强大,支持了回弹效果(关于不同的回弹效果我们可以自定义不同的动画插值器即可),不过原理基本和之前分析的Scroller源码一样,所以这里我们不会再对OverScroller源码分析,只对他和Scroller的差异进行说明,下面我们来看看。
OverScroller在Scroller类基础上多出来的方法:
| 方法 | 含义 |
| — | — |
| isOverScrolled() | 返回当前的位置是否有效或者是否超出滚动边界。 |
| springBack(int startX, int startY, int minX, int maxX, int minY, int maxY) | 当你想回滚的时候调用这个方法,回滚的范围在有效的坐标范围内。 |
| fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY, int overX, int overY) | 同Scroller的,只是最后两个参数含义为fling滚动超过有效值的范围。 |
| notifyHorizontalEdgeReached(int startX, int finalX, int overX) | 通知水平滚动是否到达边界。 |
| notifyVerticalEdgeReached(int startY, int finalY, int overY) | 同上。 |
关于Scroller的基本使用流程可以参见我之前博客Scroller源码浅析和ViewDragHelper源码浅析两篇文章,如果需要深入理解可以看看官方ScrollView的实现,其就完全使用了OverScroller。
特别注意: Scroller(OverScroller)这货也有一个在support兼容包的兼容类ScrollerCompat,使用时请留意一下。
【工匠若水 http://blog.youkuaiyun.com/yanbober 未经允许严禁转载,请尊重作者劳动成果。私信联系我】
VelocityTracker手势速率工具类
==========================
VelocityTracker主要用跟踪触摸屏事件(Flinging及其他Gestures手势事件等)的速率。我们在拿到实例后可以通过computeCurrentVelocity(int)来初始化速率的单位,然后接着通过addMovement(MotionEvent)方法将MotionEvent加入VelocityTracker实例中,然后在需要的地方通过getXVelocity() 或getXVelocity
《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
()获得横向和竖向的速率即可。
下面给出相关的API说明(VelocityTracker许多方法都是native实现的):
public final class VelocityTracker {
//获取VelocityTracker实例
static public VelocityTracker obtain() {}
public static VelocityTracker obtain(String strategy) {}
//回收后代表你不需要使用了,系统将此对象在此分配到其他请求者
public void recycle() {}
//清空回到初始状态,computeCurrentVelocity都被reset了
public void clear() {}
//将事件加入到VelocityTracker类实例中
public void addMovement(MotionEvent event) {}
//unitis表示速率的基本时间单位,1表示一毫秒时间单位内运动了多少个像素
public void computeCurrentVelocity(int units) {}
//同上,floatVelocity表示速率的最大值,超过最大值的都返回最大值
public void computeCurrentVelocity(int units, float maxVelocity) {}
//获取xy方向速率
public float getXVelocity() {}
public float getYVelocity() {}
//获取xy速率,id为event的pointid
public float getXVelocity(int id) {}
public float getYVelocity(int id) {}
}
有了上面这些手势速率的检测工具类,下面我们来看下他的一些通用模板:
VelocityTracker mVelocityTracker = null;
@Override
public boolean onTouchEvent(MotionEvent event){
int action = event.getAction();
switch(action){
case MotionEvent.ACTION_DOWN:
if(mVelocityTracker == null){
mVelocityTracker = VelocityTracker.obtain();
}else{
mVelocityTracker.clear();
}
mVelocityTracker.addMovement(event);
break;
case MotionEvent.ACTION_MOVE:
mVelocityTracker.addMovement(event);
mVelocityTracker.computeCurrentVelocity(1000);
Log.i("X = "+mVelocityTracker.getXVelocity());
Log.i("Y = "+mVelocityTracker.getYVelocity());
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mVelocityTracker.recycle();
break;
}
return true;
}
关于速率检测类的知识就介绍到这里,没啥新鲜的。
【工匠若水 http://blog.youkuaiyun.com/yanbober 未经允许严禁转载,请尊重作者劳动成果。私信联系我】
GestureDetector手势工具类
========================
除了我们通过onTouchEvent()自己处理一堆复杂的手势以外,其实Android给我们提供了现成的便捷方式,那就是GestureDetector手势监听类,如下:
public class GestureDetector {
public interface OnGestureListener {
//ACTION_DOWN时触发
boolean onDown(MotionEvent e);
//ACTION_DOWN了过一会还没有滑动时触发,onDown->onShowPress->onLongPress
void onShowPress(MotionEvent e);
//ACTION_DOWN后没有滑动(onScroll)且没有长按(onLongPress)接着ACTION_UP时触发
boolean onSingleTapUp(MotionEvent e);
//滑动时实时触发
boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);
//ACTION_DOWN长按时触发
void onLongPress(MotionEvent e);
//触摸滑动一定距离后松手ACTION_UP时触发,后参数为速率
boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);
}
public interface OnDoubleTapListener {
//ACTION_DOWN后没有滑动(onScroll)且没有长按(onLongPress)接着ACTION_UP时触发
boolean onSingleTapConfirmed(MotionEvent e);
//双击的第二下ACTION_DOWN时触发
boolean onDoubleTap(MotionEvent e);
//双击的第二下ACTION_DOWN和ACTION_UP都会触发,e.getAction()区别
boolean onDoubleTapEvent(MotionEvent e);
}
public interface OnContextClickListener {
//context点击触发,与View#onGenericMotionEvent(MotionEvent)相关,不常用
boolean onContextClick(MotionEvent e);
}
public static class SimpleOnGestureListener implements OnGestureListener, OnDoubleTapListener,
OnContextClickListener {
…
//OnGestureListener、OnDoubleTapListener、OnContextClickListener所有接口的默认实现
…
}
//各种推荐的不推荐的构造方法
@Deprecated
public GestureDetector(OnGestureListener listener, Handler handler) {}
@Deprecated
public GestureDetector(OnGestureListener listener) {}
public GestureDetector(Context context, OnGestureListener listener) {}
public GestureDetector(Context context, OnGestureListener listener, Handler handler) {}
public GestureDetector(Context context, OnGestureListener listener, Handler handler,
boolean unused) {}
//其他两类回调接口的设置,OnGestureListener必须在构造中就处理掉
public void setOnDoubleTapListener(OnDoubleTapListener onDoubleTapListener) {}
public void setContextClickListener(OnContextClickListener onContextClickListener) {}
//一些处理判断方法
public void setIsLongpressEnabled(boolean isLongpressEnabled) {}
public boolean isLongpressEnabled() {}
public boolean onTouchEvent(MotionEvent ev) {}
public boolean onGenericMotionEvent(MotionEvent ev) {}
}
有了上面这些GestureDetector手势工具类的基本API介绍之后我们就可以各种使用了,没啥特殊的介绍。
特别注意: 其实手势相关的东西还有Gesture类等GestureOverlayView手势创建识别类的,这里不作介绍,作为拓展。
【工匠若水 http://blog.youkuaiyun.com/yanbober 未经允许严禁转载,请尊重作者劳动成果。私信联系我】
View及ViewGroup触摸事件总结
========================
关于View触摸屏事件的传递机制源码分析其实可以参考我前面写的博客,这篇文章既然是总结,那就是只给出结论,相关分析请看前面的博文。
触摸事件传递源Activity:
Activity的dispatchTouchEvent()方法将事件传递给它的根布局ViewGroup(即调用根布局ViewGroup的dispatchTouchEvent()方法,该方法会对事件进行如下情况处理:
-
如果根布局ViewGroup及其内部子布局控件均没处理(此时根布局ViewGroup的dispatchTouchEvent()方法返回false)则调用Activity自己的onTouchEvent()方法;如果Activity自己的onTouchEvent()方法仍然没有处理(返回false)则该事件处理宣告结束。
-
如果根布局ViewGroup的dispatchTouchEvent()方法返回true则表明根布局中处理了这一次事件,此时就不会再调用Activity的onTouchEvent()方法了(因为Activity没有父控件且不能设置触摸监听OnTouchListener,所以没有onInterceptTouchEvent()方法)。
触摸事件传递View级别处理:
这里所谓的View级别泛指其内部不包含子控件(已经为最小控件单位)的View,当该View的父级ViewGroup触发该View的dispatchTouchEvent()方法时,由于该View没有子控件可以被继续派发,所以事件只能自己调度自己相关方法。测试的调度如下:
-
如果该View注册了OnTouchListener,则优先调用OnTouchListener的onTouch()方法,如果onTouch()方法返回false则继续调运该View的onTouchEvent()方法,如果onTouch()方法返回true则该View的dispatchTouchEvent()方法直接返回true。
-
如果该View没有注册OnTouchListener则直接调用该View的onTouchEvent()方法,该方法返回true、false决定了该View的dispatchTouchEvent()方法返回值。
触摸事件传递ViewGroup级别处理:
ViewGroup的dispatchTouchEvent()方法被其父布局 (父ViewGroup或者Activity)调用,当前ViewGroup的dispatchTouchEvent()方法主要任务就是为子控件派发事件(调运子控件的dispatchTouchEvent()),同时向父级布局返回事件处理情况。
不过在当前ViewGroup的dispatchTouchEvent()方法向子控件派发事件之前我们在当前ViewGroup里是可以通过自己的onInterceptTouchEvent()方法来决定触摸事件是否拦截(当前ViewGroup的onInterceptTouchEvent()返回true则不再传递给自己的子控件,而是当前ViewGroup自己处理,接着将处理结果告诉父控件;返回false则不拦截(继续传递给子控件,如果子控件的dispatchTouchEvent()方法都返回false则ViewGroup就尝试自己处理事件,然后告诉父布局自己处理的结果)。
一次完整的事件流程处理:
综合从Activity到根ViewGroup到中间ViewGroup,再到View的事件处理流程,我们要注意其传递过程中的下面几点:
-
一次完整的事件触发可以分为ACTION_DOWN->[ACTION_MOVE]->ACTION_UP。当我们手指按下派发ACTION_DOWN事件时,如果我们当前层级的View或者ViewGroup的onTouch()或onTouchEvent()方法返回false,则当前层级的View或者ViewGroup的onTouch()或onTouchEvent()方法就再也接收不到其他事件了,直到下次新的触摸事件(ACTION_DOWN)开始。
-
在一次完整的事件传递(ACTION_DOWN->[ACTION_MOVE]->ACTION_UP)过程中只要当前ViewGroup的onInterceptTouchEvent()方法有一次返回true则当前ViewGroup将会拦截这次事件传递的全部后续触发事件,同时这些后续触发事件都不会再触发当前ViewGroup的onInterceptTouchEvent()方法(直到下次ACTION_DOWN来临),同时向之前处理事件的子布局传递一个ACTION_CANCEL事件。如果当前ViewGroup的onInterceptTouchEvent()方法返回false,则本次传递的每个事件来临时都会触发当前ViewGroup的onInterceptTouchEvent()方法。
-
只有ViewGroup才有onInterceptTouchEvent()方法,因为最小单位的View不具备再往下派发事件的能力,它只会直接调用自己的onTouch()和onTouchEvent()方法。