Anroid View事件响应机制和ViewGroup的事件响应分发机制
注:低版本的源码内容比高版本的源码简单,分析起来方便,但是高版本源码更为严密。
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);
}
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。