前段時間在修改一個關(guān)于按鍵事件點擊的bug,讓猿對Android的事件傳遞有了更深的了解,現(xiàn)在分享出來給大家。

需求描述
當(dāng)頁面中ListView處于選擇態(tài)時,當(dāng)?shù)谝淮吸c擊Back鍵需要將ListView的選擇態(tài)清除,點擊第二次Back鍵時,頁面才關(guān)閉退出。
需求實現(xiàn)
在頁面Activity中重寫了onBackPressed()方法,ListView中重寫了View的onKeyUp方法,當(dāng)ListView中如果是點擊態(tài)時,在onKeyUp中攔截事件,并清除ListView的選擇態(tài),并return True攔截按鍵事件返回。
問題描述
在調(diào)試過程中發(fā)現(xiàn):當(dāng)頁面中ListView處于選擇態(tài)時,點擊Back鍵時,頁面直接關(guān)閉。程序并不執(zhí)行ListView中的onKeyUp方法。
出現(xiàn)原因
在頁面初始化后,在選擇ListView為選擇態(tài)結(jié)束后,調(diào)用了ListView.clearFocus()方法,導(dǎo)致了ListView的onKeyUp方法不回調(diào)。
原因分析
我們大概都知道,Android事件傳遞從上到下傳遞,而對于KeyEvent來說,ViewGroup中分發(fā)事件方法代碼中有這樣一段:
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onKeyEvent(event, 1);
}
if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
== (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
if (super.dispatchKeyEvent(event)) {
return true;
}
} else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
== PFLAG_HAS_BOUNDS) {
if (mFocused.dispatchKeyEvent(event)) {
return true;
}
}
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
}
return false;
}
我們可以看到,在ViewGroup中,會對mFocused這個View進(jìn)行判斷,如果它不為空,有焦點,才會由他去分發(fā)事件,最終才有可能執(zhí)行它的回調(diào)方法。所以如果我們手動調(diào)用clearFocus后,使當(dāng)前FocusView為空,自然就無法最終執(zhí)行其回調(diào)方法了。
事實上,當(dāng)Activity中dispatcherKeyEvent時,是通過Activity中的PhoneWindow綁定的DecoderView進(jìn)行分發(fā)的,DecoderView最終通過ViewGroup的dispatcherKeyEvent將事件分發(fā)出去。詳細(xì)的事件分發(fā)流程會在后續(xù)文章中進(jìn)行詳細(xì)分析。請大家期待O(∩_∩)O