zl程序教程

您现在的位置是:首页 >  其他

当前栏目

AndroidTouch事件分发过程详解

事件 详解 过程 分发
2023-06-13 09:15:45 时间

本文以实例形式讲述了AndroidTouch事件分发过程,对于深入理解与掌握Android程序设计有很大的帮助作用。具体分析如下:

首先,从一个简单示例入手:

先看一个示例如下图所示:

布局文件:

<FrameLayoutxmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
tools:context="com.example.touch_event.MainActivity"
tools:ignore="MergeRootFrame">

<Button
android:id="@+id/my_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/hello_world"/>

</FrameLayout>

MainActivity文件:

publicclassMainActivityextendsActivity{

@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

ButtonmBtn=(Button)findViewById(R.id.my_button);
mBtn.setOnTouchListener(newOnTouchListener(){

@Override
publicbooleanonTouch(Viewv,MotionEventevent){
Log.d("","###onTouch:"+event.getAction());
returnfalse;
}
});
mBtn.setOnClickListener(newOnClickListener(){

@Override
publicvoidonClick(Viewv){
Log.d("","###onClick:"+v);
}
});

}

@Override
publicbooleandispatchTouchEvent(MotionEventev){
Log.d("","###activitydispatchTouchEvent");
returnsuper.dispatchTouchEvent(ev);
}
}

当用户点击按钮时会输出如下Log:

08-3103:03:56.116:D/(1560):###activitydispatchTouchEvent
08-3103:03:56.116:D/(1560):###onTouch:0
08-3103:03:56.196:D/(1560):###activitydispatchTouchEvent
08-3103:03:56.196:D/(1560):###onTouch:1
08-3103:03:56.196:D/(1560):###onClick:android.widget.Button{52860d98VFED..C....PH...0,0-1080,144#7f05003dapp:id/my_button}

我们可以看到首先执行了Activity中的dispatchTouchEvent方法,然后执行了onTouch方法,然后再是dispatchTouchEvent-->onTouch,最后才是执行按钮的点击事件。这里我们可能有个疑问,为什么dispatchTouchEvent和onTouch都执行了两次,而onClick才执行了一次?为什么两次的Touch事件的action不一样,action0和action1到底代表了什么?

覆写过onTouchEvent的朋友知道,一般来说我们在该方法体内都会处理集中touch类型的事件,有ACTION_DOWN、ACTION_MOVE、ACTION_UP等,不过上面我们的例子中并没有移动,只是单纯的按下、抬起。因此,我们的触摸事件也只有按下、抬起,因此有2次touch事件,而action分别为0和1。我们看看MotionEvent中的一些变量定义吧:

publicfinalclassMotionEventextendsInputEventimplementsParcelable{
//代码省略
publicstaticfinalintACTION_DOWN=0;//按下事件
publicstaticfinalintACTION_UP=1;//抬起事件
publicstaticfinalintACTION_MOVE=2;//手势移动事件
publicstaticfinalintACTION_CANCEL=3;//取消
//代码省略
}

可以看到,代表按下的事件为0,抬起事件为1,也证实了我们上面所说的。

在看另外两个场景:

1、我们点击按钮外的区域,输出Log如下:

08-3103:04:45.408:D/(1560):###activitydispatchTouchEvent08-31
03:04:45.512:D/(1560):###activitydispatchTouchEvent

2、我们在onTouch函数中返回true,输出Log如下:

08-3103:06:04.764:D/(1612):###activitydispatchTouchEvent
08-3103:06:04.764:D/(1612):###onTouch:0
08-3103:06:04.868:D/(1612):###activitydispatchTouchEvent
08-3103:06:04.868:D/(1612):###onTouch:1

以上两个场景为什么会这样呢?  我们继续往下看吧。

AndroidTouch事件分发

那么整个事件分发的流程是怎样的呢? 

简单来说就是用户触摸手机屏幕会产生一个触摸消息,最终这个触摸消息会被传送到ViewRoot(看4.2的源码时这个类改成了ViewRootImpl)的InputHandler,ViewRoot是GUI管理系统与GUI呈现系统之间的桥梁,根据ViewRoot的定义,发现它并不是一个View类型,而是一个Handler。InputHandler是一个接口类型,用于处理KeyEvent和TouchEvent类型的事件,我们看看源码:

publicfinalclassViewRootextendsHandlerimplementsViewParent,
View.AttachInfo.Callbacks{
//代码省略
privatefinalInputHandlermInputHandler=newInputHandler(){
publicvoidhandleKey(KeyEventevent,RunnablefinishedCallback){
startInputEvent(finishedCallback);
dispatchKey(event,true);
}
publicvoidhandleMotion(MotionEventevent,RunnablefinishedCallback){
startInputEvent(finishedCallback);
dispatchMotion(event,true);//1、handle触摸消息
}
};
//代码省略
//2、分发触摸消息
privatevoiddispatchMotion(MotionEventevent,booleansendDone){
intsource=event.getSource();
if((source&InputDevice.SOURCE_CLASS_POINTER)!=0){
dispatchPointer(event,sendDone);//分发触摸消息
}elseif((source&InputDevice.SOURCE_CLASS_TRACKBALL)!=0){
dispatchTrackball(event,sendDone);
}else{
//TODO
Log.v(TAG,"Droppingunsupportedmotionevent(unimplemented):"+event);
if(sendDone){
finishInputEvent();
}
}
}
//3、通过Handler投递消息
privatevoiddispatchPointer(MotionEventevent,booleansendDone){
Messagemsg=obtainMessage(DISPATCH_POINTER);
msg.obj=event;
msg.arg1=sendDone?1:0;
sendMessageAtTime(msg,event.getEventTime());
}
@Override
publicvoidhandleMessage(Messagemsg){//ViewRoot覆写handlerMessage来处理各种消息
switch(msg.what){
//代码省略
caseDO_TRAVERSAL:
if(mProfile){
Debug.startMethodTracing("ViewRoot");
}

performTraversals();

if(mProfile){
Debug.stopMethodTracing();
mProfile=false;
}
break;

caseDISPATCH_POINTER:{//4、处理DISPATCH_POINTER类型的消息,即触摸屏幕的消息
MotionEventevent=(MotionEvent)msg.obj;
try{
deliverPointerEvent(event);//5、处理触摸消息
}finally{
event.recycle();
if(msg.arg1!=0){
finishInputEvent();
}
if(LOCAL_LOGV||WATCH_POINTER)Log.i(TAG,"Donedispatching!");
}
}break;
//代码省略
}
//6、真正的处理事件
privatevoiddeliverPointerEvent(MotionEventevent){
if(mTranslator!=null){
mTranslator.translateEventInScreenToAppWindow(event);
}
booleanhandled;
if(mView!=null&&mAdded){
//entertouchmodeonthedown
booleanisDown=event.getAction()==MotionEvent.ACTION_DOWN;
if(isDown){
ensureTouchMode(true);//如果是ACTION_DOWN事件则进入触摸模式,否则为按键模式。
}
if(Config.LOGV){
captureMotionLog("captureDispatchPointer",event);
}
if(mCurScrollY!=0){
event.offsetLocation(0,mCurScrollY);//物理坐标向逻辑坐标的转换
}
if(MEASURE_LATENCY){
lt.sample("ADispatchingTouchEvents",System.nanoTime()-event.getEventTimeNano());
}
//7、分发事件,如果是窗口类型,则这里的mView对应的就是PhonwWindow中的DecorView,否则为根视图的ViewGroup。
handled=mView.dispatchTouchEvent(event);
//代码省略
}
}
//代码省略
}

经过层层迷雾,不管代码7处的mView是DecorView还是非窗口界面的根视图,其本质都是ViewGroup,即触摸事件最终被根视图ViewGroup进行分发!!!
       我们就以Activity为例来分析这个过程,我们知道显示出来的Activity有一个顶层窗口,这个窗口的实现类是PhoneWindow,PhoneWindow中的内容区域是一个DecorView类型的View,这个View这就是我们在手机上看到的内容,这个DecorView是FrameLayout的子类,Activity的的dispatchTouchEvent实际上就是调用PhoneWindow的dispatchTouchEvent,我们看看源代码吧,进入Activity的dispatchTouchEvent函数:

publicbooleandispatchTouchEvent(MotionEventev){
if(ev.getAction()==MotionEvent.ACTION_DOWN){
onUserInteraction();
}
if(getWindow().superDispatchTouchEvent(ev)){//1、调用的是PhoneWindow的superDispatchTouchEvent(ev)

returntrue;
}
returnonTouchEvent(ev);
}

publicvoidonUserInteraction(){
}

可以看到,如果事件为按下事件,则会进入到onUserInteraction()这个函数,该函数为空实现,我们暂且不管它。继续看,发现touch事件的分发调用了getWindow().superDispatchTouchEvent(ev)函数,getWindow()获取到的实例的类型为PhoneWindow类型,你可以在你的Activity类中使用如下方式查看getWindow()获取到的类型:

Log.d("","###Activiti中getWindow()获取的类型是:"+this.getWindow());

输出:

08-3103:40:17.036:D/(1688):###Activiti中getWindow()获取的类型是:com.android.internal.policy.impl.PhoneWindow@5287fe38

OK,废话不多说,我们还是继续看PhoneWindow中的superDispatchTouchEvent函数吧。

@Override
publicbooleansuperDispatchTouchEvent(MotionEventevent){
returnmDecor.superDispatchTouchEvent(event);
}

恩,调用的是mDecor的superDispatchTouchEvent(event)函数,这个mDecor就是我们上面所说的DecorView类型,也就是我们看到的Activity上的所有内容的一个顶层ViewGroup,即整个ViewTree的根节点。看看它的声明吧。

//Thisisthetop-levelviewofthewindow,containingthewindowdecor.
privateDecorViewmDecor;

DecorView

那么我继续看看DecorView到底是个什么玩意儿吧。

privatefinalclassDecorViewextendsFrameLayoutimplementsRootViewSurfaceTaker{
/*package*/intmDefaultOpacity=PixelFormat.OPAQUE;

/**ThefeatureIDofthepanel,or-1ifthisistheapplication"sDecorView*/
privatefinalintmFeatureId;

privatefinalRectmDrawingBounds=newRect();

privatefinalRectmBackgroundPadding=newRect();

privatefinalRectmFramePadding=newRect();

privatefinalRectmFrameOffsets=newRect();

privatebooleanmChanging;

privateDrawablemMenuBackground;
privatebooleanmWatchingForMenu;
privateintmDownY;

publicDecorView(Contextcontext,intfeatureId){
super(context);
mFeatureId=featureId;
}

@Override
publicbooleandispatchKeyEvent(KeyEventevent){
finalintkeyCode=event.getKeyCode();
//代码省略
returnisDown?PhoneWindow.this.onKeyDown(mFeatureId,event.getKeyCode(),event)
:PhoneWindow.this.onKeyUp(mFeatureId,event.getKeyCode(),event);
}

@Override
publicbooleandispatchTouchEvent(MotionEventev){
finalCallbackcb=getCallback();
returncb!=null&&mFeatureId<0?cb.dispatchTouchEvent(ev):super
.dispatchTouchEvent(ev);
}

@Override
publicbooleandispatchTrackballEvent(MotionEventev){
finalCallbackcb=getCallback();
returncb!=null&&mFeatureId<0?cb.dispatchTrackballEvent(ev):super
.dispatchTrackballEvent(ev);
}

publicbooleansuperDispatchKeyEvent(KeyEventevent){
returnsuper.dispatchKeyEvent(event);
}

publicbooleansuperDispatchTouchEvent(MotionEventevent){
returnsuper.dispatchTouchEvent(event);
}

publicbooleansuperDispatchTrackballEvent(MotionEventevent){
returnsuper.dispatchTrackballEvent(event);
}

@Override
publicbooleanonTouchEvent(MotionEventevent){
returnonInterceptTouchEvent(event);
}
//代码省略
}

可以看到,DecorView继承自FrameLayout,它对于touch事件的分发(dispatchTouchEvent)、处理都是交给super类来处理,也就是FrameLayout来处理,我们在FrameLayout中没有看到相应的实现,那继续跟踪到FrameLayout的父类,即ViewGroup,我们看到了dispatchTouchEvent的实现,那我们就先看ViewGroup(Android2.3源码)是如何进行事件分发的吧。

ViewGroup的Touch事件分发

/**
*{@inheritDoc}
*/
@Override
publicbooleandispatchTouchEvent(MotionEventev){
if(!onFilterTouchEventForSecurity(ev)){
returnfalse;
}

finalintaction=ev.getAction();
finalfloatxf=ev.getX();
finalfloatyf=ev.getY();
finalfloatscrolledXFloat=xf+mScrollX;
finalfloatscrolledYFloat=yf+mScrollY;
finalRectframe=mTempRect;

booleandisallowIntercept=(mGroupFlags&FLAG_DISALLOW_INTERCEPT)!=0;

if(action==MotionEvent.ACTION_DOWN){
if(mMotionTarget!=null){
//thisisweird,wegotapendown,butwethoughtitwas
//alreadydown!
//XXX:WeshouldprobablysendanACTION_UPtothecurrent
//target.
mMotionTarget=null;
}
//Ifwe"redisallowinginterceptorifwe"reallowingandwedidn"t
//intercept
if(disallowIntercept||!onInterceptTouchEvent(ev))//1、是否禁用拦截、是否拦截事件
//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--)//2、迭代所有子view,查找触摸事件在哪个子view的坐标范围内
finalViewchild=children[i];
if((child.mViewFlags&VISIBILITY_MASK)==VISIBLE
||child.getAnimation()!=null){
child.getHitRect(frame);//3、获取child的坐标范围
if(frame.contains(scrolledXInt,scrolledYInt))//4、判断发生该事件坐标是否在该child坐标范围内
//offsettheeventtotheview"scoordinatesystem
finalfloatxc=scrolledXFloat-child.mLeft;
finalfloatyc=scrolledYFloat-child.mTop;
ev.setLocation(xc,yc);
child.mPrivateFlags&=~CANCEL_NEXT_UP_EVENT;
if(child.dispatchTouchEvent(ev))//5、child处理该事件
//Eventhandled,wehaveatargetnow.
mMotionTarget=child;
returntrue;
}
//Theeventdidn"tgethandled,trythenextview.
//Don"tresettheevent"slocation,it"snot
//necessaryhere.
}
}
}
}
}

booleanisUpOrCancel=(action==MotionEvent.ACTION_UP)||
(action==MotionEvent.ACTION_CANCEL);

if(isUpOrCancel){
//Note,we"vealreadycopiedthepreviousstatetoourlocal
//variable,sothistakeseffectonthenextevent
mGroupFlags&=~FLAG_DISALLOW_INTERCEPT;
}

//Theeventwasn"tanACTION_DOWN,dispatchittoourtargetif
//wehaveone.
finalViewtarget=mMotionTarget;
if(target==null){
//Wedon"thaveatarget,thismeanswe"rehandlingthe
//eventasaregularview.
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"reallowedtoandwanttointerceptits
//events
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;
}

if(isUpOrCancel){
mMotionTarget=null;
}

//finallyoffsettheeventtothetarget"scoordinatesystemand
//dispatchtheevent.
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);
}

这个函数代码比较长,我们只看上文中标注的几个关键点。首先在代码1处可以看到一个条件判断,如果disallowIntercept和!onInterceptTouchEvent(ev)两者有一个为true,就会进入到这个条件判断中。disallowIntercept是指是否禁用掉事件拦截的功能,默认是false,也可以通过调用requestDisallowInterceptTouchEvent方法对这个值进行修改。那么当第一个值为false的时候就会完全依赖第二个值来决定是否可以进入到条件判断的内部,第二个值是什么呢?onInterceptTouchEvent就是ViewGroup对事件进行拦截的一个函数,返回该函数返回false则表示不拦截事件,反之则表示拦截。第二个条件是是对onInterceptTouchEvent方法的返回值取反,也就是说如果我们在onInterceptTouchEvent方法中返回false,就会让第二个值为true,从而进入到条件判断的内部,如果我们在onInterceptTouchEvent方法中返回true,就会让第二个值的整体变为false,从而跳出了这个条件判断。例如我们需要实现ListView滑动删除某一项的功能,那么可以通过在onInterceptTouchEvent返回true,并且在onTouchEvent中实现相关的判断逻辑,从而实现该功能。

进入代码1内部的if后,有一个for循环,遍历了当前ViewGroup下的所有子childview,如果触摸该事件的坐标在某个childview的坐标范围内,那么该childview来处理这个触摸事件,即调用该childview的dispatchTouchEvent。如果该childview是ViewGroup类型,那么继续执行上面的判断,并且遍历子view;如果该childview不是ViewGroup类型,那么直接调用的是View中的dispatchTouchEvent方法,除非这个childview的类型覆写了该方法。我们看看View中的dispatchTouchEvent函数:

View的Touch事件分发

/**
*Passthetouchscreenmotioneventdowntothetargetview,orthis
*viewifitisthetarget.
*
*@parameventThemotioneventtobedispatched.
*@returnTrueiftheeventwashandledbytheview,falseotherwise.
*/
publicbooleandispatchTouchEvent(MotionEventevent){
if(!onFilterTouchEventForSecurity(event)){
returnfalse;
}

if(mOnTouchListener!=null&&(mViewFlags&ENABLED_MASK)==ENABLED&&
mOnTouchListener.onTouch(this,event)){
returntrue;
}
returnonTouchEvent(event);
}

该函数中,首先判断该事件是否符合安全策略,然后判断该view是否是enable的,以及是否设置了TouchListener,mOnTouchListener即我们通过setOnTouchListener设置的。

/**
*Registeracallbacktobeinvokedwhenatoucheventissenttothisview.
*@paramlthetouchlistenertoattachtothisview
*/
publicvoidsetOnTouchListener(OnTouchListenerl){
mOnTouchListener=l;
}

如果mOnTouchListener.onTouch(this,event)返回false则继续执行onTouchEvent(event);如果mOnTouchListener.onTouch(this,event)返回true,则表示该事件被消费了,不再传递,因此也不会执行onTouchEvent(event)。这也验证了我们上文中留下的场景2,当onTouch函数返回true时,点击按钮,但我们的点击事件没有执行。那么我们还是先来看看onTouchEvent(event)函数到底做了什么吧。

/**
*Implementthismethodtohandletouchscreenmotionevents.
*
*@parameventThemotionevent.
*@returnTrueiftheeventwashandled,falseotherwise.
*/
publicbooleanonTouchEvent(MotionEventevent){
finalintviewFlags=mViewFlags;

if((viewFlags&ENABLED_MASK)==DISABLED)//1、判断该view是否enable
//Adisabledviewthatisclickablestillconsumesthetouch
//events,itjustdoesn"trespondtothem.
return(((viewFlags&CLICKABLE)==CLICKABLE||
(viewFlags&LONG_CLICKABLE)==LONG_CLICKABLE));
}

if(mTouchDelegate!=null){
if(mTouchDelegate.onTouchEvent(event)){
returntrue;
}
}

if(((viewFlags&CLICKABLE)==CLICKABLE||
(viewFlags&LONG_CLICKABLE)==LONG_CLICKABLE))//2、是否是clickable或者longclickable
switch(event.getAction()){
caseMotionEvent.ACTION_UP://抬起事件
booleanprepressed=(mPrivateFlags&PREPRESSED)!=0;
if((mPrivateFlags&PRESSED)!=0||prepressed){
//takefocusifwedon"thaveitalreadyandweshouldin
//touchmode.
booleanfocusTaken=false;
if(isFocusable()&&isFocusableInTouchMode()&&!isFocused()){
focusTaken=requestFocus();//获取焦点
}

if(!mHasPerformedLongPress){
//Thisisatap,soremovethelongpresscheck
removeLongPressCallback();

//Onlyperformtakeclickactionsifwewereinthepressedstate
if(!focusTaken){
//UseaRunnableandpostthisratherthancalling
//performClickdirectly.Thisletsothervisualstate
//oftheviewupdatebeforeclickactionsstart.
if(mPerformClick==null){
mPerformClick=newPerformClick();
}
if(!post(mPerformClick))//post
performClick();//3、点击事件处理
}
}
}

if(mUnsetPressedState==null){
mUnsetPressedState=newUnsetPressedState();
}

if(prepressed){
mPrivateFlags|=PRESSED;
refreshDrawableState();
postDelayed(mUnsetPressedState,
ViewConfiguration.getPressedStateDuration());
}elseif(!post(mUnsetPressedState)){
//Ifthepostfailed,unpressrightnow
mUnsetPressedState.run();
}
removeTapCallback();
}
break;

caseMotionEvent.ACTION_DOWN:
if(mPendingCheckForTap==null){
mPendingCheckForTap=newCheckForTap();
}
mPrivateFlags|=PREPRESSED;
mHasPerformedLongPress=false;
postDelayed(mPendingCheckForTap,ViewConfiguration.getTapTimeout());
break;

caseMotionEvent.ACTION_CANCEL:
mPrivateFlags&=~PRESSED;
refreshDrawableState();
removeTapCallback();
break;

caseMotionEvent.ACTION_MOVE:
finalintx=(int)event.getX();
finalinty=(int)event.getY();

//Belenientaboutmovingoutsideofbuttons
intslop=mTouchSlop;
if((x<0-slop)||(x>=getWidth()+slop)||
(y<0-slop)||(y>=getHeight()+slop)){
//Outsidebutton
removeTapCallback();
if((mPrivateFlags&PRESSED)!=0){
//Removeanyfuturelongpress/tapchecks
removeLongPressCallback();

//Needtoswitchfrompressedtonotpressed
mPrivateFlags&=~PRESSED;
refreshDrawableState();
}
}
break;
}
returntrue;
}

returnfalse;
}

我们看到,在onTouchEvent函数中就是对ACTION_UP、ACTION_DOWN、ACTION_MOVE等几个事件进行处理,而最重要的就是UP事件了,因为这个里面包含了对用户点击事件的处理,或者是说对于用户而言相对重要一点,因此放在了第一个case中。在ACTION_UP事件中会判断该view是否enable、是否clickable、是否获取到了焦点,然后我们看到会通过post方法将一个PerformClick对象投递给UI线程,如果投递失败则直接调用performClick函数执行点击事件。

/**
*CausestheRunnabletobeaddedtothemessagequeue.
*Therunnablewillberunontheuserinterfacethread.
*
*@paramactionTheRunnablethatwillbeexecuted.
*
*@returnReturnstrueiftheRunnablewassuccessfullyplacedintothe
*messagequeue.Returnsfalseonfailure,usuallybecausethe
*looperprocessingthemessagequeueisexiting.
*/
publicbooleanpost(Runnableaction){
Handlerhandler;
if(mAttachInfo!=null){
handler=mAttachInfo.mHandler;
}else{
//Assumethatpostwillsucceedlater
ViewRoot.getRunQueue().post(action);
returntrue;
}

returnhandler.post(action);
}

我们看看PerformClick类吧。

privatefinalclassPerformClickimplementsRunnable{
publicvoidrun(){
performClick();
}
}

可以看到,其内部就是包装了View类中的performClick()方法。再看performClick()方法:

/**
*Registeracallbacktobeinvokedwhenthisviewisclicked.Ifthisviewisnot
*clickable,itbecomesclickable.
*
*@paramlThecallbackthatwillrun
*
*@see#setClickable(boolean)
*/
publicvoidsetOnClickListener(OnClickListenerl){
if(!isClickable()){
setClickable(true);
}
mOnClickListener=l;
}

/**
*Callthisview"sOnClickListener,ifitisdefined.
*
*@returnTruetherewasanassignedOnClickListenerthatwascalled,false
*otherwiseisreturned.
*/
publicbooleanperformClick(){
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);

if(mOnClickListener!=null){
playSoundEffect(SoundEffectConstants.CLICK);
mOnClickListener.onClick(this);
returntrue;
}

returnfalse;
}

代码很简单,主要就是调用了mOnClickListener.onClick(this);方法,即执行用户通过setOnClickListener设置进来的点击事件处理Listener。
 
总结

用户触摸屏幕产生一个触摸消息,系统底层将该消息转发给ViewRoot(ViewRootImpl),ViewRoot产生一个DISPATCHE_POINTER的消息,并且在handleMessage中处理该消息,最终会通过deliverPointerEvent(MotionEventevent)来处理该消息。在该函数中会调用mView.dispatchTouchEvent(event)来分发消息,该mView是一个ViewGroup类型,因此是ViewGroup的dispatchTouchEvent(event),在该函数中会遍历所有的childview,找到该事件的触发的左边与每个childview的坐标进行对比,如果触摸的坐标在该childview的范围内,则由该childview进行处理。如果该childview是ViewGroup类型,则继续上一步的查找过程;否则执行View中的dispatchTouchEvent(event)函数。在View的dispatchTouchEvent(event)中首先判断该控件是否enale以及mOnTouchListent是否为空,如果mOnTouchListener不为空则执行mOnTouchListener.onTouch(event)方法,如果该方法返回false则再执行View中的onTouchEvent(event)方法,并且在该方法中执行mOnClickListener.onClick(this,event);方法;如果mOnTouchListener.onTouch(event)返回true则不会执行onTouchEvent方法,因此点击事件也不会被执行。

粗略的流程图如下:

相信本文所述对大家进一步深入掌握Android程序设计有一定的借鉴价值。