問題
Activity中使用了ListView作為布局.當每一清單項目中含有預設能擷取焦點的子View時有可能會對ListView的某些事件有影響:
1. OnItemClick
2. OnItemLongClick
3. ContextMenu
這三個事件都無法正確響應.
對於ContextMenu.首先要在onCreate中註冊Activity的ContextMenu到ListView上:
registerForContextMenu(mListView);
要在onDestroy中取消註冊:
unregisterForContextMenu(mListView);
一個簡單的解法就是在Adapter中構建列表中的每個Item時,把相應事件Click或者LongClick加到具體的item上面.但這樣的話,會造成邏輯十分混亂.而且點擊Item或者長按Item時不會有Focus的Selection.此現象十分奇怪,必定另有隱情!
Google了一下,果然StackOverflow上面有很多關於ListView 的ContextMenu的問題,首先要注意的是基本的用法,也就是上面提到的要registerForContextMenu.之後就是奇怪事件發生的地方了:
如果ListView的清單項目中含有預設就有Focus的子View(如Checkbox)時,就無法擷取ContextMenu.
這時就需要把Checkbox的Focus屬性改變:
android:focusable="false"
然後就可以了.
其實,ContextMenu也就是長按事件來的.所以不光是ContextMenu出不來,連onItemClick, onItemLongClick也都出不來!如果把Item中的Checkbox的android:focusable屬性設定成false,就可以解決這些問題.
但是,沒有人能解釋清楚為啥會醬紫!
看了ListView的原始碼,ListView並沒有控制Click和LongClick以及ContextMenu(ContextMenu也是由長按事件觸發的).這些事件是由它的父類AbsListView來管理的.
在AbsListView中有一個用於檢查LongClick的Runnable,它裡面有這樣一個判斷:
View v = getChildAt(position);if (v != null && !v.hasFocusable()) { .... do long click handling which will trigger context menu.}
也就是說每當長按一個項時,會檢查其hasFocusable()傳回值,返回false時才做長按的動作.其他地方比如onTouchEvent時也都會如此的檢查,發現hasFocusable()為true時就直接返回!這裡注意,檢查的是所點擊的清單項目,所以如果清單項目的布局不一樣,現象就有可能不一樣!
那麼View.hasFocusable(),當View的focusable為true時返回true,或者當其有子View的focusable為true時,就返回true.簡單講就是View本身focusable為true或者有子View的focusable為true時就返回true.
因為,ListItem裡面有CheckBox,它的focusable屬性預設就是true的.所以就不會有ContextMenu彈出來.如果把CheckBox的focusable屬性設定為false,就可以正常的彈出了.
可以還是沒弄明白為啥要醬紫設計?需要牛人來指點下!
擴充
目前來看預設就有focusable屬性的有Button, CompoundButton, SeekBar, EditText,ImageButton,AutoCompleteTextView,WebView,WebTextView和它們的子類(怎麼找出來了呢,到系統的style檔案frameworks/base/core/res/res/value/styles.xml裡面搜尋focusable屬性為true).所以理論上來講,如果把這些Widget放入ListView裡面時,ListView的OnItemClick,OnItemLongClick以及ContextMenu都不會有效果.解決辦法就是把它們的focusable屬性設為false.