阅读 85

Android Touch 事件分发感悟

本编意在指引入门,非技术性分析文章,以生活中的实例帮你更好的理解 touch 事件的传递。

回忆刚接触 touch 事件传递时,文章大多是源码分析,对于有基础的人来说,上手很容易,但对于我这种新手来说,心中有万马奔腾。。。

其实事件的分发可以转换成我们生活中的场景:

炎炎夏日,你和你爸爸都很热,现在在你爸爸手里有一根雪糕,那么这根雪糕怎么分、谁吃就可以演变成 Android touch 的事件分发。

我们先说说结果,雪糕无非就三种,你吃、你爸爸吃、都不吃

先说说你爸爸吃的情况:

  • 你爸爸不问你要不要,直接留下给自己,然后直接吃了。
  • 你爸爸问你要不要,你说不要,然后你爸爸吃了。

再说说你吃的情况:

  • 你爸爸问你要不要,你说要,你爸爸就给你吃了。
  • 你爸爸不问你要不要,但是你告诉你爸爸说你想吃,你爸爸虽然想吃,但还是给你了。

最后都不吃的情况:

  • 你爸爸不吃,问你吃不吃,你也不吃,这时候就没人吃了。

上面这个场景应该很容易理解,那么换到 Android 的 touch 事件分发中,

  • 雪糕 = touch 事件,被封装成 MotionEvent 对象中。
  • 你爸爸 = ViewGroup,父 View,有 dispatchTouchEvent(拿到雪糕)、onInterceptTouchEvent(不问你,留下给自己)、onTouch(吃雪糕)三个方法。
  • 你 = View,子 View,有 dispatchTouchEvent(拿到雪糕)、onTouch(吃雪糕)两个方法。

通过这个场景,大家将方法带入,是不是就很容易理解了呢?

疑问一:为什么子 View 没有 onInterceptTouchEvent 这个呢?

因为你已经是最后一个了,不用问谁了,直接决定吃不吃就可以了。

疑问二:你吃里面的第二种情况,为什么你爸爸都决定自己吃了,但为什么只要你说你想吃,他就会给你呢?

其实你爸爸能否吃到雪糕的决定权还是在你的手里,

伪代码:

if( 孩子是否想吃 || !爸爸是否留下){
    给孩子吃
}else{
    爸爸吃
}
复制代码

默认孩子不想吃(false),爸爸不留下(false),

if( false || !false){
    给孩子吃
}else{
    爸爸吃
}
复制代码

正常的情况下都会给孩子吃, 当爸爸留下(true)时,

if( false || !true){
    给孩子吃
}else{
    爸爸吃
}
复制代码

就走到了 else 中,也就是爸爸吃。

在 Android 中我们可以通过 requestDisallowInterceptTouchEvent(孩子是否想吃)这个方法改变孩子是否想吃的值,当孩子调用requestDisallowInterceptTouchEvent方法返回 true 时,无论爸爸是否想吃,都会走到孩子想吃中,也就是最终结果是你吃。

if( true || !爸爸是否留下){
    给孩子吃
}else{
    爸爸吃
}
复制代码

我们的终极伪代码:

// 标记子 view 请求是否不拦截(孩子是否想吃,默认不想)
boolean disallowIntercept = false;
// 调用 dispatchTouchEvent(爸爸拿来了一根雪糕)
public boolean dispatchTouchEvent(MotionEvent ev) {
    // 标记事件是否被消费掉(雪糕是否被吃)
    boolean consume = false; 
    // 判断是否拦截事件(雪糕给不给孩子)
    if ( disallowIntercept || !onInterceptTouchEvent(ev)) {
      // 若拦截,则将该事件交给当前View进行处理
      // 即调用onTouchEvent 方法去处理点击事件(爸爸吃雪糕)
        consume = onTouchEvent (ev) ;
    } else {
      // 若不拦截,则将该事件传递到下层,即下层元素的dispatchTouchEvent就会被调用,直到点击事件被最终处理为止(孩子吃或者不吃)
      consume = child.dispatchTouchEvent (ev) ;
    }
    // 最终返回通知 该事件是否被消费(吃或不吃)
    return consume;
}
// 子 view 可以通过调用此方法 改变 disallowIntercept 的值
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept){
    this.disallowIntercept = disallowIntercept;
}
复制代码

这里有几篇非常好的讲解:

文章分类
Android
文章标签