我正在参与「启航方案」

用户接触屏幕时会产生很多手势,在 Android 中,监听屏幕接触事情也有好几种办法,比如 OnTouchListener,onTouchEvent,OnClickListener,GestureDetector 等等,这些办法完成细节和功用有所不同,下面就来理清这些吧!

事情分发机制

事情分发的三个首要对象是:Activity,ViewGroup,View。一个事情产生之后,都是先传给 Activity,再传给 ViewGroup,最终传给 View。

其间有三个重要的办法:

  • dispatchTouchEvent:分发事情
  • onInterceptTouchEvent:判别是否阻拦某个事情,只存在于 ViewGroup
  • onTouchEvent:处理事情

当用户点击了屏幕,事情先传递到 Activity 中,Activity 通过它的 dispatchTouchEvent 将事情分发到 phoneWindow,phonewindow 有个内部类 DecorView,DecorView 会调用 dispatchTouchEvent 去进行事情分发,假如不阻拦事情,就会下传到 Rootview,Rootview 在 dispatchTouchEvent 内部调用 onInterceptTouchEvent 去判别是否阻拦,不阻拦就会把事情分发给下一个 Viewgroup,阻拦就在 onTouchEvent 进行处理并回来 true,Viewgroup 中也是相同,最终事情传递到 View,View 是最底层控件,不会有onInterceptTouchEvent,它的挑选就只有处理和不处理,处理就在 onTouchEvent 进行处理并回来 true,不处理的话事情也不会被毁掉,View 这时会把事情回传,通过上述流程后回传给 Activity,假如此时 Activity 还不处理,那么这个事情才会被毁掉。

一文搞懂 Android 手势监听

所以,Activity 和 View 是没有 onInterceptTouchEvent 这个办法的,由于 Activity 是处于分发机制的最顶端,假如一开端就把事情阻拦了,那么会导致整个屏幕都无法呼应用户的操作,而 View 处于事情分发的最末端,它不需求阻拦,事情分发到 View 的时候,View 能处理就处理,不处理就回来给他的父容器。

OnTouchListener 和 onTouchEvent

OnTouchListener 接口的办法 onTouch,它是获取某一个控件的接触事情,使用时必须绑定到控件,当一个 View 绑定了 OnTouchLister 之后,当有 Touch 事情触发时,就会调用 onTouch 办法。

onTouchEvent 是屏幕事情的处理办法,是获取的对屏幕的各种操作,归于一个宏观的屏幕接触监控。OnTouchListener 是履行在 onTouchEvent 之前的,假如在 OnTouchListener 的 onTouch 函数中回来 true,则表明消费了该事情,该事情不会传到 onTouchEvent 中。

OnLongClickListener 和 OnClickListener

OnClickListener 和 OnLongClickListener 都是在 onTouchEvent 中进行处理的,在 onTouchEvent 中回来 true 时才干判别该接触事情是不是点击,假如在 onTouchEvent 中回来 false 则不处理事情,这时的 OnClickListener 和 OnLongClickListener 都是无效的。那 OnClickListener 和 OnLongClickListener 的差异在于 OnLongClickListener 在 onTouchEvent 判别是 ACTION_DOWN 事情并且按压继续一定时间后就会触发 OnLongClickListener,但之后要等到 onTouchEvent 判别是 ACTION_UP 事情时才会触发 OnClickListener,所以 OnLongClickListener 优先级是大于 OnClickListener 的,当然假如在 OnLongClickListener 的 onLongClick 函数中回来 true 的话就不会触发后面的 OnClickListener 了。

总的来说,优先级是:OnTouchListener > onTouchEvent > OnLongClickListener > OnClickListener,当然,这个需求具体问题具体分析,毕竟随着回来值的不同,有些函数可能都不会调用。

GestureDetecor

用户接触事情,一般使用上面的那些办法就能够解决了,但是假如需求处理一些杂乱的手势,用这些接口就会感觉有点麻烦,需求自己根据用户接触的轨迹去判别是什么手势,这时就要靠 GestureDetector 了。

首先,完成 OnGestureListener 接口中的办法,里边包含着各种事情,如下所示

class MyGestureListener : GestureDetector.OnGestureListener {
    /**
     * 每按一下屏幕立即触发,假如要呼应操作需求回来 true
     */
    override fun onDown(e: MotionEvent): Boolean {
        return true
    }
    /**
     * 用户按下屏幕后未松手前长按触发
     */
    override fun onShowPress(e: MotionEvent) {
    }
    /**
     * 单击抬起时触发,回来值表明事情是否被处理
     */
    override fun onSingleTapUp(e: MotionEvent): Boolean {
        return false
    }
    /**
     * 用户按下后在屏幕上滑动时触发,回来值表明事情是否被处理
     * @param e1:开端滑动第一次按下操作,也便是 ACTION_DOWN
     * @param e2:触发当时 onScroll 办法的 ACTION_MOVE
     * @param distanceX:自前次调用 onScroll 以来沿 X 轴翻滚的间隔
     * @param distanceY:自前次调用 onScroll 以来沿 Y 轴翻滚的间隔
     */
    override fun onScroll(
        e1: MotionEvent, e2: MotionEvent, distanceX: Float, distanceY: Float
    ): Boolean {
        return false
    }
    /**
     * 长按事情触发
     */
    override fun onLongPress(e: MotionEvent) {
    }
    /**
     * 用户快速滑动屏幕时触发,回来值表明事情是否被处理
     * @param e1:开端快速滑动第一次按下操作,也便是 ACTION_DOWN
     * @param e2:触发当时 onFling 办法的 ACTION_MOVE
     * @param velocityX:X 轴上的移动速度,像素/秒
     * @param velocityY:Y 轴上的移动速度,像素/秒
     */
    override fun onFling(
        e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float
    ): Boolean {
        return false
    }
}

onFling 在用户快速拖动并抬起手指产生时履行,一般用来完成翻页作用,而 onScroll,只需手指移动就会履行,能够用来完成扩大缩小。

创立 GestureDetector 类的实例

val myGestureDetector = GestureDetector(context, MyGestureListener())

能够在 onTouch 办法中阻拦事情处理,将控制权交给 GestureDector

button.setOnTouchListener { _, event ->
    return@setOnTouchListener myGestureDetector.onTouchEvent(event)
}

但是这样需求重写这么多办法,显然不好用,所以一般开发中咱们都会使用 SimpleOnGestureListener,它结合了 OnGestureListener,OnDoubleTapListener 和 OnContextClickListener 中的一切办法,能够有挑选的去重写其间的办法。

class MyGestureListener : GestureDetector.SimpleOnGestureListener() {
    /**
     * 单击事情,假如只点击一次,系统等待一段时间后没有收到第二次点击则判别为单击
     * 与 onSingleTapUp 的差异在于:
     * onSingleTapUp 只需手抬起就会履行,假如是双击的话,onSingleTapConfirmed 不会履行。
     */
    override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
        return super.onSingleTapConfirmed(e)
    }
    /**
     * 双击事情
     */
    override fun onDoubleTap(e: MotionEvent): Boolean {
        return super.onDoubleTap(e)
    }
    /**
     * 双击之间产生的其他动作
     */
    override fun onDoubleTapEvent(e: MotionEvent): Boolean {
        return super.onDoubleTapEvent(e)
    }
    override fun onSingleTapUp(e: MotionEvent): Boolean {
        return super.onSingleTapUp(e)
    }
    override fun onLongPress(e: MotionEvent) {
        super.onLongPress(e)
    }
}