详细介绍Android中回调函数机制
提示:在阅读本文章之前,请确保您对Touch事件的分发机制有一定的了解
在Android的学习过程中经常会听到或者见到“回调”这个词,那么什么是回调呢?所谓的回调函数就是:在A类中定义了一个方法,这个方法中用到了一个接口和该接口中的抽象方法,但是抽象方法没有具体的实现,需要B类去实现,B类实现该方法后,它本身不会去调用该方法,而是传递给A类,供A类去调用,这种机制就称为回调。
下面我们拿具体的Button的点击事件进行模拟分析:
首先,在View类中我们能找到setOnClickListener(OnClickListenerl)方法:
publicvoidsetOnClickListener(OnClickListenerl){
if(!isClickable()){
setClickable(true);
}
getListenerInfo().mOnClickListener=l;
}
可以看到,在该方法中将OnClickListener赋值给了mOnClickListener,那么我们继续向下找,会看到在performClick()方法中执行了我们实现的onClick()方法。
publicbooleanperformClick(){
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
ListenerInfoli=mListenerInfo;
if(li!=null&&li.mOnClickListener!=null){
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
returntrue;
}
returnfalse;
}
由此我们可以清楚的看不到,在父类中我们要用到onClick()方法,但是父类却没有去实现该方法,而是定义了一个方法setOnClickListener(OnClickListenerl),如果子类想要自己能够响应点击事件,则它就必须重写父类的该方法,实现OnClickListener接口和它的onClick()方法。在子类实现该接口和方法后,将其通过参数传递给父类,在父类中执行onClick()方法。
那么,为什么会在父类中执行到该方法呢,这就要说到Android中的另一个重要的机制——触摸事件的传递机制。
我们知道,只要我们的手指触摸到手机屏幕,就一定会执行dispatchTouchEvent(MotionEventevent)方法,接下来我们就看一下dispatchTouchEvent方法中都有哪些内容:
if(mInputEventConsistencyVerifier!=null){ mInputEventConsistencyVerifier.onTouchEvent(event,0); } if(onFilterTouchEventForSecurity(event)){ //noinspectionSimplifiableIfStatement ListenerInfoli=mListenerInfo; if(li!=null&&li.mOnTouchListener!=null &&(mViewFlags&ENABLED_MASK)==ENABLED &&li.mOnTouchListener.onTouch(this,event)){ returntrue; } if(onTouchEvent(event)){ returntrue; } } if(mInputEventConsistencyVerifier!=null){ mInputEventConsistencyVerifier.onUnhandledEvent(event,0); } returnfalse; } 这里我们不细讲Touch事件的分发机制,因为网上有哥们已经讲的很清楚了。请参看篇首提供的链接。 我们看一下第17行,由于我们没有实现OnTouchListener接口,而onTouch()方法的默认返回值为false,所以第一个if语句中的代码不会被执行到,进入第二个if语句中,执行了onTouchEvent()方法。那么我们再来看一下该方法: finalintviewFlags=mViewFlags; if((viewFlags&ENABLED_MASK)==DISABLED){ if(event.getAction()==MotionEvent.ACTION_UP &&(mPrivateFlags&PFLAG_PRESSED)!=0){ setPressed(false); } //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)){ switch(event.getAction()){ caseMotionEvent.ACTION_UP: booleanprepressed=(mPrivateFlags&PFLAG_PREPRESSED)!=0; if((mPrivateFlags&PFLAG_PRESSED)!=0||prepressed){ //takefocusifwedon"thaveitalreadyandweshouldin //touchmode. booleanfocusTaken=false; if(isFocusable()&&isFocusableInTouchMode()&&!isFocused()){ focusTaken=requestFocus(); } if(prepressed){ //Thebuttonisbeingreleasedbeforeweactually //showeditaspressed. Makeitshowthepressed //statenow(beforeschedulingtheclick)toensure //theuserseesit. setPressed(true); } if(!mHasPerformedLongPress){ //Thisisatap,soremovethelongpresscheck removeLongPressCallback(); //Onlyperformtakeclickactionsifwewereinthepressedstate if(!focusTaken){ //UseaRunnableandpostthisratherthancalling //performClickdirectly.Thisletsothervisualstate //oftheviewupdatebeforeclickactionsstart. if(mPerformClick==null){ mPerformClick=newPerformClick(); } if(!post(mPerformClick)){ performClick(); } } } if(mUnsetPressedState==null){ mUnsetPressedState=newUnsetPressedState(); } if(prepressed){ postDelayed(mUnsetPressedState, ViewConfiguration.getPressedStateDuration()); }elseif(!post(mUnsetPressedState)){ //Ifthepostfailed,unpressrightnow mUnsetPressedState.run(); } removeTapCallback(); } break; caseMotionEvent.ACTION_DOWN: mHasPerformedLongPress=false; if(performButtonActionOnTouchDown(event)){ break; } //Walkupthehierarchytodetermineifwe"reinsideascrollingcontainer. booleanisInScrollingContainer=isInScrollingContainer(); //Forviewsinsideascrollingcontainer,delaythepressedfeedbackfor //ashortperiodincasethisisascroll. if(isInScrollingContainer){ mPrivateFlags|=PFLAG_PREPRESSED; if(mPendingCheckForTap==null){ mPendingCheckForTap=newCheckForTap(); } postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout()); }else{ //Notinsideascrollingcontainer,soshowthefeedbackrightaway setPressed(true); checkForLongClick(0); } break; caseMotionEvent.ACTION_CANCEL: setPressed(false); removeTapCallback(); removeLongPressCallback(); break; caseMotionEvent.ACTION_MOVE: finalintx=(int)event.getX(); finalinty=(int)event.getY(); //Belenientaboutmovingoutsideofbuttons if(!pointInView(x,y,mTouchSlop)){ //Outsidebutton removeTapCallback(); if((mPrivateFlags&PFLAG_PRESSED)!=0){ //Removeanyfuturelongpress/tapchecks removeLongPressCallback(); setPressed(false); } } break; } returntrue; } returnfalse; } 代码太长,我们只看重点,在ACTION_UP这个case当中,我们找到了关键的代码(第109行):performClick()。 至此,我们已经基本搞清楚了回调机制的整个过程。
publicbooleandispatchTouchEvent(MotionEventevent){
publicbooleanonTouchEvent(MotionEventevent){
相关文章