ListView(2)——自定義Adapter

憶江南的部落格發表於2015-08-24

之前介紹了三種adapter:ArrayAdapter<T>SimpleAdapter和SimpleCursorAdapter。

使用android提供的adapter來繪製列表的話,列表的每一項的顯示都是一樣的。為了實現ListView的單雙行不同顏色顯示,需要自定義adapter的子類。adapter的常用子類有BaseAdapter、ArrayAdapter、SimpleAdapter等,下面介紹自定義BaseAdapter和ArrayAdapter的實現。


1.自定義BaseAdapter

效果圖如下:

    

在上一篇《ListView(1)——各種Adapter的使用》的基礎上,為了實現ListView的單雙行不同顏色顯示,需要自定義adapter的子類,而activity_main.xml檔案和list_item.xml檔案都不需要更改,MainActivity.java程式碼修改如下:

[java] view plaincopy
  1. @Override  
  2. protected void onCreate(Bundle savedInstanceState) {  
  3.     super.onCreate(savedInstanceState);  
  4.     setContentView(R.layout.activity_main);  
  5.   
  6.     // 圖片資源的ID  
  7.     int[] images = new int[] { R.drawable.item_img_a,  
  8.             R.drawable.item_img_b, R.drawable.item_img_c,  
  9.             R.drawable.item_img_d, R.drawable.item_img_e };  
  10.   
  11.     List<HashMap<String, Object>> data = new ArrayList<HashMap<String, Object>>();  
  12.     for (int i = 0; i < 5; i++) {  
  13.         HashMap<String, Object> map = new HashMap<String, Object>();  
  14.         map.put("ItemImage", images[i]);  
  15.         map.put("ItemTitle""This is Title " + i);  
  16.         map.put("ItemText""This is text " + i);  
  17.         data.add(map);  
  18.     }  
  19.   
  20.     // 繫結XML中的ListView,作為Item的容器  
  21.     ListView listView = (ListView) findViewById(R.id.list);  
  22.   
  23.     MyBaseAdapter adapter = new MyBaseAdapter(MainActivity.this, data);  
  24.     listView.setAdapter(adapter);  
  25. }  

下面我們實現自定義的MyBaseAdapter類。MyBaseAdapter類繼承自BaseAdapter類,BaseAdapter為抽象類,繼承它需要實現如下方法,因此具有較高的靈活性。

[java] view plaincopy
  1. public class MyBaseAdapter extends BaseAdapter {  
  2.   
  3.     @Override  
  4.     public int getCount() {  
  5.         // TODO Auto-generated method stub  
  6.         return 0;  
  7.     }  
  8.   
  9.     @Override  
  10.     public Object getItem(int arg0) {  
  11.         // TODO Auto-generated method stub  
  12.         return null;  
  13.     }  
  14.   
  15.     @Override  
  16.     public long getItemId(int position) {  
  17.         // TODO Auto-generated method stub  
  18.         return 0;  
  19.     }  
  20.   
  21.     @Override  
  22.     public View getView(int position, View convertView, ViewGroup parent) {  
  23.         // TODO Auto-generated method stub  
  24.         return null;  
  25.     }  
  26.   
  27. }  

ListView在繪製時首先會呼叫getCount()方法得到繪製次數,然後通過getView()方法一層一層進行繪製,所以我們可以在getView()方法中根據position(當前繪製的ID)來的修改繪製內容。而getItem()和getItemId()則在需要處理和取得Adapter中的資料時呼叫。

[java] view plaincopy
  1. public class MyBaseAdapter extends BaseAdapter {  
  2.     private int[] colors = new int[] { 0xff3cb3710xffa0a0a0 };  
  3.     private Context mContext;  
  4.     private List<HashMap<String, Object>> dataList;  
  5.   
  6.     public MyBaseAdapter(Context context, List<HashMap<String, Object>> dataList) {  
  7.         this.mContext = context;  
  8.         this.dataList = dataList;  
  9.     }  
  10.   
  11.     @Override  
  12.     public int getCount() {  
  13.         return dataList.size();  
  14.     }  
  15.   
  16.     @Override  
  17.     public HashMap<String, Object> getItem(int position) {  
  18.         return dataList.get(position);  
  19.     }  
  20.   
  21.     @Override  
  22.     public long getItemId(int position) {  
  23.         return position;  
  24.     }  
  25.   
  26.     @Override  
  27.     public View getView(int position, View convertView, ViewGroup parent) {  
  28.         ViewHolder holder = null;  
  29.         if (convertView == null) {  
  30.             holder = new ViewHolder();  
  31.             convertView = LayoutInflater.from(mContext).inflate(  
  32.                     R.layout.list_item, null);  
  33.             holder.image = (ImageView) convertView.findViewById(R.id.ItemImage);  
  34.             holder.title = (TextView) convertView.findViewById(R.id.ItemTitle);  
  35.             holder.text = (TextView) convertView.findViewById(R.id.ItemText);  
  36.   
  37.             // 將holder繫結到convertView  
  38.             convertView.setTag(holder);  
  39.         } else {  
  40.             holder = (ViewHolder) convertView.getTag();  
  41.         }  
  42.   
  43.         // 向ViewHolder中填入的資料  
  44.         holder.image.setImageResource((Integer) getItem(position).get(  
  45.                 "ItemImage"));  
  46.         holder.title.setText((String) getItem(position).get("ItemTitle"));  
  47.         holder.text.setText((String) getItem(position).get("ItemText"));  
  48.   
  49.         int colorPos = position % colors.length;  
  50.         convertView.setBackgroundColor(colors[colorPos]);  
  51.   
  52.         return convertView;  
  53.     }  
  54.   
  55.     /** 
  56.      * ViewHolder類用以儲存item中控制元件的引用 
  57.      */  
  58.     final class ViewHolder {  
  59.         ImageView image;  
  60.         TextView title;  
  61.         TextView text;  
  62.     }  
  63. }  
getView()方法用來獲得繪製每個item的View物件,如果每次getView()被執行都new出一個View物件,長此以往會產生很大的消耗,特別當item中還有Bitmap等,甚至會造成OOM的錯誤導致程式崩潰。從上面的程式碼可以看到getView()有一個convertView引數,這個引數用來快取View物件。當ListView滑動的過程中,會有item被滑出螢幕而不再被使用,這時候Android會回收這個item的view,這個view也就是這裡的convertView。這樣如果convertView不為null,就不用new出一個新的View物件,只用往convertView中填充新的item,這樣就省去了new View的大量開銷。

在上面的程式碼中,在快取convertView減少new View開銷的同時,通過setTag()方法將資料結構ViewHolder繫結到convertView,從而利用ViewHolder儲存convertView中控制元件物件的引用,這樣避免每次呼叫findViewById()方法。


2.自定義ArrayAdapter<T>

在開發中需要將物件顯示在listview中,這時候使用ArrayAdapter<T>來顯示指定物件型別。下面自定義ArrayAdapter<T>實現上一節中自定義BaseAdapter實現的同樣的效果,首先定義要顯示的物件,程式碼如下ItemBean.java。

[java] view plaincopy
  1. public class ItemBean {  
  2.     private int image;  
  3.     private String title;  
  4.     private String text;  
  5.   
  6.     public ItemBean(int image, String title, String text) {  
  7.         this.image = image;  
  8.         this.title = title;  
  9.         this.text = text;  
  10.     }  
  11.   
  12.     public int getImage() {  
  13.         return image;  
  14.     }  
  15.   
  16.     public String getTitle() {  
  17.         return title;  
  18.     }  
  19.   
  20.     public String getText() {  
  21.         return text;  
  22.     }  
  23. }  
和上一節一樣,為了實現ListView的單雙行不同顏色顯示,需要自定義adapter的子類,而activity_main.xml檔案和list_item.xml檔案都不需要更改,MainActivity.java程式碼修改如下:

[java] view plaincopy
  1. @Override  
  2. protected void onCreate(Bundle savedInstanceState) {  
  3.     super.onCreate(savedInstanceState);  
  4.     setContentView(R.layout.activity_main);  
  5.   
  6.     // 圖片資源的ID  
  7.     int[] images = new int[] { R.drawable.item_img_a,  
  8.             R.drawable.item_img_b, R.drawable.item_img_c,  
  9.             R.drawable.item_img_d, R.drawable.item_img_e };  
  10.   
  11.     ArrayList<ItemBean> data = new ArrayList<ItemBean>();  
  12.     for (int i = 0; i < 5; i++) {  
  13.         ItemBean itemBean = new ItemBean(images[i], "This is Title " + i,  
  14.                 "This is text " + i);  
  15.         data.add(itemBean);  
  16.     }  
  17.   
  18.     // 繫結XML中的ListView,作為Item的容器  
  19.     ListView listView = (ListView) findViewById(R.id.list);  
  20.   
  21.     MyArrayAdapter adapter = new MyArrayAdapter(MainActivity.this,  
  22.             R.layout.list_item, data);  
  23.     listView.setAdapter(adapter);  
  24. }  
接下來自定義繼承自ArrayAdapter<ItemBean>MyArrayAdapter類,繼承ArrayAdapter<ItemBean>只需要重寫getView()方法就可以實現與上一節相同的效果,並且不用儲存List<ItemBean>物件引用。

[java] view plaincopy
  1. public class MyArrayAdapter extends ArrayAdapter<ItemBean> {  
  2.     private int[] colors = new int[] { 0xff3cb3710xffa0a0a0 };  
  3.     private Context mContext;  
  4.     private int resource;  
  5.   
  6.     public MyArrayAdapter(Context context, int resource, List<ItemBean> data) {  
  7.         super(context, resource, data);  
  8.         this.mContext = context;  
  9.         this.resource = resource;  
  10.     }  
  11.   
  12.     @Override  
  13.     public View getView(int position, View convertView, ViewGroup parent) {  
  14.         ViewHolder holder = null;  
  15.         if (convertView == null) {  
  16.             holder = new ViewHolder();  
  17.             convertView = LayoutInflater.from(mContext).inflate(resource, null);  
  18.             holder.image = (ImageView) convertView.findViewById(R.id.ItemImage);  
  19.             holder.title = (TextView) convertView.findViewById(R.id.ItemTitle);  
  20.             holder.text = (TextView) convertView.findViewById(R.id.ItemText);  
  21.   
  22.             // 將holder繫結到convertView  
  23.             convertView.setTag(holder);  
  24.         } else {  
  25.             holder = (ViewHolder) convertView.getTag();  
  26.         }  
  27.   
  28.         // 向ViewHolder中填入的資料  
  29.         holder.image.setImageResource(getItem(position).getImage());  
  30.         holder.title.setText(getItem(position).getTitle());  
  31.         holder.text.setText(getItem(position).getText());  
  32.   
  33.         int colorPos = position % colors.length;  
  34.         convertView.setBackgroundColor(colors[colorPos]);  
  35.   
  36.         return convertView;  
  37.     }  
  38.   
  39.     /** 
  40.      * ViewHolder類用以儲存item中控制元件的引用 
  41.      */  
  42.     final class ViewHolder {  
  43.         ImageView image;  
  44.         TextView title;  
  45.         TextView text;  
  46.     }  
  47. }  

參考文章:

http://www.open-open.com/lib/view/open1339485728006.html


相關文章