標籤:
Why ListView?
ListView 如果僅僅出於功能上的需求ListView可能沒有存在的必要,ListView能作的事情基本上ScrollView也能勝任。ListView存在的最根本的原因在於它的高效(如何?的?).ListView通過對象的複用從而減少記憶體的消耗,也減少了對象的建立從而也減少的cpu的消耗(在Androidk中建立View對象經常伴隨著解析xml)。ListView的本質是一張bitmap(當然所有的控制項文字等在螢幕上看到的最終都會變成bitmap),ListView會按照需求,根據Adapter提供的資訊把需要的Item畫出來顯示在螢幕上,當螢幕滾動的時候會重新計算Item的位置並繪製出新的bitmap顯示在螢幕上。這樣聽起來感覺可能不是很高效,但這樣帶的好處就是,每用為一第個Item 建立一個View對象,樣式一樣的對象可以共用一個View對象,減少了記憶體的消耗。而且ListView是事件驅動的,只有當需要的時候才會重新繪製,並且只會 繪製當前螢幕上所顯示的Items.
How To Use?
ListView 離不開Adapter,通常的作法建立一個類繼承BaseAdapter,Override getCount()和getView()等方法。產生這個類的對象,調用ListView的setAdapter()與ListViw進行綁定。
How Does It work?
ListView會調用跟其綁定的Adapter的getCount()方法知道有多少個Item需要展示,然後迴圈調用getView(int position, View convertView, ViewGroup parent)知道第position個Item該怎麼畫,並畫出來直到把當前的ListView的空間填滿。當Adapter當中的資料改變時,需調用notifyDataSetChanged ()告訴Adapter資料發生了變化或者給Adapter註冊一個觀察者registerDataSetObserver (DataSetObserver observer)。當Adapter得知與其繫結資料己發生改變時間,會再次調用getCount()方法,並迴圈調用getView(int position, View convertView, ViewGroup parent)重新整理當前頁面。
| Item1 |
| Item2 |
| Item3 |
| Item4 |
| Item5 |
| Item6 |
| Item7 |
| Item8 |
當這個ListView 向上滾動需要建立一個Item9 同時,有些對象(比如Item1 )不在顯示地區將看不到,這時android 將會把item1 的 引用傳遞給 Adapter.getView() 中的convertView這樣我們就不用再建立一個View來存放Item9,只需要把原來的item1對象作下修改,就可以重複使用了 ;我們也不用擔心convertView 是不是正確的類型,這個由系統保證,所以我們要作的就是把convertView 轉換(經常需要向下轉型)成我們自己的View 再給它賦值,in this case :(TextView) convertView.setText(“Item9”);
public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { //this would be first time to show the item,so we need to create it convertView = mInflater.inflate(R.layout.item, null); } //we grab the convertView,modify it and reuse it ((TextView) convertView.findViewById(R.id.text)).setText(DATA[position]); return convertView;}
調用方法所需要的消耗要比訪問變數高得多,而上面的代碼一次又一次的調用findViewById()方法,作著重複的事情。所以我們可以進一步進行如下最佳化: 建立一個類用來儲存一些View的引用,這樣我們就可以直接使用,而不用再調用findViewById().因為我們所儲存的只是引用不是對象本身,所以不用擔心會佔用大量記憶體
static class ViewHolder { TextView text; ImageView icon;}public View getView(int position, View convertView, ViewGroup parent){ ViewHolder holder; if (convertView == null) { convertView = mInflater.inflate(R.layout.list_item_icon_text, null); holder = new ViewHolder(); holder.text = (TextView) convertView.findViewById(R.id.text); holder.icon = (ImageView) convertView.findViewById(R.id.icon); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } //we store the reference ,so that we don’t have to call findViewById() over and over again holder.text.setText(DATA[position]); holder.icon.setImageBitmap((position & 1) == 1 ? mIcon1 : mIcon2); return convertView;}
是T-mobile g1上採集的的資料,現在看來可能現在不是很準確,但其效能上的差異還是很有參考價值的。
Tips & Tricks
ListView是為了大容量資料展示而設計的。如果資料量(Item的數量)不是很大,且用ListView實現起來比較麻煩,不妨換種思路,不使用ListView,而用ScrollView來實現。
如果Item資訊布局比較複雜或者Item的數量很多,出於效能的考慮,建議自訂一個View組件實現需要的功能,而不是組合其它控制項達到所要的效果。
ListView滾動變黑:在xml中給ListView增加一個屬性android:cacheColorHint="#00000000" 。當ListVIew中有很多Item,有時候需要快速的滾動。比如從第一個Item滾動到第600個Item這個時候,中間的很多Item對使用者來說意義不是很大,但android卻要調用 adapter.getView()方法將這些Item逐一畫出,並且因為滾動很快使用者不希望有任何的延遲。這在一些低端手機比如g1,是很難作到的。所以google工程師想出的一個辦法是在滾動的時候,讓螢幕變黑用一張黑色bitmap蓋住ListView,而不去繪製中間過程中的很多Item,從而提升效能。
Item有自己的背景蓋住了Selector游標:在xml中給ListView增加一個屬性:android:drawSelectorOnTop="true"這樣游標就會跑到Item上面的圖層,解決我們的問題。
Snippets
多選框ListView
帶有進度條的ListView,多個子線程重新整理各自的進度,如果子線程很多那麼重新整理就會變得很頻繁,我們可以由一個handler負責統一重新整理,這樣我們就要以增加一些額外條件限制重新整理的次數和條件
分批載入的原理很簡單,給ListView添加一個OnScrollListener監聽滾動事件,當使用者滾動到螢幕到特定的位置時載入新資料,並給LIstView加一個正在載入的footerView,當載入資料結束時再把這個footerView去掉。
ListView總結(多選框ListViiew,動態載入,多線程更新ListView中的進度條)