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() 方法