注:低版本的源码内容比高版本的源码简单,分析起来方便,但是高版本源码更为严密。

View的事件响应机制

涉及2个方法dispatchTouchEvent和onTouchEvent

1.View的dispatchTouchEvent方法(事件传递到View,View的这个方法就自动执行。)

dispatchTouchEvent返回true,响应事件;返回false,不响应事件。

publicbooleandispatchTouchEvent(MotionEventevent){

...

ListenerInfoli=mListenerInfo;

if(li!=null&&li.mOnTouchListener!=null&&(mViewFlags&ENABLED_MASK)==ENABLED&&li.mOnTouchListener.onTouch(this,event)){

returntrue;

}

if(onTouchEvent(event)){

returntrue;

}//等价于returnonTouchEvent(event)

}


...

returnfalse;//(如果View没有setOnTouchListener,默认dispatchTouchEvent

事件就是返回false)

}

----------------------------------------------------------------------------------------------------------------------

上述源码中的ListenerInfo是View类中的一个静态成员类,里面封装了各种事件类型的监听

者XxxListener的变量,包括privateOnTouchListenermOnTouchListener;

在View这个类中有ListenerInfomListenerInfo;这个类型的成员变量

而mListenerInfo是通过下面这个方法来返回的,从方法可以看出返回值肯定不为空。

ListenerInfogetListenerInfo(){

if(mListenerInfo!=null){

returnmListenerInfo;

}

mListenerInfo=newListenerInfo();

returnmListenerInfo;

}

结论1:所以View的dispatchTouchEvent方法中的if判断中li(也就是mListenerInfo)!=null

----------------------------------------------------------------------------------------------------------------------

再看View的setOnTouchListener这个方法,只要这个方法被调用了,参数不为空,那么

mListenerInfo.mOnTouchListener就不为空。

publicvoidsetOnTouchListener(OnTouchListenerl){

getListenerInfo().mOnTouchListener=l;

}

结论2:所以View的dispatchTouchEvent方法中的if判断中li.mOnTouchListener!=null

所以

if(li!=null&&li.mOnTouchListener!=null&&(mViewFlags&ENABLED_MASK)==ENABLED&&li.mOnTouchListener.onTouch(this,event))

这个条件组只是和li.mOnTouchListener.onTouch(this,event)这个回调方法有关

如果返回true的话,ImageView的dispatchTouchEvent方法也返回true。

如果返回false的话,会继续进行下面的一个if判断,也就是onTouchEvent方法的判断。

if(onTouchEvent(event)){

returntrue;

}

上面这3行代码等价于returnonTouchEvent(event),其实2.3.3的源码就是这么写的。

###############################################################################

所以对于dispatchTouchEvent()方法,如果直接继承自View的控件

1.没有调用setOnTouchListener()设置监听者,那么

li.mOnTouchListener==null,dispatchTouchEvent()方法默认就会返回false,这时

onTouchEvent方法和dispatchTouchEvent()方法就没有任何的关联了。

2.调用setOnTouchListener()设置监听者,那么li.mOnTouchListener!=null,

dispatchTouchEvent()方法就会判断li.mOnTouchListener.onTouch(this,event),即监听者

的onTouch回调方法(表明用户的意图)的返回值

***onTouch回调方法返回true,dispatchTouchEvent()方法就会返回true。

***onTouch回调方法返回false,dispatchTouchEvent()方法就会调用onTouchEvent处理

事件,即把事件转交给onTouchEvent方法进行处理

###############################################################################

2.View的onTouchEvent方法

publicbooleanonTouchEvent(MotionEventevent){

...

if(((viewFlags&CLICKABLE)==CLICKABLE||

(viewFlags&LONG_CLICKABLE)==LONG_CLICKABLE)){

...

switch(event.getAction()){

caseMotionEvent.ACTION_UP:

...

if(!post(mPerformClick)){

performClick();/*

底层实现:li.mOnClickListener.onClick(this);

View的点击事件真正是在UP事件之后执行的。

*/

}

...

returntrue;

}

returnfalse;

}

---------------------------------------------------------------------

View的onTouchEvent的返回值,取决于View是否可点击。

这就解释了为什么当ImageView和Button的监听事件的OnTouch事件都是返回false时,ImageView只能响应按下事件,而Button能响应所有事件的原因了:因为ImageView默认的clickable属性为false,而Button的clickable属性为true。

想让ImageView监听事件的onTouch返回false也能响应所有的事件,有2种方式

第1种:直接将ImageView的clickable属性设置为true。

第2种:为ImageView添加点击事件,通过查看下面的源码可以看出,点击事件会将View

的clickable属性设置为true。

publicvoidsetOnClickListener(OnClickListenerl){

if(!isClickable()){

setClickable(true);

}

getListenerInfo().mOnClickListener=l;}

假如Button添加了点击事件,如果去屏蔽它的点击事件呢?

n在添加点击事件的代码后面设置onclickable属性为false。

n在onTouchListener监听的onTouch方法里返回true。

原理:因为View的点击事件的本质是由onTouchEvent方法中的,performClick

这个方法所执行的,不让View执行onTouchEvent或clickable为false

即不会执行点击事件。

由此也可以看出onTouch事件和onClick事件不是一回事

ViewGroup的事件响应和传递机制(以2.3.3的源码来分析)

涉及3个方法dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent

ViewGroup的事件传递,是伴随着递归算法查找坐标落在那一个控件的范围,由父控件

向子控件,由外到内。

@Override

publicbooleandispatchTouchEvent(MotionEventev){

...

finalintaction=ev.getAction();

finalfloatxf=ev.getX();

finalfloatyf=ev.getY();

finalfloatscrolledXFloat=xf+mScrollX;

finalfloatscrolledYFloat=yf+mScrollY;

finalRectframe=mTempRect;//矩形类mTempRect=newRect();

booleandisallowIntercept=(mGroupFlags&FLAG_DISALLOW_INTERCEPT)!=0;

if(action==MotionEvent.ACTION_DOWN){

...

//Ifwe'redisallowinginterceptorifwe'reallowingandwedidn'tintercept

//如果onInterceptTouchEvent(ev)没有拦截事件

//只有自己先不拦截,才有必要去判断有没有子View去响应这个事件。

if(disallowIntercept||!onInterceptTouchEvent(ev)){

//resetthisevent'saction(justtoprotectourselves)

ev.setAction(MotionEvent.ACTION_DOWN);

//Weknowwewanttodispatchtheeventdown,findachild

//whocanhandleit,startwiththefront-mostchild.

finalintscrolledXInt=(int)scrolledXFloat;

finalintscrolledYInt=(int)scrolledYFloat;

finalView[]children=mChildren;

finalintcount=mChildrenCount;

for(inti=count-1;i>=0;i--){

finalViewchild=children[i];

if((child.mViewFlags&VISIBILITY_MASK)==VISIBLE

||child.getAnimation()!=null){//当child

可见或有动画时

child.getHitRect(frame);//测量子控件的矩形参数

//判断事件的坐标有没有包含在子控件的矩形范围之内

if(frame.contains(scrolledXInt,scrolledYInt)){

//计算事件在子View中的坐标

finalfloatxc=scrolledXFloat-child.mLeft;

finalfloatyc=scrolledYFloat-child.mTop;

ev.setLocation(xc,yc);

child.mPrivateFlags&=~CANCEL_NEXT_UP_EVENT;

if(child.dispatchTouchEvent(ev)){

//Eventhandled,wehaveatargetnow.!!!!

//子View如果处理了事件,就把这个View作为事件的目

mMotionTarget=child;

returntrue;

}

//Theeventdidn'tgethandled,trythenextview.

//Don'tresettheevent'slocation,it'snot

//necessaryhere.

}

}

//子View能够成为mMotionTarget的前提是事件坐标落在它的

矩形范围之内,并且它响应处理了这个事件。

}

}

}Action_down的if结束

...

//Theeventwasn'tanACTION_DOWN,dispatchittoourtargetif

wehaveone.(**没有子View响应**)

finalViewtarget=mMotionTarget;

if(target==null){

//Wedon'thaveatarget,thismeanswe'rehandlingthe

//eventasaregularview.(**如果没有子View响应这个事件,这个

事件就会当作一般的View的事件来处理,即ViewGroup执行像View

一样的去执行事件分发**)

ev.setLocation(xf,yf);

if((mPrivateFlags&CANCEL_NEXT_UP_EVENT)!=0){

ev.setAction(MotionEvent.ACTION_CANCEL);

mPrivateFlags&=~CANCEL_NEXT_UP_EVENT;

}

returnsuper.dispatchTouchEvent(ev);

}

//ifhaveatarget,seeifwe'reallowedtoandwanttointerceptitsevents(**有子View响应**)

if(!disallowIntercept&&onInterceptTouchEvent(ev)){

finalfloatxc=scrolledXFloat-(float)target.mLeft;

finalfloatyc=scrolledYFloat-(float)target.mTop;

mPrivateFlags&=~CANCEL_NEXT_UP_EVENT;

ev.setAction(MotionEvent.ACTION_CANCEL);

ev.setLocation(xc,yc);

if(!target.dispatchTouchEvent(ev)){

//targetdidn'thandleACTION_CANCEL.notmuchwecando

//buttheyshouldhave.

}

//clearthetarget

mMotionTarget=null;

//Don'tdispatchthiseventtoourownview,becausewealready

//sawitwhenintercepting;wejustwanttogivethefollowing

//eventtothenormalonTouchEvent().

returntrue;

}

//finallyoffsettheeventtothetarget'scoordinatesystemand

//dispatchtheevent.(**把事件转交给子View去处理**)

finalfloatxc=scrolledXFloat-(float)target.mLeft;

finalfloatyc=scrolledYFloat-(float)target.mTop;

ev.setLocation(xc,yc);

if((target.mPrivateFlags&CANCEL_NEXT_UP_EVENT)!=0){

ev.setAction(MotionEvent.ACTION_CANCEL);

target.mPrivateFlags&=~CANCEL_NEXT_UP_EVENT;

mMotionTarget=null;

}

returntarget.dispatchTouchEvent(ev);

}