GestureDetector源码简析

一、概述

GestureDetector,放进去一个触摸事件,提供回调一系列触摸动作接口的用于手势检测的工具类(大概是怎么个东西吧)。

二、实例化对象

上面三个呢,就不说了,我们主要来看第四个。

    @Deprecated
    public GestureDetector(OnGestureListener listener, Handler handler) {
        this(null, listener, handler);
    }
    
    public GestureDetector(OnGestureListener listener) {
        this(null, listener, null);
    }

    public GestureDetector(Context context, OnGestureListener listener) {
        this(context, listener, null);
    }

    public GestureDetector(Context context, OnGestureListener listener, Handler handler) {
    	//这里对handler判空,构造GestureHandler对象。
        if (handler != null) {
            mHandler = new GestureHandler(handler);
        } else {
            mHandler = new GestureHandler();
        }
        mListener = listener;
        //这里下面两个if我不太能理解是什么意思,在我看来这两个应该永远都if不进去。因为OnGestureListener和	OnDoubleTapListener以及OnContextClickListener是平级的接口,不存在相互的继承关系。
        if (listener instanceof OnDoubleTapListener) {
            setOnDoubleTapListener((OnDoubleTapListener) listener);
        }
        if (listener instanceof OnContextClickListener) {
            setContextClickListener((OnContextClickListener) listener);
        }
        //在这里初始化一些东西啦
        init(context);
    }
    

我们来看构造的GestureHandler对象是干嘛的,

private class GestureHandler extends Handler {
        GestureHandler() {
            super();
        }
        GestureHandler(Handler handler) {
            super(handler.getLooper());
        }
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case SHOW_PRESS:
            	//这里回调了一个onShowPress方法(轻触摸屏)
                mListener.onShowPress(mCurrentDownEvent);
                break;
            case LONG_PRESS:
            	//这里里面回调了一个onLongPress方法(长按触摸屏)
                dispatchLongPress();
                break;
            case TAP:
                if (mDoubleTapListener != null) {
                    if (!mStillDown) {
            			//这里回调了一个onSingleTapConfirmed方法(单击事件)
                        mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent);
                    } else {
                        mDeferConfirmSingleTap = true;
                    }
                }
                break;

            default:
                throw new RuntimeException("Unknown message " + msg); //never
            }
        }
    }

来看看初始化了啥东西

  private void init(Context context) {
  		//常规的判空抛异常
        if (mListener == null) {
            throw new NullPointerException("OnGestureListener must not be null");
        }
        //长按,可
        mIsLongpressEnabled = true;

        int touchSlop, doubleTapSlop, doubleTapTouchSlop;
        //这里context为空想不出应用场景跳过不看
        if (context == null) {
            touchSlop = ViewConfiguration.getTouchSlop();
            doubleTapTouchSlop = touchSlop; // Hack rather than adding a hiden method for this
            doubleTapSlop = ViewConfiguration.getDoubleTapSlop();
            mMinimumFlingVelocity = ViewConfiguration.getMinimumFlingVelocity();
            mMaximumFlingVelocity = ViewConfiguration.getMaximumFlingVelocity();
        } else {
        //这里用View的常量配置类ViewConfiguration获取View的一些基本配置参数,保存备用。
            final ViewConfiguration configuration = ViewConfiguration.get(context);
            //表示系统滑动距离的最小值(大于该值,可以认为滑动)
            touchSlop = configuration.getScaledTouchSlop();
            //表示点击的时候,手指移动距离的最大值(大于该值,就被认为不可能是双击)
            doubleTapTouchSlop = configuration.getScaledDoubleTapTouchSlop();
            //表示第二次点击的时候,和第一次的点击点位置距离的最大值(大于该值,就被认为不是双击)
            doubleTapSlop = configuration.getScaledDoubleTapSlop();
            //获得允许执行fling (抛)的最小速度值
            mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity();
            //获得允许执行fling (抛)的最大速度值
            mMaximumFlingVelocity = configuration.getScaledMaximumFlingVelocity();
        }
        //这里存储它们平方值,后面做移动距离平方的比较,不清楚为什么要用平方,可能是精度之类的吧
        mTouchSlopSquare = touchSlop * touchSlop;
        mDoubleTapTouchSlopSquare = doubleTapTouchSlop * doubleTapTouchSlop;
        mDoubleTapSlopSquare = doubleTapSlop * doubleTapSlop;
    }

三、事件回调

OnGestureListener

onDown(按下)

是常见的那种按下事件回调。

	@Override
    public boolean onDown(MotionEvent e) {
        return false;
    }
    public boolean onTouchEvent(MotionEvent ev) {
        boolean handled = false;
        switch (action & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
            handled |= mListener.onDown(ev);
            break;
        }
        return handled;
    }

onShowPress(按下后100ms不动)

按下后定时回调,TAP_TIMEOUT为系统默认100ms

	@Override
    public void onShowPress(MotionEvent e) {

    }
    public boolean onTouchEvent(MotionEvent ev) {
        switch (action & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
        	//getDownTime()按下的当前系统时间,TAP_TIMEOUT为系统默认100ms
            mHandler.sendEmptyMessageAtTime(SHOW_PRESS, mCurrentDownEvent.getDownTime() + TAP_TIMEOUT);
            break;
        }
    }

onSingleTapUp(轻按抬起)

手指抬起时,如果没有执行onScroll()和onLongPress()的话,会回调这个,但是注意这里指的是抬起,意味着双击事件的抬起也会触发这个。

    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        return false;
    }
    public boolean onTouchEvent(MotionEvent ev) {
        boolean handled = false;
        switch (action & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_UP:
        	//mAlwaysInTapRegion: ACTION_MOVE、ACTION_CANCEL、ACTION_POINTER_DOWN时会赋值false
        	//mIgnoreNextUpEvent: 鼠标右键等外设设备会赋值true
			if (mAlwaysInTapRegion && !mIgnoreNextUpEvent) {
                handled = mListener.onSingleTapUp(ev);
			}
            break;
        }
        return handled;
    }

onScroll(滑动)

手指滑动的时候执行的回调,e1,e2分别是之前DOWN事件和当前的MOVE事件,distanceX和distanceY就是当前MOVE事件和上一个MOVE事件的位移量。

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        return false;
    }
    public boolean onTouchEvent(MotionEvent ev) {
        boolean handled = false;
        switch (action & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_MOVE:
        	//mAlwaysInTapRegion: ACTION_MOVE、ACTION_CANCEL、ACTION_POINTER_DOWN时会赋值false
			if (mAlwaysInTapRegion) {
				//X轴和Y轴方向移动距离的平方和大于最小滑动距离的平方,则滑动开始
				if (distance > slopSquare) {
					//滑动开始
                    handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
                    mAlwaysInTapRegion = false;
                }
			}else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {
				//滑动ing
                handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
            }
            break;
        }
        return handled;
    }

onLongPress(长按)

是常见的那种长按事件回调。

    @Override
    public void onLongPress(MotionEvent e) {

    }
    public boolean onTouchEvent(MotionEvent ev) {
        boolean handled = false;
        switch (action & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
        	//是否可长按
            if (mIsLongpressEnabled) {
            	//去除掉之前的LongPress
                mHandler.removeMessages(LONG_PRESS);
            	//定时触发新的的LongPress, LONGPRESS_TIMEOUT: 100ms+500ms
                mHandler.sendEmptyMessageAtTime(LONG_PRESS,
                        mCurrentDownEvent.getDownTime() + LONGPRESS_TIMEOUT);
            }
            break;
        }
        return handled;
    }

onFling(抛)

这里的滑动速度的判断不是整个冲down开始到up,是你设置的computeCurrentVelocity()来看的,这里设置1000ms,就是up事件之前的1000ms内的移动距离。

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        return false;
    }
    public boolean onTouchEvent(MotionEvent ev) {
        boolean handled = false;
        switch (action & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_UP:
        		//这里有个滑动速度跟踪器VelocityTracker,一般用于自定义控件的一些粘性操作效果。
                final VelocityTracker velocityTracker = mVelocityTracker;
                final int pointerId = ev.getPointerId(0);
                //设置检测时间1000ms
                velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
                //获取1000ms内Y轴上移动的像素
                final float velocityY = velocityTracker.getYVelocity(pointerId);
                //获取1000ms内X轴上移动的像素
                final float velocityX = velocityTracker.getXVelocity(pointerId);
        		//判断一下X轴和Y轴上的移动速度,确定是否是抛操作
        		if ((Math.abs(velocityY) > mMinimumFlingVelocity)
                        || (Math.abs(velocityX) > mMinimumFlingVelocity)){
                    handled = mListener.onFling(mCurrentDownEvent, ev, velocityX, velocityY);
                }
            break;
        }
        return handled;
    }

OnDoubleTapListener

onSingleTapConfirmed(单击)

这里单击之间的间隔时间要大于300ms,慢一点,单击点太快了就变成了双击事件咯

    @Override
    public boolean onSingleTapConfirmed(MotionEvent e) {
    	return false;
    }
    public boolean onTouchEvent(MotionEvent ev) {
        boolean handled = false;
        switch (action & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
        	//如果300ms没有下一个down则当前为单击事件
        	if (mDoubleTapListener != null) {
        		//判断有没有down事件
                boolean hadTapMessage = mHandler.hasMessages(TAP);
                //有的话就Remove掉
                if (hadTapMessage) mHandler.removeMessages(TAP);
                //这里进去双击
                if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null) && hadTapMessage &&
                        isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) {
                    mIsDoubleTapping = true;
                    handled |= mDoubleTapListener.onDoubleTap(mCurrentDownEvent);
                    handled |= mDoubleTapListener.onDoubleTapEvent(ev);
                //这里进去单击,延时300ms,期间没有第二次down则为单击事件
                } else {
                    mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT);
                }
            }
            break;
        case MotionEvent.ACTION_UP:
        	//如果300ms没有下一个down则当前为单击事件
            if (mDeferConfirmSingleTap && mDoubleTapListener != null) {
            	mDoubleTapListener.onSingleTapConfirmed(ev);
            }
            break;
        }
        return handled;
    }

onDoubleTap(双击事件第二次单击时的按下)

这里并不是我们想象的那种双击,即第二次点击抬起触发,它是在双击中第二次单击时的按下回调的,所以我这里的标题写的是双击中第二次单击时的按下,而不是双击

	@Override
    public boolean onDoubleTap(MotionEvent e) {
    	return false;
    }
    public boolean onTouchEvent(MotionEvent ev) {
        boolean handled = false;
        switch (action & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
        	//如果300ms没有下一个down则当前为单击事件
        	if (mDoubleTapListener != null) {
        		//判断有没有down事件
                boolean hadTapMessage = mHandler.hasMessages(TAP);
                //有的话就Remove掉
                if (hadTapMessage) mHandler.removeMessages(TAP);
                //这里进去双击
                if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null) && hadTapMessage &&
                        isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) {
                    mIsDoubleTapping = true;
                    //触发双击事件第二次单击时的按下
                    handled |= mDoubleTapListener.onDoubleTap(mCurrentDownEvent);
                    handled |= mDoubleTapListener.onDoubleTapEvent(ev);
                //这里进去单击,延时300ms,期间没有第二次down则为单击事件
                } else {
                    mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT);
                }
            }
            break;
        return handled;
    }

onDoubleTapEvent(双击事件第二次单击时的不同事件)

这里的回调都是在onDoubleTap()回调之后的输入事件(DOWN、MOVE、UP),可以做一些如双击拖动之类的特殊操作

    @Override
    public boolean onDoubleTapEvent(MotionEvent e) {
    	return false;
    }
    public boolean onTouchEvent(MotionEvent ev) {
        boolean handled = false;
        switch (action & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
        	//如果300ms没有下一个down则当前为单击事件
        	if (mDoubleTapListener != null) {
        		//判断有没有down事件
                boolean hadTapMessage = mHandler.hasMessages(TAP);
                //有的话就Remove掉
                if (hadTapMessage) mHandler.removeMessages(TAP);
                //这里进去双击
                if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null) && hadTapMessage &&
                        isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) {
                    mIsDoubleTapping = true;
                    handled |= mDoubleTapListener.onDoubleTap(mCurrentDownEvent);
                    //触发双击事件第二次单击时的按下
                    handled |= mDoubleTapListener.onDoubleTapEvent(ev);
                //这里进去单击,延时300ms,期间没有第二次down则为单击事件
                } else {
                    mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT);
                }
            }
            break;
        case MotionEvent.ACTION_MOVE:
        	//如果300ms没有下一个down则当前为单击事件
        	if (mIsDoubleTapping) {
                //触发双击事件第二次单击时的移动
                handled |= mDoubleTapListener.onDoubleTapEvent(ev);
            }
            break;
        case MotionEvent.ACTION_UP:
        	//如果300ms没有下一个down则当前为单击事件
        	if (mIsDoubleTapping) {
                //触发双击事件第二次单击时的抬起
                handled |= mDoubleTapListener.onDoubleTapEvent(ev);
            }
            break;
        return handled;
    }

OnDoubleTapListener

onContextClick(外设设备动作,如当鼠标/触摸板的右键点击)

这个估计很难用的上,目前我知道的是鼠标/触摸板的右键点击,其他的外设不清楚会不会触发。

	@Override
    public boolean onContextClick(MotionEvent e) {
    	return false;
    }
	public boolean onGenericMotionEvent(MotionEvent ev) {
        final int actionButton = ev.getActionButton();
        switch (ev.getActionMasked()) {
            case MotionEvent.ACTION_BUTTON_PRESS:
                if (mContextClickListener != null && !mInContextClick && !mInLongPress
                        && (actionButton == MotionEvent.BUTTON_STYLUS_PRIMARY
                        || actionButton == MotionEvent.BUTTON_SECONDARY)) {
					//在这里触发
                    if (mContextClickListener.onContextClick(ev)) {
                        mInContextClick = true;
                        mHandler.removeMessages(LONG_PRESS);
                        mHandler.removeMessages(TAP);
                        return true;
                    }
                }
                break;
        }
        return false;
    }

SimpleOnGestureListener(简单实现)

这种Simple开头的Listener基本都是包含接口的简单实现,这个SimpleOnGestureListener包含了上述3个接口的所有简单实现,可以通过这个类去拿自己想要的接口。

四、总结

GestureDetector是一个用于手势检测的工具类,通过分析源码实现,能看出它实现的逻辑和方式,在自己自定义控件时能借鉴这种思路,还有一个之前没接触到的类VelocityTracker滑动速度跟踪器,利用这个类可以实现一些滑动控件时粘性,惯性操作。结束撒花。

接口实现方法
OnGestureListeneronDown()、onShowPress()、onSingleTapup()、onScroll()、onLongPress()、onFling()
OnDoubleTapListeneronSingleTapConfirmed()、onDoubleTap()、onDoubleTapEvent()
OnContextClickListeneronContextClick()
SimpleOnGestureListener上述所有()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值