1. ListView的OnItemClickListener不被觸發的另外一種情況
如上圖,在一個ItemView中,只有一個TextView位於最左側,他的右側是空白區域,沒有任何控件,當點擊右側區域時,並不會觸發OnItemClickListener,當點擊TextView所在的區域時,就能觸發這個事件。
看看這個事件的執行流程
右側空白的部分沒有View控件,也就是說雖然用手指點擊了這一部分,但是沒有view獲取焦點,Android的事件觸發是從頂層view一層層往下尋找的,如果有view獲取焦點,就交給這個view處理,如果沒有,就交給activity處理。
click事件與touch事件的傳播方式是不同的
給ListView同時添加對touch和itemClick的監聽事件,他們的觸發順序是:
actionDown--------》action up---------》onItemClick
他們的執行流程為:
先按照touch事件的處理流程進行,然後在進行click事件的處理,這就說明,當用戶按下屏幕,產生了兩個事件,一個是touch事件,一個是click事件,添加到主線程的隊列中。
用戶手指觸摸屏幕,滾動ListView,看起來也進行了click操作,但是結果是,只觸發了touch事件,沒有觸發click事件。
2. ListView 獲取焦點和ItemView獲取焦點之間的關系
2.1 ListView不獲取焦點,ItemView能獲取焦點嗎?
通過設置ItemView的android:focusable="true" android:focusableInTouchMode="true"屬性,可以使ItemView在Touch mode 下獲取焦點,默認情況下,Touch mode下ItemView,menu等等控件都是不能獲取焦點的。只有ListView獲取了焦點之後,ItemView才能獲取焦點。
實驗一:
設置ListView的focusable屬性為true,ItemView的android:focusable="true" android:focusableInTouchMode="true",自定義touch事件監聽器,重寫onTouch方法
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//clear();
int x = (int) event.getX();
int y = (int) event.getY();
int position = mListView.pointToPosition(x, y);
int firstVisiblePosition = mListView.getFirstVisiblePosition();
view = mListView.getChildAt(position-firstVisiblePosition);
if(view==null) return false;
if(view.isFocusable()){
Log.i(tag, "ItemView is focusable ");
}
if(view.isFocusableInTouchMode()){
Log.i(tag, "ItemView is focusable in touchMode");
}
if(view.isInTouchMode()){
Log.i(tag, "device is in touch mode.");
}
if(mListView.isFocusable()){
Log.i(tag, "mListView is focusable ");
}
if(mListView.isFocusableInTouchMode()){
Log.i(tag, "mListView isFocusableInTouchMode in touchMode");
}
if(view.isFocused()){
Log.i(tag, "ItemView have get focus ");
}
if(mListView.isFocused()){
Log.i(tag, "mListView have get focus ");
}
if(view.isPressed()){
Log.i(tag, "ItemView have get pressed ");
}
break;
case MotionEvent.ACTION_UP:
if(view==null) return false;
Log.i(tag, "OnTouchListener: up is working ");
if(view.isFocused()){
Log.i(tag, "ItemView have get focus ");
}
break;
default:
break;
}
return false;
}
打印結果為:
ItemView is focusable
ItemView is focusable in touchMode
device is in touch mode.
mListView is focusable
mListView isFocusableInTouchMode in touchMode
mListView have get focus
OnTouchListener: up is working
當用戶觸摸屏幕的時候,觸發了touch事件,但是只有ListView獲取了焦點,itemView卻沒有獲得焦點,說明itemView在默認狀態下,即時設置了能獲取焦點,能在touchmode下獲取焦點,實際上也是不能的
而ListView的focusable屬性即時不設為true,也是能夠獲取焦點的,那麼如何讓ItemView獲取焦點呢,有兩個方法,一個是View類的requestFocus()方法,一個是ListView的requestChildFocus(child, focused)方法,還有就是requestFocusFromTouch()方法,該方法是View類的方法,ListView繼承了該方法。
我們讓itemView調用requestFocus()方法或者requestFocusFromTouch()方法,這時itemView獲取了焦點,但是ListView沒有獲取焦點,這說明一個視圖中只能有一個view獲取焦點。
去掉itemView的android:focusableInTouchMode="true"屬性,調用requestFocusFromTouch()方法,可以強制使itemViw獲取焦點。ListView依舊沒有獲取焦點。
總結:
1. 當使用導航鍵上下左右滾動時,android框架會自動讓view獲取焦點(獲取焦點後,就會高亮顯示),然而當用戶用手觸摸屏幕的時候,就不需要讓view自動獲取焦點了,也就是說,當用戶點擊了屏幕上的某個控件時,該控件就沒有必要自動獲取焦點了,因為用戶知道自己操作的是哪個控件,當用戶觸摸手機屏幕,就會進入touch mode模式。
2. 在touch mode 模式下,有些控件是不會自動獲取焦點的,但是還有些控件會,通過isFocusableInTouchMode()可以知道該控件在該touch模式下能否獲得焦點,TextView是默認不能獲得焦點的,ListView默認能夠獲得焦點,EditText等文字編輯類控件都可以。通過設置android:focusableInTouchMode="true"貌似可以使view控件獲取焦點,但是實際上並不會是veiw控件獲取焦點,還需要手動調用requestFocusFromTouch()方法或者requestFocus()方法才能真正獲取焦點。這裡推薦使用requestFocusFromTouch()方法,即時不用設置android:focusableInTouchMode="true",也能強制使控件獲取焦點。
3. 獲取ListView中有那個控件獲取了焦點的方法
mListView.findFocus();
mListView.getFocusedChild();
4. 通過ListView的setItemsCanFocus(true)方法並不可以使ItemView在touch mode下可以獲取焦點,他只是表明在由ListAdapter創建的視圖中,可包含能獲得焦點的項目。
滾動事件發生在touch事件的後面,這種說法是不對的,通過實驗可以得到onScroll方法的執行是在touch事件之前的,並且每一次觸摸屏幕,先觸發這個方法,然後才觸發touch事件,此外,當我們第一次進入列表界面時,onScroll方法也多次被調用,第一次是在執行onCreate方法時被調用,這事還沒有生成界面,所以visibleItemCount參數為0,然後的幾次調用就有了界面了,visibleItemCount也被賦予界面上顯示的item的個數,顯示不全的也算。差不多有兩次,這兩次的調用也是不一樣的,看看這三次調用這個方法的不同
當用手觸摸屏幕上的某一項時,也會首先觸發這個方法,然後才是touch事件
通過重寫onTouch方法,可以實現當用戶觸摸屏幕的時候,itemView獲取焦點,並且變色,但是出現了兩個問題:
1. 如果ListView可以顯示多頁,可以看到每頁上又有一個ItemViw獲取了焦點,不知道為什麼?
推測:Adapter中getView方法的第二個蠶食convertView是復用的,估計是生成新的頁面的時候復用了獲取焦點的veiw,這裡還要看源碼
2. 當滾動到非第一頁的時候,觸發屏幕,並不能使觸發點所在的ItemView獲取焦點?
ListView的getChildAt(int index)方法的參數index,與Adapter的getView方法的第一個參數position是不一樣的,每個界面顯示幾個item,就建立從0到界面顯示個數的索引,比如一個屏幕上顯示8條記錄,那麼索引就是
0---7,翻頁了,仍然會建立類似的索引,因此應該計算出觸發點的item在屏幕上的索引。
int x = (int) event.getX();
int y = (int) event.getY();
int position = mListView.pointToPosition(x, y);
int firstVisiblePosition = mListView.getFirstVisiblePosition();
view = mListView.getChildAt(position-firstVisiblePosition);