事件分发的基础认知
事件分发的本质
将点击事件(MotionEvent)传递到某个具体的View & 处理的整个过程(事件的传递过程 = 分发过程)
事件的传递对象
- Activity
- ViewGroup
- view
Android的UI界面由Activity、ViewGroup、View 及其派生类组成
事件分发过程中的重要方法
- public boolean dispatchTouchEvent(MotionEvent ev)
用来进行事件的分发。如果事件能够传递给当前view,此方法一定会被调用,返回结果受当前view的onTouchEvent和下级view的dispatchTouchEvent方法的影响,表示是否消耗当前事件。
- public boolean onInterceptTouchEvent(MotionEvent event)
在上述方法内部调用,用来判断是否拦截某个事件,如果当前view拦截了某个事件,那么在同一个事件序列中,此方法不会被再次调用,返回结果表示是否拦截当前事件。
- public boolean onTouchEvent(MotionEvent event)
在dispatchTouchEvent方法中调用,用来处理点击事件,返回结果表示是否消耗当前事件,如果不消耗,则在同一个事件序列中,当前view无法再次接收到事件。
上述三个方法可以用如下伪代码表示:
public boolean dispatchTouchEvent(MotionEvent ev){
boolean consume = false;
if(onInterceptTouchEvent(ev)){
consume = onTouchEvent(ev);
}else{
consume = onTouchEvent(ev);
}
return consume;
}
事件分发的源码解析
Activity事件的分发机制
当一个点击操作发生时,事件最先传递给当前Activity,由Activity的dispatchTouchEvent来进行事件派发,具体的工作是由Activity内部的Window来完成的。
源码分析
public boolean dispatchTouchEvent(MotionEvent ev){
//一般事件列开始都是DOWN事件
if(ev.getAction() == MotionEvent.ACTION_DOWN){
// -->>分析一
onUserInteraction();
//a.该方法为空方法
//b.当此Activity在栈顶时,触摸屏幕点击home、back、menu键等都会触发此方法
}
// -->>分析二
if(getWindow().superDispatchTouchEvent(ev)){
return true;
//事件交给activity所附属的Window进行分发
//若返回true:整个事件循环结束
//若返回false:意味事件无人处理,所有view的onTouchEvent都返回了false,Activity的onTouchEvent被调用。
}
// -->>分析三
return onTouchEvent();
}
(分析一)onUserInteraction
每当Key,Touch,Trackball事件分发到当前Activity就会被调用。如果你想当你的Activity在运行的时候,能够得知用户正在与你的设备交互,你可以override该方法。此回调和Activity.onUserLeaveHint为了帮助Activities智能的管理状态栏Notification;特别是为了帮助Activities在恰当的时间取消Notification。
对活动的Activity.onUserLeaveHint回调的所有调用都将伴随对Activity.OnUserInteraction的调用。这可确保您的活动将被告知相关的用户活动,例如拉下通知窗格并触摸其中的项目。
请注意,将针对开始触摸手势的触摸操作调用此回调,但可能不会针对随后的触摸移动和触摸操作调用此回调。
Window如何将事件传递给ViewGroup?
(分析二)getwindow().superDispatchTouchEvent(ev)
1.activity.getWindow()
getWindow()返回Window抽象类,superDispatchTouchEvent()是Window的抽象方法。
public abstract boolean superDispatchTouchEvent(MotionEvent ev);
2.PhoneWindow.superDispatchTouchEvent()
Window的实现类是PhoneWindow,在Activity的启动过程中就创建了Window(PolicyManager(Policy)、makeNewWindow)
public boolean superDispatchTouchEvent(MotionEvent ev){
returen mDector.superDispatchTouchWindow(ev);
}
3.DecorView.superDispatchTouchEvent()
public final class DecorView extends FrameLayout implents RootViewSurfaceTracker{
private DecorView mDecor;
public final View getDecorView(){
if(mDecor == null){
inStallDecor();
}
return mDecor;
}
}
由此可见,DecorView的父类是FrameLayout,FrameLayout继承ViewGroup。
public boolean superDispatchTouchEvent(MotionEvent event){
return super.dispatchTouchEvent(event);
//调用父类ViewGroup的dispatchTouchEvent()
//即将事件传递给ViewGroup去处理,详细请看ViewGroup的事件分发机制
}
(分析三)Activity.onTouchEvent()
public boolean onTouchEvent(MotionEvent event) {
//当一个点击事件未被Activity下任何一个View接收/处理时
//应用场景:处理发生在Window边界外(顶层view--DecorView)的触摸事件
// ->> 分析四
if (mWindow.shouldCloseOnTouch(this, event)) {
finish();
return true;
}
return false;
//只有点击事件在Window边界外才会返回true,一般情况都返回false
(分析四)mWindow.shouldCloseOnTouch(this, event)
public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
// 主要是对于处理边界外点击事件的判断:是否是DOWN事件,event的坐标是否在边界内等
if (mCloseOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN && isOutOfBounds(context, event) && peekDecorView() != null) {
return true;
}
return false;
// 返回true:说明事件在边界外,即 消费事件
// 返回false:未消费(默认)
}
网友评论