一、引言
总觉得知识必须要总结,不然就算再熟悉的东西,一段时间不接触就容易遗忘;上次给一个朋友解释回调的时候就有深刻的体会,所以现在养成总结的习惯,而我觉得最直观的方法就是图解,所以就有了下面的图,通过这张图看透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
网友评论
1.所谓 “往上回传”的意思应该是: 调用了某控件当前ontouchEvent,如果 返回false,就会调用父控件的ontouchEvent(ev)吧,以此类推,直到activity的 onTouchEvent(ev) ,我不太确定,是这样吧?
2. 当 dispatchTouchEvent 返回false的时候,本次事件就不处理,算是直接消费了,而且这个系列事件的后续部分(move,up),不会再传到当前控件。 好像有个结论是: 当 dispatchTouchEvent 返回false了,这个事件的后续部分就不会再传递到当前控件了。 对么?