美文网首页Android自定义View
最简单的Android事件分发讲解,dispatchTouchE

最简单的Android事件分发讲解,dispatchTouchE

作者: 蹦蹦跳跳猪猪侠 | 来源:发表于2020-07-18 15:53 被阅读0次

本文所得出的结论,均经过代码验证,请放心食用

Android View的事件分发是View知识点里面最复杂的,但不是最重要的,
我看了很多关于事件分发的文章,都是深入源码不能自拔,看得迷迷糊糊,
本文旨在屏蔽不必要的细节,简略的说说事件分发的大致思想,
可以作为事件分发的入门导读。

事件,指的是MotionEvent,
一般用户的一次触摸,能产生多个MotionEvent。
所谓View的事件分发过程,指的就是MotionEvent传递给View的过程。
说的再通俗一点就是,
MotionEvent从何而来?
MotionEvent要到哪个View去?
MotionEvent到哪个View是怎样决定的?

一.MotionEvent从何而来?

从Activity来,
所有事件都是从Activity开始传递的,
具体传递方向为:
Activity->Window->ViewGroup->View

二.MotionEvent要到哪个View去?

到消耗它的View去。
所有的View都有View.onTouchEvent(MotionEvent event)方法,
其返回结果表示是否消耗了事件,
当事件传递到这个方法,且这个方法返回了true,
那这个事件会被消耗,不再传递。

三.MotionEvent到哪个View是怎样决定的?

为屏蔽不必要的细节,在回答这个问题前,先说一个结论:
View的事件传递是U形的,
由父View传给子View,
子View不处理*,
就会传回给父View。

*:不处理指的是:
不消耗类型为MotionEvent.ACTION_DOWN的事件,
即在View.onTouchEvent(MotionEvent event)中,
对于类型为MotionEvent.ACTION_DOWN的事件返回false,
当然,只要不消耗类型为MotionEvent.ACTION_DOWN的事件,
后续事件也不会传给这个View了

事件传递的过程呈u型

MotionEvent传到哪个View由这四个方法来决定:

1).View.onTouchEvent(MotionEvent event)

上面第二个问题我们已经见过它,这个方法用于消耗事件,
返回结果表示是否消耗了事件。
一般情况下给View设置了点击回调,这个方法就会返回true。

2).ViewGroup.onInterceptTouchEvent(MotionEvent ev)

ViewGroup专有方法,返回结果表示是否拦截点击事件

3).ViewGroup.dispatchTouchEvent(MotionEvent ev)

4).View.dispatchTouchEvent(MotionEvent ev)

方法3和方法4是同一个方法,
只不过一个在ViewGroup,一个在View,
ViewGroup继承自View,重写了方法4
方法4总是会默认回调方法1,

至于方法3
重点来了!!!

方法3是事件分发逻辑的核心,
其内容阐述了四个方法间的关系,
可用以下伪代码表示:

public boolean dispatchTouchEvent(MotionEvent ev){//方法3
    boolean consume = false;//表示是否消耗事件
    if( !onInterceptTouchEvent(ev) ){//方法2
        ......
         /*
        上面省略号代表的代码大致写的是,
        寻找一个能处理这个事件View,并赋值给child
        如果没有这样的View,child为null
        */
        if (child != null) {
            consume = child.dispatchTouchEvent(ev)//方法3
        }
    }

    if(child == null){
            /*
            super.dispatchTouchEvent(event)
            会默认回调View.onTouchEvent(MotionEvent event),即方法1
            */
            consume = super.dispatchTouchEvent(event)//方法4
    }
    retutn consume;
}

代码逻辑非常简单:
先判断自身需不需要拦截事件,
由方法2决定,
方法2默认返回false,
我们可以通过重写方法2来控制是否拦截事件。

如果拦截,那么会跳过寻找child的代码,
child为null,会执行super.dispatchTouchEvent(event),
而super.dispatchTouchEvent(event),就是方法4,
方法4总是会默认调用方法1,
即自身处理事件,事件会传到自身的onTouchEvent方法中。

如果不拦截,就寻找有没有能处理事件的子View,
如果有,
就会调用子View的dispatchTouchEvent(event)方法,
即子View处理事件,事件会传到子View的onTouchEvent方法中。
至于什么样的子View能处理事件,其实上面结论的括号内容中已经提到:
消耗类型为MotionEvent.ACTION_DOWN的事件,
即为处理,后续事件会继续传给这个View,
不消耗类型为MotionEvent.ACTION_DOWN的事件,
即为不处理,后续事件不会传给这个View。

可以看到事件在向下传递的过程中,
是通过层层调用dispatchTouchEvent(ev)向下传递的。
还值得一提的是,
方法2其默认返回值为false,
也就是说,ViewGroup总是默认把事件传给子View。

事件分发的大致流程就是这样,
父传子,子不处理就传回给父,至于子处不处理,
则看子View消不消耗类型为MotionEvent.ACTION_DOWN的事件,
而父传子的过程中,父View不想往下传了,想自己处理事件
可以通过重写onInterceptTouchEvent方法返回true实现。

end

相关文章

网友评论

    本文标题:最简单的Android事件分发讲解,dispatchTouchE

    本文链接:https://www.haomeiwen.com/subject/jkjxkktx.html