本文轉自 http://blog.sina.com.cn/s/blog_783ede030101bnm4.html 作者kiven
辭職3,4個月在家休息,本以為樓主要程式設計師逆襲,結果失敗告終繼續碼農生涯今天開始更新部落格。
正文。
專案中有個ListView內容比較複雜現在要新增長按刪除功能。樓主自然想到利用ListView的onItemLongClick事件來處理。結果可想而知在實際體驗中很不好,會出現失靈有些選項能觸發onItemLongClick事件有些卻沒反應。樓主去網上看了都是focusable要設定成false。但依照樓主的經驗這隻能解決都不能觸發onItemLongClick事件問題,像樓主遇到的有些可以有些失靈的估計不是一劑良藥。所以樓主具體分析了一下內部細節,希望能給後來的人提供一些幫助。
首先看一下foucsable造成的事件沒反應的問題。
原因:
若你的item中有button或者checkbox等控制元件,預設情況下焦點focus是最先交給這些子控制元件,而ListView的Item能被選中的基礎是它能獲取Focus焦點,所以,我們可以通過將ListView中Item中包含的所有控制元件的focusable屬性設定為false,這樣ListView的Item就自動獲得了Focus焦點的許可權,也就可以被選中了,同時也會響應onItemClickListener中的onItemClick()方法。
解決辦法:(以下兩種辦法任意一種都可)
1.將ListView的Item Layout中的所有子控制元件focusable屬性設定為false
2.將item layout的根控制元件設定屬性(推薦輕量級特別是修改別人的程式碼)
android:descendantFocusability=”blocksDescendant”
這樣Item Layout就遮蔽了所有子控制元件獲取Focus焦點的許可權,不需要針對Item Layout中的每一個控制元件重新設定focusable屬性
分析:
為什麼不設定focusable為false就不會觸發onItemLongClick事件呢。通過檢視AbsListView的原始碼你會發現如果focusable不為false跟本就不分發touch事件。程式碼如下:
附android:descendantFocusability用法簡析
開發中很常見的一個問題,專案中的listview不僅僅是簡單的文字,常常需要自己定義listview,自己的Adapter去繼承BaseAdapter,在adapter中按照需求進行編寫,問題就出現了,可能會發生點選每一個item的時候沒有反應,無法獲取的焦點。原因多半是由於在你自己定義的Item中存在諸如ImageButton,Button,CheckBox等子控制元件(也可以說是Button或者Checkable的子類控制元件),此時這些子控制元件會將焦點獲取到,所以常常當點選item時變化的是子控制元件,item本身的點選沒有響應。
這時候就可以使用descendantFocusability來解決啦,API描述如下:
該屬性是當一個為view獲取焦點時,定義viewGroup和其子控制元件兩者之間的關係。
屬性的值有三種:
beforeDescendants:viewgroup會優先其子類控制元件而獲取到焦點
afterDescendants:viewgroup只有當其子類控制元件不需要獲取焦點時才獲取焦點
blocksDescendants:viewgroup會覆蓋子類控制元件而直接獲得焦點
通常我們用到的是第三種,即在Item佈局的根佈局加上android:descendantFocusability=”blocksDescendants”的屬性就好了
實際情況根據上面的設定樓主的問題依然沒有解決,樓主堅信上面的文章的權威性,再次分析自己的程式碼。發現在adapter中convertView被設定clickable為true(另一位同事為了遮蔽item的黃色selector設定的待會兒樓主會介紹修改listview點選黃色背景修改)這必然導致touch事件被convertView截獲,由於convertView是item的根容器,所以就無法點選到listView使listview獲取touch事件。去掉item跟容器的clickable設定。果然能觸發onItemLongClick事件。失靈問題解決後發現在內容很少簡單的item沒問題,但在複雜的item時item內的子view不可避免的要設定click事件,而這個view又佔item的大部分地方,總不能每次要長按長按item狹小的空餘地方來觸發onItemLongClick事件吧。樓主這裡要給一個比較輕量級的解決方案。程式碼片段如下:
//Adapter 內部程式碼:
//holder.audioPanel這個view佔很大的地方但又必須實現click事件,還好它有也有longclick事件
holder.audioPanel.setOnClickListener(this);
holder.audioPanel.setOnLongClickListener(this);
//關鍵在這裡手動觸發listview的長按方法自然又回到了正常的邏輯了。
@Override
public boolean onLongClick(View v) {
mListView.performLongClick();
return false;
}
這下就完美解決觸發onItemLongClick事件了不管listview介面多麼複雜。
對了如何去除listview預設的黃色背景。樓主是這樣解決的:
附錄一下樓主網上看到的一些小細節:
1.ListView本身可不可以呼叫setOnClickListner()?
程式碼上可以,但是執行馬上會丟出異常,所以是不可以攔截Listview本身的click事件。
2.ListView.setOnItemClickListener設定的listener什麼時候會被呼叫?
當點選某行內容是會被呼叫,但是如果這行內容中包含Button,ImgButton等控制元件時就不會被呼叫,為什麼以及怎麼解決見後面。
3.ListView.setOnItemLongClickListener設定的listener什麼時候被呼叫?
當長按某一行時會被呼叫,而且在抬起之前就已經呼叫了。
4.收到LongClick的呼叫後還會呼叫click嗎?
這個要根據LongClick listener的返回值來決定。
Java程式碼
1.lv.setOnItemLongClickListener(new OnItemLongClickListener() {
2. public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
3. System.out.println("Item LONG clicked. Position:" + position);
4. return false;
5. }
6. });
如果返回false那麼click仍然會被呼叫。而且是先呼叫Long click,然後呼叫click。
如果返回true那麼click就會被吃掉,click就不會再被呼叫了。
5.監聽click以及long click影響彈出選單嗎?
click不影響;long click如果返回true那麼就會吃掉click事件,導致選單不能彈出。
附錄:
Android使用ListView應該注意的地方
在ListView中設定Selector為null會報空指標?
mListView.setSelector(null);//空指標
試試下面這種:
mListView.setSelector(new ColorDrawable(Color.TRANSPARENT));
如何讓ListView初始化的時候就選中一項?
ListView需要在初始化好資料後,其中一項需要呈選中狀態。所謂"選中狀態"就是該項底色與其它項不同,setSelection(position)只能定位到某個item,但是無法改變底色呈高亮。setSelection(position)只能讓某個item顯示在可見Item的最上面(如果Item超過一屏的話)! 就是所謂的firstVisibleItem啦!
如果想要實現效果可以在listview所繫結的adapter裡的getView函式裡去完成一些具體的工作。可以記下你要高亮的那個item的index,在getView函式裡判斷index(也就是position),如果滿足條件則載入不同的背景。
ListView的右邊滾動滑塊啟用方法?
很多開發者不知道ListView列表控制元件的快速滾動滑塊是如何啟用的,其實輔助滾動滑塊只需要一行程式碼就可以搞定,如果你使用XML佈局只需要在ListView節點中加入 android:fastScrollEnabled="true" 這個屬性即可,而對於Java程式碼可以通過myListView.setFastScrollEnabled(true); 來控制啟用,引數false為隱藏。
還有一點就是當你的滾動內容較小,不到當前ListView的3個螢幕高度時則不會出現這個快速滾動滑塊,該方法是AbsListView的基礎方法,可以在ListView或GridView等子類中使用快速滾動輔助。
1. 更新ListView中的資料,通過呼叫BaseAdapter物件的notifyDataSetChanged()方法:
mAdapter.notifyDataSetChanged();
2. 每個listview都有無效的位置,如第一行的前一行,最後一行的後一行,這個無效的位置是一個常量.
ListView.INVALID_POSITION
3. 有時我們需要在程式中通過點選按鈕來控制ListView行的選中,這就用到了在程式中如何使用程式碼來選擇ListView項.
mListView.requestFocusFromTouch();
mListView.setSelection(int index);
第一條語句並不是必須的,但是若你ListView項中含有Button,RadioButton,CheckBox等比ListView取得 焦點優先順序高的控制元件時,那麼第一條語句是你必須加的.
4. 同樣的,若你ListView項中含有Button,RadioButton,CheckBox等比ListView取得 焦點優先順序高的控制元件時,ListView的setOnItemClickListener是不被執行的,這時你需要在你的xml檔案中對這些控制元件新增 android:focusable="false" 注意這條語句要放在xml檔案中修改,在程式碼中使用是無效的.
5. 如何保持ListView的滾動條一直顯示,不隱藏呢: xml檔案中做如下修改 android:fadeScrollbars="false"
6. ListView本身有自己的按鍵事件,即你不需要設定方向鍵的標識,按下方向鍵ListView就會有預設的動作,那如何進行控制,編寫自己的onKey呢,你需要在Activity中重寫dispatchKeyEvent(KeyEvent event);方法,在這裡面定義你自己的動作就可以了
ListView 自定義滾動條樣式:
<ListView android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:stackFromBottom="true"//從下開始顯示條目
android:transcriptMode="normal"
android:fastScrollEnabled="true"
android:focusable="true"
android:scrollbarTrackVertical="@drawable/scrollbar_vertical_track"
android:scrollbarThumbVertical="@drawable/scrollbar_vertical_thumb"
/>
//scrollbar_vertical_track,crollbar_vertical_thumb自定義的xml檔案,放在Drawable中,track是指長條,thumb是指短條
去掉ListView Selector選種時黃色底紋一閃的效果:
Xml程式碼
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@android:color/transparent"/>
<corners android:radius="0dip" />
</shape>
//listview.setSelector(R.drawable.thisShape);
或者還有一種辦法:
在Adapter中重寫public boolean isEnabled(int position)方法,將其返回false就可以了,推薦採用此種辦法,具體見http://gundumw100.iteye.com/admin/blogs/850654
Java程式碼
public boolean isEnabled(int position) {
// TODO Auto-generated method stub
return false;
}
ListView幾個比較特別的屬性
首先是stackFromBottom屬性,這隻該屬性之後你做好的列表就會顯示你列表的最下面,值為true和false
android:stackFromBottom="true"
第二是transciptMode屬性,需要用ListView或者其它顯示大量Items的控制元件實時跟蹤或者檢視資訊,並且希望最新的條目可以自動滾動到可視範圍內。通過設定的控制元件transcriptMode屬性可以將Android平臺的控制元件(支援ScrollBar)自動滑動到最底部。
android:transcriptMode="alwaysScroll"
第三cacheColorHint屬性,很多人希望能夠改變一下它的背景,使他能夠符合整體的UI設計,改變背景背很簡單隻需要準備一張圖片然後指定屬性 android:background="@drawable/bg",不過不要高興地太早,當你這麼做以後,發現背景是變了,但是當你拖動,或者點選list空白位置的時候發現ListItem都變成黑色的了,破壞了整體效果。
如果你只是換背景的顏色的話,可以直接指定android:cacheColorHint為你所要的顏色,如果你是用圖片做背景的話,那也只要將android:cacheColorHint指定為透明(#00000000)就可以了
第四divider屬性,該屬性作用是每一項之間需要設定一個圖片做為間隔,或是去掉item之間的分割線android:divider="@drawable/list_driver" 其中 @drawable/list_driver 是一個圖片資源,如果不想顯示分割線則只要設定為android:divider="@drawable/@null" 就可以了
第五fadingEdge屬性,上邊和下邊有黑色的陰影android:fadingEdge="none" 設定後沒有陰影了~
第五scrollbars屬性,作用是隱藏listView的滾動條,android:scrollbars="none"與setVerticalScrollBarEnabled(true);的效果是一樣的,不活動的時候隱藏,活動的時候也隱藏
第六fadeScrollbars屬性,android:fadeScrollbars="true" 配置ListView佈局的時候,設定這個屬性為true就可以實現滾動條的自動隱藏和顯示。
如何在使用gallery在flinging拖動時候不出現選擇的情況?
這時候需要注意使用
gallery.setCallbackDuringFling(false)
如何讓ListView自動滾動?
注意stackFromBottom以及transcriptMode這兩個屬性。類似Market客戶端的低端不斷滾動。
<ListView android:id="listCWJ"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:stackFromBottom="true"
android:transcriptMode="alwaysScroll"
/>
如何遍歷listView 的的單選框?
Java程式碼
ListView listView = (ListView)findViewById(R.id.配置檔案中ListView的ID);
//全選遍歷ListView的選項,每個選項就相當於佈局配置檔案中的RelativeLayout
for(int i = 0; i < listView.getChildCount(); i++){
View view = listView.getChildAt(i);
CheckBox cb = (CheckBox)view.findViewById(R.id.CheckBoxID);
cb.setChecked(true);
}
如何讓ListView中TextView的字型顏色跟隨焦點的變化?
我們通常需要ListView中某一項選中時,他的字型顏色和原來的不一樣。 如何設定字型的顏色呢? 在佈局檔案中TextColor一項來設定顏色,但是不是隻設定一種顏色,而是在不同的條件下設定不同的顏色: 下面是個例子:
Xml程式碼
<?xml version="1.0" encoding="utf-8" ?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false" android:color="@color/orange"></item>
<item android:state_window_focused="false" android:color="@color/orange"></item>
<item android:state_pressed="true" android:color="@color/white"></item>
<item android:state_selected="true" android:color="@color/white"></item>
<item android:color="@color/orange"></item>
</selector>
在獲取焦點或者選中的情況下設定為白色,其他情況設定為橘黃色。
如何自定義ListView行間的分割線?
所有基於ListView或者說AbsListView實現的widget控制元件均可以通過下面的方法設定行間距的分割線,分割線可以自定義顏色、或圖片。
在ListView中我們使用屬性android:divider="#FF0000" 定義分隔符為紅色,當然這裡值可以指向一個drawable圖片物件,如果使用了圖片可能高度大於系統預設的畫素,可以自己設定高度比如6個畫素android:dividerHeight="6px" ,當然在Java中ListView也有相關方法可以設定。
ListView不通過notifyDataSetChanged()更新指定的Item
Listview一般大都是通過notifyDataSetChanged()來更新listview,但通過notifyDataSetChanged()會把介面上現實的的item都重繪一次,這樣會影響ui效能。
可以通過更新指定的Item提高效率,虛擬碼如下:
Java程式碼
private void updateView(int itemIndex){
int visiblePosition = yourListView.getFirstVisiblePosition();
View v = yourListView.getChildAt(itemIndex - visiblePosition);//Do something fancy with your listitem view
TextView tv = (TextView)v.findViewById(R.id.sometextview);
tv.setText("Hi! I updated you manually");
}