ListView適配器Adapter介紹與優化


一、ListView與Adapter的關系

 

       ListView是Android開發過程中較為常見的組件之一,它將數據以列表的形式展現出來。一般而言,一個ListView由以下三個元素組成:

       1、View,用於展示列表,通常是一個xml所指定的。大家都知道Android的界面基本上是由xml文件負責完成的,所以ListView的界面也理所應當的使用了xml定義。例如在ListView中經常用到的“android.R.layout.simple_list_item”等, 就是Android系統內部定義好的一個xml文件。

     2、適配器,用來將不同的數據映射到View上。不同的數據對應不同的適配器,如BaseAdapter、ArrayAdapter、CursorAdapter、SimpleAdapter等, 他們能夠將數組、指針指向的數據、Map等數據映射到View上。也正是由於適配器的存在,使得ListView的使用相當靈活,經過適配器的處理后,在 view看來所有的數據映射過來都是一樣的。

       3、數據,具體的來映射數據和資源,可以是字符串,圖片等。通過適配器,這些數據將會被實現到 ListView上。所有的數據和資源要顯示到ListView上都通過適配器來完成。

系統已有的適配器可以將基本的數據顯示到ListView上,如:數組,Cursor指向的數據,Map里的數據。但是在實際開發中這些系統已實現的適配器,有時不能滿足我們的需求。而且系統自帶的含有多選功能ListView在實際使用過程中會有一些問題。要實現復雜的ListView可以通過繼承ListView並重寫相應的方法完成,同時也可以通過繼承BaseAdapter來實現。

 二、ListView繪制流程

 

       public abstract class BaseAdapter——抽象類,繼承它需要實現較多的方法,所以就具有較高的靈活性,實現了ListAdapter和SpinnerAdapter。

       BaseAdapter需要重寫的方法:

       getCount(),

       getItem(int position),

       getItemId(int position),

       getView(int position, View convertView, ViewGroup parent)

       ListView在開始繪制的時候,系統首先調用getCount()函數,根據他的返回值得到 listView的長度,然后根據這個長度,調用getView()逐一繪制每一行。如果你的 getCount()返回值是0的話,列表將不顯示同樣return 1,就只顯示一行。

       系統顯示列表時,首先實例化一個適配器(這里將實例化自定義的適配器)。當手動完成適配時,必須手動映射數據,這需要重寫getView()方法。系統在繪制列表的每一行的時候將調用此方法。getView()有三個參數,position表示將顯示的是第幾行,covertView是從布局文件中inflate來的布局。我們用LayoutInflater的方法將定義好的item.xml文件提取成View實例用來顯示。然后將xml文件中的各個組件實例化(簡單的findViewById()方法)。這樣便可以將數據對應到各個組件上了。但是按鈕為了響應點擊事件,需要為它添加點擊監聽器,這樣就能捕獲點擊事件。至此一個自定義的listView就完成了,現在讓我們回過頭從新審視這個過程。系統要繪制ListView了,他首先獲得要繪制的這個列表的長度,然后開始繪制第一行,怎么繪制呢?調用getView()函數。在這個函數里面首先獲得一個View(實際上是一個 ViewGroup),然后再實例並設置各個組件,顯示之。好了,繪制完這一行了。那再繪制下一行,直到繪完為止。

三、ListView優化

       Adapter的作用就是ListView界面與數據之間的橋梁,當列表里的每一項顯示到頁面時,都會調用Adapter的getView方法返回一個View。在我們的列表有1000000項時會占用極大的系統資源。先看看下面的代碼:

public View getView(int position, View convertView, ViewGroup parent)
{
    View item = mInflater.inflate(R.layout.list_item_icon_text, null);
    ((TextView) item.findViewById(R.id.text)).setText(DATA[position]);
    ((ImageView) item.findViewById(R.id.icon)).setImageBitmap((position & 1) == 1 ? mIcon1 : mIcon2);
    return item;
}

 

如果超過1000000項時,后果不堪設想!您可千萬別這么寫!我們再來看看下面的代碼:

public View getView(int position, View convertView, ViewGroup parent) 
{
    if (convertView == null) 
    {
        convertView = mInflater.inflate(R.layout.item, null);
    }
    ((TextView) convertView.findViewById(R.id.text)).setText(DATA[position]);
 ((ImageView) convertView.findViewById(R.id.icon)).setImageBitmap((position & 1) == 1 ? mIcon1 : mIcon2);
 return convertView;
}

 

怎么樣,上面的代碼是不是好了很多?系統將會減少創建很多View。性能得到了很大的提升。還有沒有優化的方法呢? 答案是肯定的,采用ViewHolder模式:

class ChatListAdapter extends BaseAdapter
{
 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();
     }
        holder.text.setText(DATA[position]);
     holder.icon.setImageBitmap((position & 1) == 1 ? mIcon1 : mIcon2);
     return convertView;
    }
}

 

怎么樣?會不會又給您的系統帶來很大的提升呢?看看下面三種方式的性能對比圖您就知道了!

 

四、總結:用ViewHolder模式優化ListView

       ViewHolder的作用:優化顯示效率,即之前顯示過的不用再從布局文件讀取,直接從緩存中讀取。可以看到它只是一個靜態類,它的作用就在於減少不必要的調用findViewById。完整的官方例子中convertView 也是避免inflating View。然后把對底下的控件引用存在ViewHolder里面,再用convertView.setTag(holder)把它放在view里,下次就可以用(ViewHolder) convertView.getTag()直接取了。

       這個ViewHolder到底是什么呢?我們可以在官方sample看到這段代碼

http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/view/List14.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM