首页 > 代码库 > 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事件的传递机制