为什么需要手势识别?

手势对于我们的app有很多的地方都在使用,比如右滑关闭界面等。手势控制分为触发动作(Touch Mechanics,用户手指在屏幕上如何动作)和触发行为(Touch Activities,界面上特定动作在特定情境下引发的结果)。这是因为同样的触发动作(如单次触击)在不同情境下可能会带来不同的结果(如轻触,取消,开启/关闭指示),同样单次触发行为(如放大)可能是由多种触发动作(如捏放,双次触击,双次触击拖拽等)实现。

一般情况下,我们知道View类有个View.OnTouchListener内部接口,通过 重写他的onTouch(View v, MotionEvent event) 方法,我们可以处理一些touch事件,但是这个方法太过简单,如果需要处理一些复杂的手势,用这个接口就会很麻烦(因为我们要自己根据用户触摸的轨迹去判断是什么手势)。

Android sdk给我们提供了 GestureDetector 类,通过这个类我们可以识别很多的手势,主要是通过他的onTouchEvent(event)方法完成了不同手势的识别。虽然他能识别手势,但是不同的手势要怎么处理,应该是提供给程序员实现的。

GestureDetector这个类对外提供了两个接口和一个外部类,用于监听不同的手势
接口: OnGestureListener, OnDoubleTapListener
内部类: SimpleOnGestureListener

1、实现 GestureDetector.OnGestureListener

private class gesturelistener implements GestureDetector.OnGestureListener{
    public boolean onDown(MotionEvent e) {
        // TODO Auto-generated method stub  
        return false;
    public void onShowPress(MotionEvent e) {
        // TODO Auto-generated method stub  
    public boolean onSingleTapUp(MotionEvent e) {
        // TODO Auto-generated method stub  
        return false;
    public boolean onScroll(MotionEvent e1, MotionEvent e2,
            float distanceX, float distanceY) {
        // TODO Auto-generated method stub  
        return false;
    public void onLongPress(MotionEvent e) {
        // TODO Auto-generated method stub  
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
            float velocityY) {
        // TODO Auto-generated method stub  
        return false;

实现该接口时,需要重载六个函数。 下面讲解一下这六个函数的触发条件(具体参看源码中的注释):

  • OnDown(MotionEvent e):用户按下屏幕就会触发;
  • onShowPress(MotionEvent e):如果是按下的时间超过瞬间,而且在按下的时候没有松开或者是拖动的,那么onShowPress就会执行。官方的注释如下:
  • * The user has performed a down {@link MotionEvent} and not performed * a move or up yet. This event is commonly used to provide visual * feedback to the user to let them know that their action has been * recognized i.e. highlight an element. * @param e The down motion event
  • onLongPress(MotionEvent e):长按触摸屏,超过一定时长,就会触发这个事件
    触发顺序:
        onDown->onShowPress->onLongPress
  • onSingleTapUp(MotionEvent e):从名子也可以看出,一次单独的轻击抬起操作,也就是轻击一下屏幕,立刻抬起来,才会有这个触发,当然,如果除了Down以外还有其它操作,那就不再算是Single操作了,所以也就不会触发这个事件
        触发顺序:
        点击一下非常快的(不滑动)Touchup:
        onDown->onSingleTapUp->onSingleTapConfirmed
        点击一下稍微慢点的(不滑动)Touchup:
        onDown->onShowPress->onSingleTapUp->onSingleTapConfirmed
  • onFling(MotionEvent e1, MotionEvent e2, float velocityX,float velocityY) :滑屏,用户按下触摸屏、快速移动后松开,由1个MotionEvent ACTION_DOWN, 多个ACTION_MOVE, 1个ACTION_UP触发  
    参数解释:(当滑动屏幕的速度较慢时,该方法不会被调用)
        e1:第1个ACTION_DOWN MotionEvent
        e2:最后一个ACTION_MOVE MotionEvent
        velocityX:X轴上的移动速度,像素/秒
        velocityY:Y轴上的移动速度,像素/秒  
  • 注: 可以通过该函数里的滑动距离和滑动速度,判断是左滑还是右滑

  • onScroll(MotionEvent e1, MotionEvent e2,float distanceX, float distanceY):在屏幕上拖动事件。无论是用手拖动view,或者是以抛的动作滚动,都会多次触发,这个方法       在ACTION_MOVE动作发生时就会触发
        滑屏:手指触动屏幕后,稍微滑动后立即松开
        onDown-----》onScroll----》onScroll----》onScroll----》………----->onFling
        拖动(当滑动的速度不是很快的时候,onFling函数最后不会被调用。)
        onDown------》onScroll----》onScroll
  • 2、实现OnDoubleTapListener

    /**
    * The listener that is used to notify when a double-tap or a confirmed
    * single-tap occur.
    */

        OnGestureListener实现了单击相关的手势识别,对于双击相关的手势识别需要单独实现,下面具体介绍里面的几个方法:

     private GestureDetector.OnDoubleTapListener mOnDoubleTapListener = new GestureDetector.OnDoubleTapListener() {
            @Override
            public boolean onSingleTapConfirmed(MotionEvent e) {
                logger("onSingleTapConfirmed:");
                return false;
            @Override
            public boolean onDoubleTap(MotionEvent e) {
                logger("onDoubleTap:");
                return false;
            @Override
            public boolean onDoubleTapEvent(MotionEvent e) {
                logger("onDoubleTapEvent:"+MotionEvent.actionToString(e.getAction()));
                return false;
    

    onSingleTapConfirmed(MotionEvent e):单击事件。用来判定该次点击是SingleTap而不是DoubleTap,如果连续点击两次就是DoubleTap手势,如果只点击一次,系统等待一段时间后没有收到第二次点击则判定该次点击为SingleTap而不是DoubleTap,然后触发SingleTapConfirmed事件。触发顺序是:OnDown->OnsingleTapUp->OnsingleTapConfirmed
    关于onSingleTapConfirmed和onSingleTapUp的一点区别: OnGestureListener有这样的一个方法onSingleTapUp,和onSingleTapConfirmed容易混淆。二者的区别是:onSingleTapUp,只要手抬起就会执行,而对于onSingleTapConfirmed来说,如果双击的话,则onSingleTapConfirmed不会执行。

    onDoubleTap(MotionEvent e):双击事件

    onDoubleTapEvent(MotionEvent e):双击间隔中发生的动作。指触发onDoubleTap以后,在双击之间发生的其它动作,包含down、up和move事件;下图是双击一下的Log输出:

    3、实现SimpleOnGestureListener

    * A convenience class to extend when you only want to listen for a subset * of all the gestures. This implements all methods in the * {@link OnGestureListener}, {@link OnDoubleTapListener}, and {@link OnContextClickListener} * but does nothing and return {@code false} for all applicable methods. public static class SimpleOnGestureListener implements OnGestureListener, OnDoubleTapListener,OnContextClickListener{…..}

    它与前两个不同的是:
    1、这是一个类,在它基础上新建类的话,要用extends派生而不是用implements继承!
    2、OnGestureListener和OnDoubleTapListener接口里的函数都是强制必须重写的,即使用不到也要重写出来一个空函数但在SimpleOnGestureListener类的实例或派生类中不必如此,可以根据情况,用到哪个函数就重写哪个函数,因为SimpleOnGestureListener类本身已经实现了这两个接口的所有函数,只是里面全是空的而已。

    4、通过兼容包中工具识别手势

    GestureDetector属于android.view包,android还提供了android.gesture包支持更多的手势操作。官方的介绍中使用了GestureDetectorCompat处理手势识别。 在识别手势的时候尽量使用 兼容包中手势识别。

    public class MainActivity extends Activity implements
            GestureDetector.OnGestureListener,
            GestureDetector.OnDoubleTapListener{
        private static final String DEBUG_TAG = "Gestures";
        private GestureDetectorCompat mDetector;
        // Called when the activity is first created. 
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            // Instantiate the gesture detector with the
            // application context and an implementation of
            // GestureDetector.OnGestureListener
            mDetector = new GestureDetectorCompat(this,this);
            // Set the gesture detector as the double tap
            // listener.
            mDetector.setOnDoubleTapListener(this);
        @Override
        public boolean onTouchEvent(MotionEvent event){
            this.mDetector.onTouchEvent(event);
            // Be sure to call the superclass implementation
            return super.onTouchEvent(event);
        @Override
        public boolean onDown(MotionEvent event) {
            Log.d(DEBUG_TAG,"onDown: " + event.toString());
            return true;
        @Override
        public boolean onFling(MotionEvent event1, MotionEvent event2,
                float velocityX, float velocityY) {
            Log.d(DEBUG_TAG, "onFling: " + event1.toString()+event2.toString());
            return true;
        @Override
        public void onLongPress(MotionEvent event) {
            Log.d(DEBUG_TAG, "onLongPress: " + event.toString());
        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
                float distanceY) {
            Log.d(DEBUG_TAG, "onScroll: " + e1.toString()+e2.toString());
            return true;
        @Override
        public void onShowPress(MotionEvent event) {
            Log.d(DEBUG_TAG, "onShowPress: " + event.toString());
        @Override
        public boolean onSingleTapUp(MotionEvent event) {
            Log.d(DEBUG_TAG, "onSingleTapUp: " + event.toString());
            return true;
        @Override
        public boolean onDoubleTap(MotionEvent event) {
            Log.d(DEBUG_TAG, "onDoubleTap: " + event.toString());
            return true;
        @Override
        public boolean onDoubleTapEvent(MotionEvent event) {
            Log.d(DEBUG_TAG, "onDoubleTapEvent: " + event.toString());
            return true;
        @Override
        public boolean onSingleTapConfirmed(MotionEvent event) {
            Log.d(DEBUG_TAG, "onSingleTapConfirmed: " + event.toString());
            return true;
    View Code
    

    有两个构造方法:一个构造方法入上述手势识别的代码,另一种增加了一个Handler参数,如下:

    梦想不是浮躁,而是沉淀和积累