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

一、引言

总觉得知识必须要总结,不然就算再熟悉的东西,一段时间不接触就容易遗忘;上次给一个朋友解释回调的时候就有深刻的体会,所以现在养成总结的习惯,而我觉得最直观的方法就是图解,所以就有了下面的图,通过这张图看透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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容