Android事件流程详解
网络上有不少博客讲述了android的事件分发机制和处理流程机制,但是看过千遍,总还是觉得有些迷迷糊糊,因此特地抽出一天事件来亲测下,向像我一样的广大入门程序员详细讲述android事件背后的故事,话不多说,上干货。
android整个事件流程主要牵扯到dispatchTouchEvent(),onInterceptTouchEvent(),
onTouchEvent()这三个方法,下表来说明这三个方法的功能和分布场景:
表1:
方法名称 | 功解解说 | Activity | ViewGroup | View |
dispatchTouchEvent() | 事件分发 | YES | YES | YES |
onInterceptTouchEvent() | 事件拦截 | NO | YES | NO |
onTouchEvent() | 事件处理 | YES | YES | YES |
先来分析下Touch事件:所有Touch事件发生时会调用当前Activity的dispatchTouchEvent()方
法来分发事件,Activity的dispatchTouchEvent()方法最终会调用PhoneWindow类中的
superDispatchTouchEvent方法,最终逻辑就是该activity会调用ViewGroup类中的
dispatchTouchEvent()进行隧道式分发事件(按布局元素由外向内分发),如本案例中的分发流程
为TouchTraining -> TouchViewGroup -> TouchView,需要注意的是,当你在Activit中的
dispatchTouchEvent()中直接返回具体的布尔值(无论是true还是false),Touch事件直接会被
消费在该方法中,不会再进行下来的事件分发流程,因此必须在activity的dispatchTouchEvrent
返回super.dispatchTouchEvent()来进行事件分发流程。下面进行案例说明分析:
首先在定义自己的View和ViewGroup,重写表1它们各自支持的事件流程方法,我这里自定ViewGroup
继承的是LinearLayout(只要继承的是ViewGroup都一样),然后分别在Activity的布局文件中加入自定
义的控件,接着在Activity中也重写它支持的事件流程方法。
activity_touchtrain.xml
TouchView(自定义View类)
@Override public boolean dispatchTouchEvent(MotionEvent event) { Log.e(TAG, "dispatchTouchEvent分发事件"+ TouchEventUtil.getTouchAction(event.getAction())); return super.dispatchTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent ev) { Log.e(TAG, "onTouchEvent处理事件"+ TouchEventUtil.getTouchAction(ev.getAction())); return super.onTouchEvent(ev); }
TouchViewGroup(自定义ViewGroup类)
@Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.e(TAG, "dispatchTouchEvent分发事件" + TouchEventUtil.getTouchAction(ev.getAction())); return super.dispatchTouchEvent(ev); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { Log.e(TAG, "onInterceptTouchEvent拦截事件"+ TouchEventUtil.getTouchAction(ev.getAction())); return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent ev) { Log.e(TAG, "onTouchEvent处理事件"+ TouchEventUtil.getTouchAction(ev.getAction())); return super.onTouchEvent(ev); }
TouchTraining(Activity)
@Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.e(TAG, "dispatchTouchEvent分发事件"+ TouchEventUtil.getTouchAction(ev.getAction())); return super.dispatchTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent ev) { Log.e(TAG, "onTouchEvent处理事件"+ TouchEventUtil.getTouchAction(ev.getAction())); return super.onTouchEvent(ev); }
TouchEventUtils(工具类,获取当前事件类型)
public static String getTouchAction(int actionId) { String actionName = "Unknow:id=" + actionId; switch (actionId) { case MotionEvent.ACTION_DOWN: actionName = "ACTION_DOWN"; break; case MotionEvent.ACTION_MOVE: actionName = "ACTION_MOVE"; break; case MotionEvent.ACTION_UP: actionName = "ACTION_UP"; break; case MotionEvent.ACTION_CANCEL: actionName = "ACTION_CANCEL"; break; case MotionEvent.ACTION_OUTSIDE: actionName = "ACTION_OUTSIDE"; break; } return actionName; }
案例分析
方案1
条件 |
|||
控件名称 | dispatchTouchEvent返回值 | onInterceptTouchEvent 返回值 | onTouchEvent 返回值 |
TouchTraining |
super.dispatchTouchEvent(ev) | ---- | super.onTouchEvent(ev) |
TouchViewGroup | false | super.onInterceptTouchEvent(ev) | super.onTouchEvent(ev) |
TouchView | super.dispatchTouchEvent(ev) | ---- | super.onTouchEvent(ev) |
运行结果:
com.training.cj.mytraining E/TouchTraining: dispatchTouchEvent分发事件ACTION_DOWN
com.training.cj.mytraining E/TouchViewGroup: dispatchTouchEvent分发事件ACTION_DOWN
com.training.cj.mytraining E/TouchTraining: onTouchEvent处理事件ACTION_DOWN
com.training.cj.mytraining E/TouchTraining: dispatchTouchEvent分发事件ACTION_UP
com.training.cj.mytraining E/TouchTraining: onTouchEvent处理事件ACTION_UP
结果分析:Touch事件开始,调用TouchTraining的dispatchTouchEvent把事件分发TouchViewGroup
的dispatchTouchEvent,TouchViewGroup的dispatchTouchEvent返回false,事件停止向下传递,同
时事件并没有消费,但由于该事件来自TouchTraining(Activity),所以最终返回给TouchTraining
的onTouchEvent进行消费。
方案2
条件 |
|||
控件名称 | dispatchTouchEvent返回值 | onInterceptTouchEvent 返回值 | onTouchEvent 返回值 |
TouchTraining |
super.dispatchTouchEvent(ev) | ---- | super.onTouchEvent(ev) |
TouchViewGroup | true | super.onInterceptTouchEvent(ev) | super.onTouchEvent(ev) |
TouchView | super.dispatchTouchEvent(ev) | ---- | super.onTouchEvent(ev) |
运行结果:
com.training.cj.mytraining E/TouchTraining: dispatchTouchEvent分发事件ACTION_DOWN
com.training.cj.mytraining E/TouchViewGroup: dispatchTouchEvent分发事件ACTION_DOWN
com.training.cj.mytraining E/TouchTraining: dispatchTouchEvent分发事件ACTION_UP
com.training.cj.mytraining E/TouchViewGroup: dispatchTouchEvent分发事件ACTION_UP
结果分析:Touch事件由TouchTraining的dispatchTouchEvent不断向TouchViewGroup分发,
TouchViewGroup的dispatchTouchEvent返回true,TouchViewGroup在dispatchTouchEvent中不断消
费来自TouchTraining的dispatchTouchEvent分发的事件。
方案3
条件 |
|||
控件名称 | dispatchTouchEvent返回值 | onInterceptTouchEvent 返回值 | onTouchEvent 返回值 |
TouchTraining |
super.dispatchTouchEvent(ev) | ---- | super.onTouchEvent(ev) |
TouchViewGroup | super.onInterceptTouchEvent(ev) | true | super.onTouchEvent(ev) |
TouchView | super.dispatchTouchEvent(ev) | ---- | super.onTouchEvent(ev) |
运行结果:
com.training.cj.mytraining E/TouchTraining: dispatchTouchEvent分发事件ACTION_DOWN
com.training.cj.mytraining E/TouchViewGroup: dispatchTouchEvent分发事件ACTION_DOWN
com.training.cj.mytraining E/TouchViewGroup: onInterceptTouchEvent拦截事件ACTION_DOWN
com.training.cj.mytraining E/TouchViewGroup: onTouchEvent处理事件ACTION_DOWN
com.training.cj.mytraining E/TouchTraining: onTouchEvent处理事件ACTION_DOWN
com.training.cj.mytraining E/TouchTraining: dispatchTouchEvent分发事件ACTION_UP
com.training.cj.mytraining E/TouchTraining: onTouchEvent处理事件ACTION_UP
结果分析:Touch事件发生,调用TouchTraining的dispatchTouchEvent分发事件到TouchViewGroup
的dispatchTouchEvent,TouchViewGroup的dispatchTouchEvent返回super.onInterceptTouchEvent(ev)进
行事件分发,事件向下传递给TouchViewGroup的onInterceptTouchEvent,TouchViewGroup的onInterceptTouchEvent返回true,事件被拦截并传递给TouchViewGroup的onTouchEvent进行消费,TouchViewGroup的onTouchEvent返回super.dispatchTouchEvent(ev),对Touch事件未消费并返回给上级控件的onTouchEvent进行消费,由于TouchViewGroup的Touch事件来自TouchTraining,所以最后 由TouchTraining的onTouchEvent进行消费。
方案4
条件 |
|||
控件名称 | dispatchTouchEvent返回值 | onInterceptTouchEvent 返回值 | onTouchEvent 返回值 |
TouchTraining |
super.dispatchTouchEvent(ev) | ---- | super.onTouchEvent(ev) |
TouchViewGroup | super.onInterceptTouchEvent(ev) | false | super.onTouchEvent(ev) |
TouchView | super.dispatchTouchEvent(ev) | ---- | super.onTouchEvent(ev) |
运行结果:
com.training.cj.mytraining E/TouchTraining: dispatchTouchEvent分发事件ACTION_DOWN
com.training.cj.mytraining E/TouchViewGroup: dispatchTouchEvent分发事件ACTION_DOWN
com.training.cj.mytraining E/TouchViewGroup: onInterceptTouchEvent拦截事件ACTION_DOWN
com.training.cj.mytraining E/TouchView: dispatchTouchEvent分发事件ACTION_DOWN
com.training.cj.mytraining E/TouchView: onTouchEvent处理事件ACTION_DOWN
com.training.cj.mytraining E/TouchViewGroup: onTouchEvent处理事件ACTION_DOWN
com.training.cj.mytraining E/TouchTraining: onTouchEvent处理事件ACTION_DOWN
com.training.cj.mytraining E/TouchTraining: dispatchTouchEvent分发事件ACTION_UP
com.training.cj.mytraining E/TouchTraining: onTouchEvent处理事件ACTION_UP
结果分析:Touch事件发生,调用TouchTraining的dispatchTouchEvent分发事件到TouchViewGroup的dispatchTouchEvent,TouchViewGroup的dispatchTouchEvent返回super.onInterceptTouchEvent(ev),继续分发向下传递事件到TouchViewGroup的onInterceptTouchEvent,TouchViewGroup的onInterceptTouchEvent返回false,继续分发向下传递事件到TouchView的dispatchTouchEvent,TouchView的dispatchTouchEvent返回super.dispatchTouchEvent(ev),继续分发向下传递事件到TouchView的onTouchEvent,TouchView的onTouchEvent返回super.onTouchEvent(ev),事件没有消费,返回给上级TouchViewGroup的onTouchEvent进行消费,TouchViewGroup的
onTouchEvent返回super.onTouchEvent(ev),继续返回给上级TouchTraining的onTouchEvent进行消费。
方案5:
条件 |
|||
控件名称 | dispatchTouchEvent返回值 | onInterceptTouchEvent 返回值 | onTouchEvent 返回值 |
TouchTraining |
super.dispatchTouchEvent(ev) | ---- | super.onTouchEvent(ev) |
TouchViewGroup | super.onInterceptTouchEvent(ev) | false | super.onTouchEvent(ev) |
TouchView | true | ---- | super.onTouchEvent(ev) |
运行结果:
com.training.cj.mytraining E/TouchTraining: dispatchTouchEvent分发事件ACTION_DOWN
com.training.cj.mytraining E/TouchViewGroup: dispatchTouchEvent分发事件ACTION_DOWN
com.training.cj.mytraining E/TouchViewGroup: onInterceptTouchEvent拦截事件ACTION_DOWN
com.training.cj.mytraining E/TouchView: dispatchTouchEvent分发事件ACTION_DOWN
com.training.cj.mytraining E/TouchView: onTouchEvent处理事件ACTION_DOWN
com.training.cj.mytraining E/TouchTraining: dispatchTouchEvent分发事件ACTION_UP
com.training.cj.mytraining E/TouchViewGroup: dispatchTouchEvent分发事件ACTION_UP
com.training.cj.mytraining E/TouchViewGroup: onInterceptTouchEvent拦截事件ACTION_UP
com.training.cj.mytraining E/TouchView: dispatchTouchEvent分发事件ACTION_UP
com.training.cj.mytraining E/TouchView: onTouchEvent处理事件ACTION_UP
结果分析:Touch事件发生,调用TouchTraining的dispatchTouchEvent分发事件到TouchViewGroup的dispatchTouchEvent,该方法返回super.dispatchTouchEvent(ev),继续分发事件到TouchViewGroup的onInterceptTouchEvent,该方法返回false,继续分发事件到TouchView的dispatchTouchEvent,该方法返回super.dispatchTouchEvent(ev),继续分发事件到onTouchEvent,由于onTouchEvent返回true,表示消费了事件,Touch事件终止。
好了,本期的Touch事件分析到这里就结束了,另外,还有一个小细节需要注意的是,在View和View Group中的onTouchEvent方法默认返回false,View Group中的onInterceptTouchEvent也默认返回false。所以上面5种方案描述出了所有的Touch事件传递可能。了解Touch事件的分发和消费机制,更有利于我们自定义控件,当然我们在自定义控件时,尽量不要重写dispatchTouchEvent这个方法。
本贴参考博客:http://www.cnblogs.com/sunzn/archive/2013/05/10/3064129.html