美文网首页Android自定义view相关流程
一张图看清Android事件传递机制

一张图看清Android事件传递机制

作者: 小七在简书 | 来源:发表于2015-08-30 19:26 被阅读6959次

    一、引言

    总觉得知识必须要总结,不然就算再熟悉的东西,一段时间不接触就容易遗忘;上次给一个朋友解释回调的时候就有深刻的体会,所以现在养成总结的习惯,而我觉得最直观的方法就是图解,所以就有了下面的图,通过这张图看透Android事件传递机制;
    PS:有时候自己的理解可能也存在问题,所以通过这种形式能够得到大家的审查和修改意见,在这里先谢谢大家了!


    二、就是这张图(这张图如果看不清楚,复制或保存本地,使用图片查看器放大查看;或者点击链接查看原图)

    查看高清原图:一张图看透Android事件传递机制


    三、图片局部细节详解

    1.Touch事件的传入 :

    开始由UserActivity(用户自定义的Activity)接收到事件,这个类继承自Activity,没有onInterceptTouchEvent方法(为什么没有?谁能给我一个合理的解释吗?)
    事件传递到disPatchTouchEvent,这里默认的是调用父类的disPatchTouchEvent,查看父类的源码可以看到

    /**
     * Called to process touch screen events.  You can override this to
     * intercept all touch screen events before they are dispatched to the
     * window.  Be sure to call this implementation for touch screen events
     * that should be handled normally.
     * 
     * @param ev The touch screen event.
     * 
     * @return boolean Return true if this event was consumed.
     */
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }
    

    这里的第一个判断条件中方法的执行为为空实现:
    第二个判断中的判断条件是有窗体事件分配决定的,窗体默认的事件分发机制,再往里面看,如下代码

    /**
     * Used by custom windows, such as Dialog, to pass the touch screen event
     * further down the view hierarchy. Application developers should
     * not need to implement or call this.
     *
     */
    public abstract boolean superDispatchTouchEvent(MotionEvent event);
    

    可以理解为Android系统自己定义的事件分发机制,上面说明了应用开发者不需要去实现或者调用这个方法。我理解的是系统已经为你准备好了普通窗口事件该如何分发,不需要你再手动去修改这块代码了!如果你不按照我的方法来做,那么好吧,后面的事你都自己干吧,就是这么任性。所以在UserActivity中手动修改dispatchTouchEvent方法的返回值,就会使本次事件被消费掉,不再向里层传递。
    顺带看看onTouchEvent方法吧:

    /**
     * Called when a touch screen event was not handled by any of the views
     * under it.  This is most useful to process touch events that happen
     * outside of your window bounds, where there is no view to receive it.
     * 
     * @param event The touch screen event being processed.
     * 
     * @return Return true if you have consumed the event, false if you haven't.
     * The default implementation always returns false.
     */
    public boolean onTouchEvent(MotionEvent event) {
        if (mWindow.shouldCloseOnTouch(this, event)) {
            finish();
            return true;
        }
        
        return false;
    }
    

    从上面的说明可以看出,当触屏事件没有被内层所有的view处理的时候才会被调用;当处理你窗体外没有任何view处理的Touch事件的时候最有用。
    从返回值说明可以看出,默认返回值通常为false,所以一般如果事件不被子view消费,事件一般都会被舍弃掉

    2.从Activity中传入外层的ViewGroup中

    经过了UserActivty后传入到ViewParent中来,第一个走的肯定还是dispatchTouchEvent,依然,如果手动修改dispathTouchEvent的返回值,本次事件就到此为止。如果使用父类(ViewGroup)的事件分发机制,那么事件就会首先传递到onInterceptToucEvent中来;(这部分里面代码太多,我想你也没兴趣看;万一你是个Geek,那你也可以自己去扒一扒源码,自己阅读一下,这里就不再详细介绍了,本文就是为了言简意赅的说明Touch事件的传递机制,所以见谅)

    • 在onInterceptTouchEvent中,就会询问你“你想不想拦截本次事件啊?”
      |--"想"(return true),那么事件就会进入自己的onTouchEvent中执行
      那么事件就会到了onTouchEvent中,这时系统就会问你:“你想不想处理这次点击事件啊?”
      |--"想"(return true)那么本次事件被消费掉了
      |--"不想"(return false)那么他就会认为你里层的子控件都不想接收本次事件,那他直接把本次事件往回传递(传回UserActivty中
      去处理)。需要注意的是,如果本次事 件是DOWN事件,那么本次的MOVE、UP也将不会传递到ViewParent中来了,注意是本次哦,仅仅只是指你一次
      按下到抬起这一套动作,还有第二次,第三次,还是要来的。
      |--"你自己说了算,按照你的想法去做吧!"(return super.onTouchEvent(ev);)那就要交给ViewParent的父类去决定了,不过一般
      ViewGruop中都不会去处理事件,即一 般返回false
      |--"不想"(return false),既然你不想拦截,那我就去问问你里面的人想不想处理吧,事件就会向里层传递,即下面的ViewChild
      |--"你自己说了算,按照你的想法去做吧!"(return super.onTouchEvent(ev);),系统当然是想有人来消费这个事件了,既然你
      让我自己决定,那我也想向你里面的子控件问问啊!

    3.事件传递到了ViewChild中

    这里当然是最里层控件了.
    dipatchTouchEvent:还是和上面的一样,还是决定是否分发事件,系统的逻辑会自动运行到自己onTouchEvent方法中,如果你主动干预,决定是否分发(手动更改返回值),事件到这里就会终结
    onInterceptTouchEvent:最里层控件没有这个方法,既然是最里层的控件,自然就不需要询问你是否需要拦截这个事件了,因为你拦不拦截,事件到这里就算是走到了路的尽头(之所以称为路的尽头,是因为还可以走走回头路的,哈哈!)所以这里没有啊onInterceptTouchEvent方法
    onTouchevent:逻辑和之前的一样,问你是否消费本次事件,
    |--return true消费掉本次事件,事件结束;
    |--return false不消费,本次事件回传到外一层控件,同样,如果是一次DOWN事件,本次的MOVE和UP将不会再传递到这里;
    |--return super.onTouchEvent(event)是否消费本次事件就要看自己控件类型了,我的例子中是TextView类型,就是不消费的;

    4.事件回传

    事件回传,差不多是同一种情况,就是当前控件中的onTouchEvent返回值为false(默认的super.onTouchEvent方法一般也是返回false),可以理解为:
    |--return false:事件都交到你手里了,而且你明确说了你不要,那我只能往回传了;
    |--return super.onTouchEvent(event):如果你不告诉我你要不要处理,我只能默认你不处理,也就往上一层传递了
    当到达了最顶层,我的Demo程序中是MainActivity,默认不处理onTouchEvent,所以如果回传到顶层,事件会调用Activity中的onTouchEvent
    本次分析到这里就算结束了,感觉有些地方的理解还存在问题,欢迎各位大神指点,先谢过了!
    本次使用的图片资源和源代码,如需要,请自行下载:
    链接:http://pan.baidu.com/s/1kTH59sj密码:1m5n

    相关文章

      网友评论

      • 93bd00a71476:神一样的图,赞
      • Aisen:楼主的图片用什么软件画的
      • Alex_Code:干的漂亮
      • 捡淑:mark
        f67599b78199:求正面高清大图
      • 樱花下等待:好图收藏!
      • 52615d0e22fe:很喜欢,简书的大神就是多,希望多分享一些纪实
      • 代码在码我:辛苦,图片有点不清 :smiley:
      • flyrun1: 写的挺好的,很喜欢。
        1.所谓 “往上回传”的意思应该是: 调用了某控件当前ontouchEvent,如果 返回false,就会调用父控件的ontouchEvent(ev)吧,以此类推,直到activity的 onTouchEvent(ev) ,我不太确定,是这样吧?
        2. 当 dispatchTouchEvent 返回false的时候,本次事件就不处理,算是直接消费了,而且这个系列事件的后续部分(move,up),不会再传到当前控件。 好像有个结论是: 当 dispatchTouchEvent 返回false了,这个事件的后续部分就不会再传递到当前控件了。 对么?

      本文标题:一张图看清Android事件传递机制

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