Android事件分发机制(1)——从源码角度分析
前言
这两天在写一个自定义控件,继承了ViewGroup,需要对子View做一个事件分派的控制,之前用的onTouchEvent事件比较多,对于android的事件分发体系很早之前有了解过,很久没用就忘记了,今天在这里记录总结下。
首先放上一张事件分发流程图(之前摘自网上的一篇博客,忘了哪一篇了)
从上图我们可以看到事件的分发流程是从 Activity(Window)——>ViewGroup——>View,主要有三个关键函数:dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent
解析
事件分发:public boolean dispatchTouchEvent(MotionEvent ev)
首先用Activity捕获到用户的触摸事件,Activity本身并没有拦截事件,所以当Activity捕获到事件后将其传递到最外层的View的dispatchTouchEvent方法,t进行事件分发,返回值说明:
1)return true:当前View消费所有的事件,后续的move和up事件都交给自己处理;
2)return false:停止分发,将事件交给上层的View的onTouchEvent进行处理,
3)调用super的dispatchTouchEvent:将事件交给本View的onInterceptTouchEvent进行处理
事件拦截:public boolean onInterceptTouchEvent(MotionEvent ev)
用于拦截事件,决定事件的传递方向,此事件是在ViewGroup中定义的,子View没有此事件
1)return true: 拦截该事件,将事件交给自身的onTouchEvent处理;
2)return false\super.onInterceptTouchEvent:不拦截,将事件下派给子View ,由子View的dispatchTouchEvent进行事件处理
事件响应:public boolean onTouchEvent(MotionEvent ev)
1)return true:表示View消费了此事件,后续的move和up都交给该view处理
2)return false\super:不消费此事件,将事件层层递交给父级的onTouchEvent处理,直到某一父级return true,否则最终交给Activity处理:
实例
1)、不消费,不拦截:下面通过实例来看看事件的传递过程,新建一个Group1类,继承自LinearLayout,分别重新上面三个方法:
@Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.d("Dispatch","Group 1 dispatchTouchEvent eventType: " + ACTIONS[ev.getAction()]); return super.dispatchTouchEvent(ev); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { Log.d("Dispatch","Group 1 onInterceptTouchEvent eventType: " + ACTIONS[ev.getAction()]); return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { Log.d("Dispatch","Group 1 onTouchEvent eventType: " + ACTIONS[event.getAction()]); return super.onTouchEvent(event); }
然后新建一个View1类,继承自TextView,重写dispatchTouchEvent和onTouchEvent:
@Override public boolean dispatchTouchEvent(MotionEvent event) { Log.d("Dispatch","View1 1 dispatchTouchEvent eventType: " + ACTIONS[event.getAction(])); return super.dispatchTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { Log.d("Dispatch","View1 1 onTouchEvent eventType: " + ACTIONS[event.getAction()]); return super.onTouchEvent(event); }
然后在MainActivity重写dispatchTouchEvent和onTouchEvent:
@Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.d("Dispatch","MainActivity dispatchTouchEvent eventType: " + ACTIONS[ev.getAction()]); return super.dispatchTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { Log.d("Dispatch","MainActivity onTouchEvent eventType: " + ACTIONS[event.getAction()]); return super.onTouchEvent(event); }
运行项目,得到打印的信息为:
MainActivity dispatchTouchEvent eventType: ACTION_DOWN Group 1 dispatchTouchEvent eventType: ACTION_DOWN Group 1 onInterceptTouchEvent eventType: ACTION_DOWN View1 1 dispatchTouchEvent eventType: ACTION_DOWN View1 1 onTouchEvent eventType: ACTION_DOWN Group 1 onTouchEvent eventType: ACTION_DOWN MainActivity onTouchEvent eventType: ACTION_DOWN MainActivity dispatchTouchEvent eventType: ACTION_MOVE MainActivity onTouchEvent eventType: ACTION_MOVE MainActivity dispatchTouchEvent eventType: ACTION_UP MainActivity onTouchEvent eventType: ACTION_UP
通过结果可以知道,由于在各个环节中,没有任何一个view拦截或者消费了事件,所以刚开始的Down事件由Activity.dispatchTouchEvent——>Group1.dispatchTouchEvent——>Group1.onInterceptTouchEvent——>View1.dispatchTouchEvent——>View1.onTouchEvent——>Group1.onTouchEvent——>Activity.onTouchEvent,而后续因为在各个子View都没有消费此事件,后续的move和up事件都由Activity处理
2)、我们在Activity的dispatchTouchEvent返回true\false,看看结果怎样:
MainActivity dispatchTouchEvent eventType: ACTION_DOWN MainActivity dispatchTouchEvent eventType: ACTION_MOVE MainActivity dispatchTouchEvent eventType: ACTION_UP
Activity直接消费了此事件并且往下传(包括自身的onTouchEvent),
3)修改Group1的dispatchTouchEvent返回true,结果:
MainActivity dispatchTouchEvent eventType: ACTION_DOWN Group 1 dispatchTouchEvent eventType: ACTION_DOWN MainActivity dispatchTouchEvent eventType: ACTION_MOVE Group 1 dispatchTouchEvent eventType: ACTION_MOVE MainActivity dispatchTouchEvent eventType: ACTION_UP Group 1 dispatchTouchEvent eventType: ACTION_UP
在Group1的事件分派函数返回true,相当于该函数消费了该事件,并截断了事件的分派流程,后续的move和up都交给该View的dispatchTouchEvent处理
4)修改Group1的dispatchTouchEvent返回false,结果:
MainActivity dispatchTouchEvent eventType: ACTION_DOWN Group 1 dispatchTouchEvent eventType: ACTION_DOWN MainActivity onTouchEvent eventType: ACTION_DOWN MainActivity dispatchTouchEvent eventType: ACTION_MOVE MainActivity onTouchEvent eventType: ACTION_MOVE MainActivity dispatchTouchEvent eventType: ACTION_MOVE MainActivity onTouchEvent eventType: ACTION_MOVE MainActivity dispatchTouchEvent eventType: ACTION_UP MainActivity onTouchEvent eventType: ACTION_UP在Group1的事件分派函数返回false,事件没有往下分发,也没有消费,最终回到Activity的onTouchEvent处理
5)修改Group1的onInterceptTouchEvent返回true,结果:
MainActivity dispatchTouchEvent eventType: ACTION_DOWN Group 1 dispatchTouchEvent eventType: ACTION_DOWN Group 1 onInterceptTouchEvent eventType: ACTION_DOWN Group 1 onTouchEvent eventType: ACTION_DOWN MainActivity onTouchEvent eventType: ACTION_DOWN MainActivity dispatchTouchEvent eventType: ACTION_MOVE MainActivity onTouchEvent eventType: ACTION_MOVE MainActivity dispatchTouchEvent eventType: ACTION_UP MainActivity onTouchEvent eventType: ACTION_UP
由于在Group1的拦截函数中返回true,即是拦截了该事件,不再往下 派送,然后down事件转向Group1的onTouchEvent处理,因为Group1的onTouchEvent没有消费该事件,继续往上传给Activity的onTouchEvent,最后后续的move和up也由该事件处理。
6)修改Group1的onInterceptTouchEvent返回false,结果:
MainActivity dispatchTouchEvent eventType: ACTION_DOWN Group 1 dispatchTouchEvent eventType: ACTION_DOWN Group 1 onInterceptTouchEvent eventType: ACTION_DOWN View1 1 dispatchTouchEvent eventType: ACTION_DOWN View1 1 onTouchEvent eventType: ACTION_DOWN Group 1 onTouchEvent eventType: ACTION_DOWN MainActivity onTouchEvent eventType: ACTION_DOWN MainActivity dispatchTouchEvent eventType: ACTION_UP MainActivity onTouchEvent eventType: ACTION_UP
Down事件完整的传递了下来,又没有子View消费down事件,后续的事件交回给Activity处理
7)修改View1的Group1的onTouchEvent为true同时Group1的onInterceptTouchEvent返回true:
MainActivity dispatchTouchEvent eventType: ACTION_DOWN Group 1 dispatchTouchEvent eventType: ACTION_DOWN Group 1 onInterceptTouchEvent eventType: ACTION_DOWN Group 1 onTouchEvent eventType: ACTION_DOWN MainActivity dispatchTouchEvent eventType: ACTION_MOVE Group 1 dispatchTouchEvent eventType: ACTION_MOVE Group 1 onTouchEvent eventType: ACTION_MOVE MainActivity dispatchTouchEvent eventType: ACTION_MOVE Group 1 dispatchTouchEvent eventType: ACTION_MOVE Group 1 onTouchEvent eventType: ACTION_MOVE MainActivity dispatchTouchEvent eventType: ACTION_UP Group 1 dispatchTouchEvent eventType: ACTION_UP Group 1 onTouchEvent eventType: ACTION_UP
由于Group1拦截了down事件,所以事件不再往下传,在Group1的onTouchEvent消费了事件,所以事件也不再往上传,在这里有一点要注意,在第一次Group1拦截了down事件后,后续不会再调用onInterceptTouchEvent事件
view1的dispatchTouchEvent和onTouchEvent就不看了,基本上和Group1的类同。
此外,Android还为我们提供了onClick和onTouch的监听,在Activity里给View1加click监听:
findViewById(R.id.tv).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.d("Dispatch","View1 onClick"); } }); findViewById(R.id.tv).setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { Log.d("Dispatch","View1 onTouch eventType: " + ACTIONS[event.getAction()]); return false; } });
运行结果:
MainActivity dispatchTouchEvent : ACTION_DOWN Group 1 dispatchTouchEvent eventType: ACTION_DOWN Group 1 onInterceptTouchEvent eventType: ACTION_DOW View1 1 dispatchTouchEvent eventType: ACTION_DOWN View1 onTouch eventType: ACTION_DOWN View1 1 onTouchEvent eventType: ACTION_DOWN MainActivity dispatchTouchEvent : ACTION_MOVE Group 1 dispatchTouchEvent eventType: ACTION_MOVE Group 1 onInterceptTouchEvent eventType: ACTION_MOV View1 1 dispatchTouchEvent eventType: ACTION_MOVE View1 onTouch eventType: ACTION_MOVE View1 1 onTouchEvent eventType: ACTION_MOVE MainActivity dispatchTouchEvent : ACTION_UP Group 1 dispatchTouchEvent eventType: ACTION_UP Group 1 onInterceptTouchEvent eventType: ACTION_UP View1 1 dispatchTouchEvent eventType: ACTION_UP View1 onTouch eventType: ACTION_UP View1 1 onTouchEvent eventType: ACTION_UP View1 onClick
我们看到,onTouch事件是在onTouchEvent之前调用的,而onClick事件是在onTouch中被调用的,且是up事件执行完毕才触发的,最后的事件由onClick消费掉了,也就是说最终事件被View1消费了。接着,我们修改onTouch事件返回true,让它消费事件:
MainActivity dispatchTouchEvent : ACTION_DOWN Group 1 dispatchTouchEvent eventType: ACTION_DOWN Group 1 onInterceptTouchEvent eventType: ACTION_DOWN View1 1 dispatchTouchEvent eventType: ACTION_DOWN View1 onTouch eventType: ACTION_DOWN MainActivity dispatchTouchEvent : ACTION_MOVE Group 1 dispatchTouchEvent eventType: ACTION_MOVE Group 1 onInterceptTouchEvent eventType: ACTION_MOVE View1 1 dispatchTouchEvent eventType: ACTION_MOVE View1 onTouch eventType: ACTION_MOVE MainActivity dispatchTouchEvent : ACTION_UP Group 1 dispatchTouchEvent eventType: ACTION_UP Group 1 onInterceptTouchEvent eventType: ACTION_UP View1 1 dispatchTouchEvent eventType: ACTION_UP View1 onTouch eventType: ACTION_UP和上面想比,View1的onTouchEvent和onClick事件都没执行,这是因为onTouch消费了该事件,事件不再往下传,所以onTouchEvent和onClick事件都不会触发,在View1的onTouchEvent消费事件也是同理,事件被onTouchEvent消费,onClick不会触发(注:在父级的onTouchEvent返回true, 不会消费事件,因为已经设置了onClick的监听,说明是该View消费了事件,不会再往上传递)。