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

個(gè)人小站

Github

簡(jiǎn)書(shū)

前言

老司機(jī)們對(duì)于回調(diào)肯定熟悉得不能再熟悉了,但是新司機(jī)可能還是一臉懵逼的,我比較笨,當(dāng)年懵逼了好久,看夏安明的這一篇博客地址,雖然下邊的留言都是,寫(xiě)得好!懂了懂了!但是我當(dāng)時(shí)看了三遍還是不懂好嗎 - -,現(xiàn)在我站在我的角度,用我理解的方式給大家講解回調(diào),我這么笨都理解了,聰明的新司機(jī)們肯定也是可以的

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)類(lè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è)贏ctivity或者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è)線(xiàn)程操作,因?yàn)槿绻@個(gè)放在主線(xiàn)程,那在事件被響應(yīng)之前,我們的線(xiàn)程都是阻塞的,因?yàn)槠聊坏馁Y源被占用了,無(wú)法進(jìn)行其他操作,而在子線(xià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è)類(lèi) 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)始搞事情了

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

為啥要繞那一圈

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

最后

新司機(jī)們?nèi)绻X(jué)得有幫助,麻煩請(qǐng)給我的github項(xiàng)目點(diǎn)一個(gè)star
地址點(diǎn)這里

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容