個(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)這里