View 事件分发机制,看这一篇就够了,不看绝对血亏

public boolean dispatchTouchEvent(MotionEvent ev) {
int x = (int) ev.getRawX();
int y = (int) ev.getRawY();
int dealtX = 0;
int dealtY = 0;

switch (ev.getAction()) {
    case MotionEvent.ACTION_DOWN:
        dealtX = 0;
        dealtY = 0;
        // 保证子View能够接收到Action_move事件
        getParent().requestDisallowInterceptTouchEvent(true);
        break;
    case MotionEvent.ACTION_MOVE:
        dealtX += Math.abs(x - lastX);
        dealtY += Math.abs(y - lastY);
        Log.i(TAG, "dealtX:=" + dealtX);
        Log.i(TAG, "dealtY:=" + dealtY);
        // 这里是够拦截的判断依据是左右滑动,读者可根据自己的逻辑进行是否拦截
        if (dealtX >= dealtY) {
            getParent().requestDisallowInterceptTouchEvent(true);
        } else {
            getParent().requestDisallowInterceptTouchEvent(false);
        }
        lastX = x;
        lastY = y;
        break;
    case MotionEvent.ACTION_CANCEL:
        break;
    case MotionEvent.ACTION_UP:
        break;

}
return super.dispatchTouchEvent(ev);

}


更多细节,可以查看我的这篇文章,里面有详细介绍哦 [ViewPager,ScrollView 嵌套ViewPager滑动冲突解决]( )

* * *

View 双击,多击事件是怎么实现的
==================

实现之前,我们首先来阐述一下思路,怎样实现双击事件,正所谓,授人以鱼不如授人以渔。

**单击**:用户点击一次之后,一段时间之内不再点击

**双击**;用户点击一次之后,一段时间之内再次点击

实现思路

1.  我们监听 onTouch 事件,在 ACTION\_DOWN 的时候,点击次数 clickCount +1;
2.  同时,在 ACTION\_DOWN 的时候,延时一段时间,执行相应的 Runnable 任务,这里我们用 handler 的 postDelayed 实现
3.  在延时任务执行的时候,我们根据点击的次数,进行单击或者多级的回调,最后,记得重置点击次数,以及移除延时任务

open class MyDoubleTouchListener(private val myClickCallBack: MyClickCallBack) : OnTouchListener {

private var clickCount = 0 //记录连续点击次数
private val handler: Handler = Handler()

interface MyClickCallBack {
    fun oneClick() //点击一次的回调
    fun doubleClick() //连续点击两次的回调
}

override fun onTouch(v: View, event: MotionEvent): Boolean {
    if (event.action == MotionEvent.ACTION_DOWN) {
        clickCount++
        handler.postDelayed({
            if (clickCount == 1) {
                myClickCallBack.oneClick()
            } else if (clickCount == 2) {
                myClickCallBack.doubleClick()
            }
            handler.removeCallbacksAndMessages(null)
            //清空handler延时,并防内存泄漏
            clickCount = 0 //计数清零
        }, timeout.toLong()) //延时timeout后执行run方法中的代码
    }
    return false //让点击事件继续传播,方便再给View添加其他事件监听
}

companion object {
    private const val TAG = "MyClickListener"
    private val timeout = ViewConfiguration.getDoubleTapTimeout() //双击间四百毫秒延时

    init {
        Log.i(TAG, "timeout is $timeout ")
    }
}

}


三击事件
----

三级事件呢,其实也很简单,我们直接判断在指定时间间隔内点击的次数即可

open class MyMultiTouchListener(private val myClickCallBack: MyClickCallBack) : OnTouchListener {

private var clickCount = 0 //记录连续点击次数
private val handler: Handler = Handler()

interface MyClickCallBack {
    fun oneClick() //点击一次的回调
    fun doubleClick() //连续点击两次的回调
    fun threeClick() // 连续点击三次的回调
}

override fun onTouch(v: View, event: MotionEvent): Boolean {
    if (event.action == MotionEvent.ACTION_DOWN) {
        clickCount++
        handler.postDelayed({
            if (clickCount == 1) {
                myClickCallBack.oneClick()
            } else if (clickCount == 2) {
                myClickCallBack.doubleClick()
            } else if (clickCount == 3) {
                myClickCallBack.threeClick()
            }
            handler.removeCallbacksAndMessages(null)
            //清空handler延时,并防内存泄漏
            clickCount = 0 //计数清零
        }, timeout.toLong()) //延时timeout后执行run方法中的代码
    }
    return false //让点击事件继续传播,方便再给View添加其他事件监听
}

companion object {
    private const val TAG = "MyClickListener"
    private val timeout = 600 //双击间四百毫秒延时

    init {
        Log.i(TAG, "timeout is $timeout ")
    }
}

}


* * *

手势识别
====

在 Android 开发当中,几乎所有的事件都会与用户进行交互,而我们用得的最多的就是手势了。

我们知道当我们触摸屏幕的时候,会产生很多事件,比如 down,move,up, fling 事件等等。一些简单的处理,我们可以直接重写 View 的 onTouchEvent 方法,根据 View 的 MotionEvent 事件进行处理。

而 Google 为了方便开发者方便接入,提供了几个默认处理类,那就是 GestureDetector 和 ScaleGestureDetector。

GestureDetector这个类对外提供了两个接口和一个外部类 。 接口:OnGestureListener,OnDoubleTapListener

内部类:SimpleOnGestureListener,同时实现了 OnGestureListener,OnDoubleTapListener 接口,如果只想使用接口里面的某个方法,可以直接使用它,方便快捷。

讲解之前,我们向来看一下怎么使用

`GestureDetector(Context context, GestureDetector.OnGestureListener listener)`

GestureDetector 基本使用
--------------------

第一步,初始化 `GestureDetector` 对象

mDetector = GestureDetectorCompat(this, MyGestureListener())


可以看到有两个参数,第一个参数 context,第二个参数 OnGestureListener,我们可以直接实现 OnGestureListener 接口,也可以直接使用 `GestureDetector.SimpleOnGestureListener`

private class MyGestureListener : GestureDetector.OnGestureListener {

    private val TAG = "GestureDemoActivity"

    override fun onShowPress(e: MotionEvent?) {
        Log.d(TAG, "onShowPress: e is $e")
    }


    override fun onSingleTapUp(e: MotionEvent?): Boolean {
        Log.d(TAG, "onSingleTapUp: e is $e")
        return false
    }

    override fun onDown(event: MotionEvent): Boolean {
        Log.d(TAG, "onDown: $event")
        return true
    }

    override fun onFling(
            event1: MotionEvent,
            event2: MotionEvent,
            velocityX: Float,
            velocityY: Float
    ): Boolean {
        Log.d(TAG, "onFling: $event1 $event2")
        return false
    }

    override fun onScroll(e1: MotionEvent?, e2: MotionEvent?, distanceX: Float, distanceY: Float): Boolean {
        Log.d(TAG, "onScroll: distanceX is $distanceX,distanceY is $distanceY ")
        return false
    }

    override fun onLongPress(e: MotionEvent?) {
        Log.d(TAG, "onLongPress: e is $e")
    }
} 

第二步:设置双击监听

// 设置双击监听
mDetector.setOnDoubleTapListener(object : GestureDetector.OnDoubleTapListener {
override fun onDoubleTap(e: MotionEvent?): Boolean {
Log.d(TAG, “onDoubleTap: e is e”)
return false
}

        override fun onDoubleTapEvent(e: MotionEvent?): Boolean {
            Log.d(TAG, "onDoubleTapEvent: e is e")
            return false
        }

        override fun onSingleTapConfirmed(e: MotionEvent?): Boolean {
            Log.d(TAG, "onSingleTapConfirmed: e is e")
            return false
        }

    }) 

最后,重写 Activity 或者 View 的 onTouchEvent ,将事件交给 mDetector 处理。

通常会有两种写法,第一种是如果手势处理器处理了,直接返回 true,进行消费。否则,进行默认处理

override fun onTouchEvent(event: MotionEvent): Boolean {
return if (mDetector.onTouchEvent(event)) {
true
} else {
super.onTouchEvent(event)
}
}


第二种写法是直接在 onTouchEvent 方法中,直接调用 `mDetector.onTouchEvent(event)` 方法

override fun onTouchEvent(event: MotionEvent): Boolean {
mDetector.onTouchEvent(event)
return super.onTouchEvent(event)
}


第二种写法,一般不会影响当前 View 或者 Activity 事件的传递,在开发当中,有时候为了减少一些触摸事件的冲突,经常这样写。

ScaleGestureDetector 这里暂时不展开描述了了,写着写着,**发现好多呀,一个周末就这样过去,贼快,觉得对你有帮助的,请来个三连,点赞,收藏,转发
上一篇:Android:ScrollView停止滚动的监听


下一篇:View的事件体系——View的基础知识