天天看點

ListView事件的研究

1. listview的onitemclicklistener不被觸發的另外一種情況

ListView事件的研究

如上圖,在一個itemview中,隻有一個textview位于最左側,他的右側是空白區域,沒有任何控件,當點選右側區域時,并不會觸發onitemclicklistener,當點選textview所在的區域時,就能觸發這個事件。

看看這個事件的執行流程

ListView事件的研究

右側空白的部分沒有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:                 log.i(tag, "ontouchlistener: up is working ");             default:             }             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的個數,顯示不全的也算。差不多有兩次,這兩次的調用也是不一樣的,看看這三次調用這個方法的不同 圖一:
ListView事件的研究
圖二:
ListView事件的研究
圖三:
ListView事件的研究
當用手觸摸螢幕上的某一項時,也會首先觸發這個方法,然後才是touch事件
ListView事件的研究
通過重寫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);

繼續閱讀