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);