首页 > 代码库 > Touch事件的传递机制
Touch事件的传递机制
本文主要从源代码的角度讲解view以及viewgroup的事件传递机制。
ViewGroup的dispatchTouchEvent方法:
Private TouchTarget firstTouchTarget;
Public Boolean dispatchTouchEvent(MotionEvent ev){
先进行安全检查,看看当前window有没有遮挡,如果没有继续
if(action==ACTION_DOWN){
清理上次的TouchTargets的状态,避免程序切换和ANR的时候会抛出ACTION_UP和ACTION_CANCEL事件对这个方法的影响,并对firstTouchTarget置空;
}
boolean intercept;
//是down事件或者有代理的情况下就判断要不要拦截
if(action==ACTION_DOWN||firstTouchTarget!=null){
//这儿判断一下子类是不是调过自己的requestDisallowInterceptTouchEvent()方法,如果调过,就不拦截事件了,没掉过,就看onInterceptTouchEvent()方法的。
if(disallow_intercept!=true){
intercept = onInterceptTouchEvent(ev);
}else{
intercept= false;
}
}else{
//不是初始的down事件,而且firstTouchTarget为空,才会走到这儿,1.没有子view 2.子view在down的时候没有消耗事件firstTouchTarget就为空了后续的move,up事件时,才会走到这儿
Intercept=true;
}
TouchTarget touchTarget=null;
boolean cancel=(action==ACTION_CANCEL);
If(!cancel&&!intercept){
//多点触控会添加多个TouchTarget。
If(action==ACTION_DOWN||actionMask==ACTION_POINTER_DOWN||actionMask==ACTION_HOVER_MOVE){
If(touchTarget==null&&childCount!=0){
//找一个子view进行TouchTarget代理设置,多从后往前搜索
for(int i=childCount-1;i>0;i--){
if(!child.contains(x,y)){
continue;
}
touchTarget=getTouchTarget(child);
If(touchTarget!=null){
break;
}
if( dispatchTransformdTouchEvent(ev,false,touchTarget.child,idbits)){
//这个方法也会给firstTouchTarget赋值
touchTarget=addTouchTarget(child);
}
}
If(touchTarget==null&&firstTouchTarget!=null){
没有找到子view,就让touchTarget变成firstTouchTarget的next,即最近的一个TouchTarget。(如果只有一个的话,首尾是相接的。)
}
}
}
}
boolean handle=false;
If(firstTouchTarget==null){
//对自己的touch事件进行处理
If( dispatchTransformdTouchEvent(ev,false,null,idbits)){
handle=true;
}
}else{
//循环的对每个子view进行touch事件的传递,一般就一个,多点触控的时候可能有多个,但只要有一个子view的dispatchTouchEvent返回的是true,则父类的dispatchTouchEvent就返回true。
TouchTarget target=firstTouchTarget;
While(tagert!=null){
//在down事件时上面给touchTarget赋值的时候已经调用了dispatchTransformdTouchEvent方法,这儿排除掉,避免重复。
If(target==touchTarget){
handle=true;
}
if( dispatchTransformdTouchEvent(ev,false,target.child,idbits)){
handle=true;
}
target=target.next;
}
}
If(cancel||action=ACTION_UP||action==ACTION_HOVER_MOVE){
一次触摸事件完成,状态重置。
}else if(action==ACTION_POINTER_UP{
多点触控的,从touchTarget链里删除离开屏幕的那个点。
}
return handle;
}
---------------------------------------------------------------------------------------
//这个方法用来判断是交给view的dispatch事件处理还是交给子view的dispatch处理
private boolean dispatchTransformedTouchEvent(MotionEvent ev, boolean cancel,
View child, int desiredPointerIdBits) {
将位置偏移到子view的相对位置,然后再派发事件
If(child==null){
return super.dispatchTouchEvent(ev);
}else{
return child.dispatchTouchEvent(ev);
}
}
View的dispatchTouchEvent方法
Public Boolean dispatchTouchEvent(MotionEvent ev){
//判断一下当前window是不是被遮挡
If(security){
If(onTouchListener!=null&&onTouchListner.onTouch(this,ev)){
return true;
}
//如果onTouchEvent返回true,view的dispatch方法也返回true;
If(onTouchEvent(ev){
return true;
}
}
return false;
}
View的touch事件:
Public Boolean onTouchEvent(MotionEvent ev){
//view基类处理了点击,长按和press等基本事件
}
Activity的dispatchTouchEvent事件:
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
Activity的onTouchEvent事件:
public boolean onTouchEvent(MotionEvent event) {
if (mWindow.shouldCloseOnTouch(this, event)) {
finish();
return true;
}
return false;
}
无任何拦截情况下的传递:
不做任何处理的情况下会尝试添加一次touchTarget,但没有添加成功,造成所有firstTouchTarget都为空,touchTarget也为空,直接走到firstTouchTarget==null的逻辑里。所以最后只有activity还相应up事件,其他view都不响应了。
ImageView的dispatch方法拦截后的传递:
ImageView的dispatch直接置成true,Relativelayout正常走进设置TouchTarget的代码,然后在最底下的代理while循环里,handle等于了true,递归下来所有父类的dispatchTouchEvent方法都返回true。
ImageView的touch事件拦截后的传递:
ImageView的onTouch只要是true,dispatchTouch肯定是true,父类正常走进设置TouchTarget,然后在最底下的while循环里,handle等于了true,递归下来所有父类的dispatchTouchEvent方法都返回true。
RelativeLayout的touch方法拦截后的传递:
第一次down到达Relativelayout,它需要找出子view中谁消耗了这个事件,如果子view都没有消耗,touchTarget和firstTouchTarget就都为空了。直接走到了firstTouchTarget==null的逻辑里了,即自己消耗事件。然后当接下来的事件传递过来后,就直接intercepted,然后走到firstTouchTarget为空的逻辑,不再往子view上传递了。
Relativelayout的intercept拦截事件后的传递
RelativeLayout的intercept方法拦截了,intercepted就为true,所以整个设置代理的过程就进不去了,直接走到了firstTouchTarget==null的逻辑,relativelayout本身自己处理。
这个情况和Relativelayout的dispatch方法直接返回true效果一样,只是少了onIntercept和onTouch的log而已。
这个情况下无论ImageView怎么写都不会有log出现。
所以总是所述:
dispatchTouchEvent是接受父类事件的第一个方法,本身有自己的控制在里面,如果重写这个方法的话,里面要加上对拦截和touch的方法的调用。
InterceptTouchEvent方法完全可以完成对touch事件的拦截,要不要交给子类去处理。一般情况下不需要对dispatch方法重写。
onTouchEvent方法就是具体的touch事件的处理,如果想继续往回传递,就返回false,如果自己消耗,就返回true。
Touch事件的传递机制