通过点击事件监听 setOnClickListener 彻底理解回调-Android
前言
老司機(jī)們對(duì)于回調(diào)肯定熟悉得不能再熟悉了,但是新司機(jī)可能還是一臉懵逼的,我比較笨,當(dāng)年懵逼了好久,看夏安明的這一篇博客地址,雖然下邊的留言都是,寫得好!懂了懂了!但是我當(dāng)時(shí)看了三遍還是不懂好嗎 - -,現(xiàn)在我站在我的角度,用我理解的方式給大家講解回調(diào),我這么笨都理解了,聰明的新司機(jī)們肯定也是可以的
setOnClickListener 分析
setOnCLickLinstener,只要寫過 Android 的同學(xué)應(yīng)該都見過,大家都知道是點(diǎn)擊事件監(jiān)聽,但是是怎么實(shí)現(xiàn)的呢?對(duì),你沒有猜錯(cuò),就是回調(diào)
你在 onClick(View view)中寫的方法,就是一個(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)都是靠接口來進(jìn)行的
并且,你實(shí)現(xiàn)了 onClick(View view)方法后,這個(gè)方法并沒有在我們的 Activity 或者 Fragment 中調(diào)用,那為什么他生效了呢?這就是回調(diào),你實(shí)現(xiàn)了他,而他卻是在另一個(gè)地方調(diào)用的
那是在什么地方調(diào)用的呢?
我們點(diǎn)進(jìn) setOnClickListener 方法中一探虛實(shí)
于是我們跳到了 View.java,原來這個(gè)方法是寫在 View 中的,這時(shí)你想到,第一行代碼中說了,我們的控件都繼承于 View,原來如此
public void setOnClickListener(@Nullable OnClickListener l) {if (!isClickable()) {setClickable(true);}getListenerInfo().mOnClickListener = l;}setOnClickListener 方法就如同我們調(diào)用時(shí)的那樣,傳入一個(gè) OnClickListener 對(duì)象作為參數(shù),那我們來看一看 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 里邊來了
記好了哦!
我們來看看 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ì)象
然后我們來看看 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;}原來是一個(gè)內(nèi)部靜態(tài)類,成員包括各種事件的監(jiān)聽接口,其中包括
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) 方法
讓我們來梳理一遍流程,我們?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)聽著屏幕的點(diǎn)擊事件,在我們觸摸到屏幕的時(shí)候進(jìn)行響應(yīng),這是一個(gè)線程操作,因?yàn)槿绻@個(gè)放在主線程,那在事件被響應(yīng)之前,我們的線程都是阻塞的,因?yàn)槠聊坏馁Y源被占用了,無法進(jìn)行其他操作,而在子線程中,系統(tǒng)監(jiān)聽著屏幕的活動(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 賦值給 mLinstenermLinstener = 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)用開始搞事情了
你如果看不懂的話,自己寫一遍,這就是 Android 中回調(diào)的一般寫法,你可以在各種自定義 View 中用來了,用著用著就理解了
為啥要繞那一圈
那一圈保證了 View 中只有一個(gè) mOnClickListener 對(duì)象,保證了我們一次只執(zhí)行一次 onClick() 方法
總結(jié)
以上是生活随笔為你收集整理的通过点击事件监听 setOnClickListener 彻底理解回调-Android的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C#接口中为什么不能像java那样使用s
- 下一篇: android sina oauth2.