1. 程式人生 > >ListView列表控制元件的介紹和效能優化

ListView列表控制元件的介紹和效能優化

ListView列表控制元件

一、ListView顯示資料的原理:mvc模式

m:mode 資料(用javabean規範封裝)

v:view ListView

c:adapter 介面卡,負責把資料展示到ListView上

二、ListView最常用介面卡

BaseAdapter、SimpleAdapter、ArrayAdapter

三、ListView顯示資料的步驟

1.建立ListView

2.自定義ListView的介面卡繼承BaseAdapter,重寫baseAdapter的getCount方法和getView方法

3.建立自定義ListView的介面卡

4.ListView設定介面卡:listView.setAdapter(adapter);

private class ListViewAdapter extends BaseAdapter{

        //返回需要展示的資料的條數
        @Override
        public int getCount() {
            return 200;
        }
        //返回指定position位置對應的資料物件,一般很少用
        @Override
        public Object getItem(int position) {
            return null;
        }
        //返回position位置對應id
        @Override
        public long getItemId(int position) {
            return 0;
        }
        /**
         * 獲取一個view,會作為listview的第position個item條目出現,用來顯示listview的資料
         * 
         * @param convertView 歷史快取物件
         */
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View view ;
            if (convertView == null) {
                //建立新的View物件 
                view = View.inflate(MainActivity.this, R.layout.listview_item,null);
            }else{
                //複用歷史快取View物件
                view = convertView;
            }
            TextView tv=(TextView)view.findViewById(R.id.tv);
            tv.setText("item:" + position);
            return view;
        }           
    }

四、ListView的效能優化

1.ListView的奇怪現象

問題:

如果ListView的高度設為包裹內容,getView方法會多次呼叫

原因:

如果height設為”wrap_content”,為了把所有條目都顯示出來,會多次校驗資料是否可在螢幕顯示完,(校驗1次不保準,再多次校驗)將多次呼叫getView方法

解決:以後再使用ListView的時候,高度設定為填充父窗體 android:layout_height=”match_parent”

2.避免記憶體溢位優化:ListView複用歷史快取View物件

問題:如果不復用歷史快取View物件,當ListView的條目數過多,向下滑的很深時,會報錯。
報錯資訊: E/dalvikvm-heap(2636): Out of memory on a -294967280-byte allocation. 記憶體溢位
原因:不停建立新的View,消耗記憶體。為每一個Item都建立一個View物件,必將佔用很多記憶體空間。從xml中生成View,這是屬於IO操作,是耗時操作,所以必將影響效能
解決:可以對消失在螢幕的View進行快取,當往下拖動時複用歷史快取,這樣就只建立螢幕能顯示的View個數的數量的View。getView方法有個引數convertView就是可以複用的歷史快取View物件。這個物件也可能為空,當它為空的時候,表示該條目view第一次建立,所以我們需要inflate一個view出來。
原理:Android提供了一個叫做Recycler(反覆迴圈)的構件,就是當ListView的Item從滾出螢幕視角之外,對應Item的View會被快取到Recycler中,相應的會從生成一個Item,而此時呼叫的getView中的convertView引數就是滾出螢幕的快取Item的View,所以說如果能重用這個convertView,就會大大改善效能。
public View getView(int position, View convertView, ViewGroup parent) {
TextView tv;
if (convertView == null) {
// 建立新的view 物件
tv = new TextView(MainActivity.this);
System.out.println(“建立新的view物件—” + position);
} else {
System.out.println(“複用歷史快取物件—” + position);
view = (TextView) convertView;
}
tv.setText(“4月14日科比告別戰倒計時:” + position);
return tv;
}

3.item子控制元件顯示卡頓優化:ViewHolder重用機制

問題:item中的子控制元件很多時載入慢。findViewById是到xml檔案中去查詢對應的id,可以想象如果元件多的話也是挺費事的,如果我們可以讓view內的元件也隨著view的複用而複用,那該是多美好的一件事。

原因:在getView()方法中的操作是先從xml中建立view物件(inflate操作,我們採用了重用convertView方法優化),然後在這個view去findViewById,找到每一個item的子View的控制元件物件,如:ImageView、TextView等。這裡的findViewById操作是一個樹查詢過程,也是一個耗時的操作。

解決:谷歌推薦了一種優化方法來做應對,那就是重新建一個內部靜態類,裡面的成員變數跟view中所包含的元件個數型別相同

private static class ViewHolder {}

基本思路就是在convertView為null的時候,我們不僅重新inflate出來一個view,並且還需要進行findviewbyId的查詢工作,但是同時我們還需要獲取一個ViewHolder類的物件,並將findviewById的結果賦值給ViewHolder中對應的成員變數。最後將holder物件與該view物件“綁”在一塊。當convertView不為null時,我們讓view=converView,同時取出這個view對應的holder物件,就獲得了這個view物件中的子控制元件,就是holder中的成員變數,這樣在複用的時候,我們就不需要再去findViewById了,只需要在最開始的時候進行數次查詢工作就可以了。這裡的關鍵在於如何將view與holder物件進行繫結,那麼就需要用到兩個方法:View中的setTag和getTag方法了。
經過上面的做法,可能大家感覺不太到優化的效果,根據Google的文件,實際優化效果在百分之5左右。

4.網路資料過多載入緩慢優化

問題:

ListView如果顯示本地的List集合中的內容,List的長度也只有100個,我們可以毫不費力一次性載入完這100個數據;但是實際應用中,我們往往會需要使用Listview來顯示網路上的內容,比如說我們拿使用ListView顯示新聞為例:假如網路情況很好,我們使用的手機也許能夠一下子載入完所有新聞資料,然後顯示在ListView中,使用者可能感覺還好,假如說在網路不太順暢的情況下,使用者載入完所有網路的資料,可能這個list是1000條新聞,那麼使用者可能需要面對一個空白的Activity好幾分鐘,這個顯然是不合適的。

解決:

我們需要進行分批載入,比如說1000條新聞的List集合,我們一次載入20條,等到使用者翻頁到底部的時候,我們再新增下面的20條到List中,再使用Adapter重新整理ListView,這樣使用者一次只需要等待20條資料的傳輸時間,不需要一次等待好幾分鐘把資料都載入完再在ListView上顯示。其次這樣也可以緩解很多條新聞一次載入進行產生OOM應用崩潰的情況。

5.載入資料過多記憶體溢位優化

問題:

我們知道Android虛擬機器給每個應用分配的執行時記憶體是一定的,一般效能不太好的機器只有16M,好一點的可能也就是64M的樣子,假如說我們現在要瀏覽的新聞總數為一萬條,即便是網路很好的情況下,我們可以很快的載入完畢,但是多數情況下也會出現記憶體溢位從而導致應用崩潰的情況。

解決:

實際上,分批載入也不能完全解決問題,因為雖然我們在分批中一次只增加20條資料到List集合中,然後再重新整理到ListView中去,假如有10萬條資料,如果我們順利讀到最後這個List集合中還是會累積海量條數的資料,還是可能會造成OOM的情況,這時候我們就需要用到分頁,比如說我們將這10萬條資料分為1000頁,每一頁100條資料,每一頁載入時都覆蓋掉上一頁中List集合中的內容,然後每一頁內再使用分批載入,這樣使用者的體驗就會相對好一些。