徹底理解Android回調(diào)-通過(guò)點(diǎn)擊事件監(jiān)聽(tīng) setOnClickListener

setOnClickListener 分析

setOnCLickLinstener,只要寫(xiě)過(guò) Android 的同學(xué)應(yīng)該都見(jiàn)過(guò),大家都知道是點(diǎn)擊事件監(jiān)聽(tīng),但是是怎么實(shí)現(xiàn)的呢?對(duì),你沒(méi)有猜錯(cuò),就是回調(diào)

你在 onClick(View view)中寫(xiě)的方法,就是一個(gè)回調(diào)方法,你仔細(xì)想一想,這個(gè)方法是在你傳的參數(shù) new View.OnClickListener()中的方法,你再仔細(xì)的想一想,為什么你傳入了 new View.OnClickListener()這個(gè)參數(shù),Android Studio 就會(huì)自動(dòng)補(bǔ)全,讓你去實(shí)現(xiàn) onClick(View view)這個(gè)方法呢? 一切都在你想象之中,OnClickListener 就是一個(gè)接口,new 出一個(gè)接口,你就得實(shí)現(xiàn)他里邊的抽象方法,在 Android 中,大多數(shù)回調(diào)都是靠接口來(lái)進(jìn)行的

并且,你實(shí)現(xiàn)了 onClick(View view)方法后,這個(gè)方法并沒(méi)有在我們的 Activity 或者 Fragment 中調(diào)用,那為什么他生效了呢?這就是回調(diào),你實(shí)現(xiàn)了他,而他卻是在另一個(gè)地方調(diào)用的

那是在什么地方調(diào)用的呢?

我們點(diǎn)進(jìn) setOnClickListener 方法中一探虛實(shí)

于是我們跳到了 View.java,原來(lái)這個(gè)方法是寫(xiě)在 View 中的,這時(shí)你想到,第一行代碼中說(shuō)了,我們的控件都繼承于 View,原來(lái)如此

public void setOnClickListener(@Nullable OnClickListener l) {

? ? ? ? if (!isClickable()) {

? ? ? ? ? ? setClickable(true);

? ? ? ? }

? ? ? ? getListenerInfo().mOnClickListener = l;

? ? }

setOnClickListener 方法就如同我們調(diào)用時(shí)的那樣,傳入一個(gè) OnClickListener 對(duì)象作為參數(shù),那我們來(lái)看一看 OnClickListener 是個(gè)啥子

? ? public interface OnClickListener {

? ? ? ? /**

? ? ? ? * Called when a view has been clicked.

? ? ? ? *

? ? ? ? * @param v The view that was clicked.

? ? ? ? */

? ? ? ? void onClick(View v);

? ? }

果然不出你所料,就是個(gè) interface

然后注意這一行

getListenerInfo().mOnClickListener = l;

把我們傳入的 OnClickListener 對(duì)象賦值給了 getListenerInfo().mOnClickListener,記住我們傳入的 OnClickListener 對(duì)象就相當(dāng)于攜帶了我們實(shí)現(xiàn)的 onClick(View view)方法,進(jìn)到 View 里邊來(lái)了

記好了哦!

我們來(lái)看看 getListenerInfo()方法

ListenerInfo getListenerInfo() {

? ? ? ? if (mListenerInfo != null) {

? ? ? ? ? ? return mListenerInfo;

? ? ? ? }

? ? ? ? mListenerInfo = new ListenerInfo();

? ? ? ? return mListenerInfo;

? ? }

getListenerInfo()返回一個(gè) ListenerInfo,如果 mListenerInfo 已經(jīng)存在,就返回,如果不存在,就 new 一個(gè)返回,也許你已經(jīng)知道,或許不久后你就知道,這叫單例模式,保證只有一個(gè) ListenerInfo 對(duì)象

然后我們來(lái)看看 ListenerInfo 又是個(gè)啥子

static class ListenerInfo {

? ? ? ? protected OnFocusChangeListener mOnFocusChangeListener;

? ? ? ? private ArrayList<OnLayoutChangeListener> mOnLayoutChangeListeners;

? ? ? ? protected OnScrollChangeListener mOnScrollChangeListener;

? ? ? ? private CopyOnWriteArrayList<OnAttachStateChangeListener> mOnAttachStateChangeListeners;

? ? ? ? public OnClickListener mOnClickListener;

? ? ? ? protected OnLongClickListener mOnLongClickListener;

? ? ? ? protected OnContextClickListener mOnContextClickListener;

? ? ? ? protected OnCreateContextMenuListener mOnCreateContextMenuListener;

? ? ? ? private OnKeyListener mOnKeyListener;

? ? ? ? private OnTouchListener mOnTouchListener;

? ? ? ? private OnHoverListener mOnHoverListener;

? ? ? ? private OnGenericMotionListener mOnGenericMotionListener;

? ? ? ? private OnDragListener mOnDragListener;

? ? ? ? private OnSystemUiVisibilityChangeListener mOnSystemUiVisibilityChangeListener;

? ? ? ? OnApplyWindowInsetsListener mOnApplyWindowInsetsListener;

? ? }

原來(lái)是一個(gè)內(nèi)部靜態(tài)類,成員包括各種事件的監(jiān)聽(tīng)接口,其中包括

public OnClickListener mOnClickListener;

誒喲,和我們傳入的一樣的一個(gè) OnClickListener 接口引用,于是繞了這么一大圈(我們先不管為啥繞),我們傳入的持有我們實(shí)現(xiàn)的 onClick(View view)方法的 OnClickListener 接口對(duì)象(還記得嗎?),被賦值到了 View 中的 mListenerInfo 中的 mOnClickListener 對(duì)象,也就是,我們實(shí)現(xiàn)的 onCLick(View view) 方法,被 mListenerInfo.mOnClickListener 持有了

這時(shí),你應(yīng)該想到了,我們實(shí)現(xiàn)的 onClick(View view)應(yīng)該就是在 View 中被調(diào)用了,bingo !

? ? public boolean performClick() {

? ? ? ? final boolean result;

? ? ? ? final ListenerInfo li = mListenerInfo;

? ? ? ? if (li != null && li.mOnClickListener != null) {

? ? ? ? ? ? playSoundEffect(SoundEffectConstants.CLICK);

? ? ? ? ? ? li.mOnClickListener.onClick(this);

? ? ? ? ? ? result = true;

? ? ? ? } else {

? ? ? ? ? ? result = false;

? ? ? ? }

? ? ? ? sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);

? ? ? ? return result;

? ? }

從字面意思理解,這個(gè)方法就是執(zhí)行 Click 的方法, 他將 mListenerInfo 對(duì)象傳給了一個(gè)靜態(tài)的 ListenerInfo 對(duì)象, li,后邊的故事大家都知道了

li.mOnClickListener.onClick(this);

這個(gè)方法執(zhí)行了點(diǎn)擊事件,并調(diào)用了我們實(shí)現(xiàn)的 onClick(View view) 方法

讓我們來(lái)梳理一遍流程,我們?cè)?Activity 或者 Fragment 中調(diào)用

View.setOnClickListener 方法,傳入一個(gè) OnCLickListener 對(duì)象,實(shí)現(xiàn)了 onCLick(View view)方法,然后在 View 中的某個(gè)地方,我們實(shí)現(xiàn)的 onCLick(View view)被調(diào)用,實(shí)現(xiàn)了回調(diào),這就是回調(diào)的流程

異步

回調(diào)有什么用呢,就是異步,想象一下,系統(tǒng)一直在監(jiān)聽(tīng)著屏幕的點(diǎn)擊事件,在我們觸摸到屏幕的時(shí)候進(jìn)行響應(yīng),這是一個(gè)線程操作,因?yàn)槿绻@個(gè)放在主線程,那在事件被響應(yīng)之前,我們的線程都是阻塞的,因?yàn)槠聊坏馁Y源被占用了,無(wú)法進(jìn)行其他操作,而在子線程中,系統(tǒng)監(jiān)聽(tīng)著屏幕的活動(dòng),然后在我們觸摸時(shí),調(diào)用 performClick()方法實(shí)現(xiàn)了點(diǎn)擊,并且調(diào)用了 onClick(View view)方法實(shí)現(xiàn)了點(diǎn)擊事件的回調(diào),我們就可以恰恰剛好在點(diǎn)擊時(shí)間觸發(fā)的時(shí)候,進(jìn)行我們想要的操作,也就是我們實(shí)現(xiàn)的 on CLick(View view)方法

半偽代碼實(shí)現(xiàn)一個(gè)回調(diào)給你看

A.class

//先定義一個(gè)接口

public interface Listener {

? ? //回調(diào)方法

? ? void 回調(diào)方法();

}

//申明一個(gè)接口

private Listener mLinstener;

//一個(gè) set 接口的方法

public void setListener(Listener listener) {

? ? //把傳入的 listener 賦值給 mLinstener

? ? mLinstener = listener

}

...

//在某個(gè)地方,進(jìn)行某個(gè)操作的時(shí)候

private void 某個(gè)操作() {

? ? //回調(diào)方法執(zhí)行

? ? mLinstener.回調(diào)方法();

}

另一個(gè)類 B.class

private A a = new A();

a.setListener(new Linstener() {

? ? public void 回調(diào)方法() {

? ? ? ? //我要在 A 中某個(gè)操作()執(zhí)行的時(shí)候要搞的事情

? ? ? ? 搞事情阿搞事情();

? ? }

});

然后在某個(gè)操作()調(diào)用的時(shí)候,我們的回調(diào)方法()也就被調(diào)用開(kāi)始搞事情了

你如果看不懂的話,自己寫(xiě)一遍,這就是 Android 中回調(diào)的一般寫(xiě)法,你可以在各種自定義 View 中用來(lái)了,用著用著就理解了

為啥要繞那一圈

那一圈保證了 View 中只有一個(gè) mOnClickListener 對(duì)象,保證了我們一次只執(zhí)行一次 onClick() 方法

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 明天又要出發(fā)了,數(shù)不清這是第幾次離開(kāi)了。從16歲就開(kāi)始飽受離別之苦,從高中時(shí)期的幾百公里到現(xiàn)在大學(xué)的幾千公里,我越...
    火炎焱燚y閱讀 366評(píng)論 0 1
  • 好在新公司不怎么忙,看上去也挺有錢的,唯一快樂(lè)的事情是可以準(zhǔn)時(shí)下班。領(lǐng)導(dǎo)給我分的工作是招商,但是我其實(shí)并不擅長(zhǎng),不...
    巴賽閱讀 218評(píng)論 0 2
  • 1 李玉真的要崩潰了! 她已經(jīng)兩個(gè)月零三天沒(méi)有見(jiàn)過(guò)丈夫的面了。從知道懷孕時(shí)的欣喜若狂到持續(xù)兩個(gè)月聯(lián)系不到丈夫的悲哀...
    愿至天成閱讀 487評(píng)論 0 1
  • 大家好!我叫康燦彬,大家叫我康導(dǎo)游就可以了。今天我?guī)Т蠹覅⒂^一下和陽(yáng)廣場(chǎng),但記住幾點(diǎn),垃圾不可以亂丟,要扔進(jìn)...
    康燦彬閱讀 278評(píng)論 0 2

友情鏈接更多精彩內(nèi)容