List控件使用---SimpleAdapter使用詳解(一)

一、簡單實現

先看效果:

轉自http://blog.csdn.net/harvic880925/article/details/17258789



1、XML

要實現這個效果,首先我們要寫一個XML文件,而這個佈局文件僅僅定義了列表中一項的表現形式。

XML文件代碼如下:(vlist.xml)

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:orientation="horizontal" android:layout_width="fill_parent"  
  4.     android:layout_height="fill_parent">  
  5.   
  6.     <ImageView android:id="@+id/img"   
  7.         android:layout_width="wrap_content"  
  8.         android:layout_height="wrap_content"   
  9.         android:layout_margin="5px"/>  
  10.   
  11.     <LinearLayout android:orientation="vertical"  
  12.         android:layout_width="wrap_content"   
  13.         android:layout_height="wrap_content">  
  14.   
  15.         <TextView android:id="@+id/title"   
  16.             android:layout_width="wrap_content"  
  17.             android:layout_height="wrap_content"   
  18.             android:textColor="#FFFFFF00"  
  19.             android:textSize="22px" />  
  20.         <TextView android:id="@+id/info"   
  21.             android:layout_width="wrap_content"  
  22.             android:layout_height="wrap_content"   
  23.             android:textColor="#FF00FFFF"  
  24.             android:textSize="13px" />  
  25.     </LinearLayout>  
  26. </LinearLayout>  

2、JAVA代碼

1、將當前Activity改爲派生自ListActivity

2、Java代碼如下:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. public class MainActivity extends ListActivity {  
  2.   
  3.     @Override  
  4.     protected void onCreate(Bundle savedInstanceState) {  
  5.         super.onCreate(savedInstanceState);  
  6.           
  7.         SimpleAdapter adapter=new SimpleAdapter(this, getData(), R.layout.vlist,new String[]{"title","info","img"},  
  8.                 new int[]{R.id.title,R.id.info,R.id.img});  
  9.         setListAdapter(adapter);  
  10.     }  
  11.     private List<Map<String, Object>> getData() {  
  12.         List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();  
  13.   
  14.         Map<String, Object> map = new HashMap<String, Object>();  
  15.         map.put("title""G1");  
  16.         map.put("info""google 1");  
  17.         map.put("img", R.drawable.i1);  
  18.         list.add(map);  
  19.   
  20.         map = new HashMap<String, Object>();  
  21.         map.put("title""G2");  
  22.         map.put("info""google 2");  
  23.         map.put("img", R.drawable.i2);  
  24.         list.add(map);  
  25.   
  26.         map = new HashMap<String, Object>();  
  27.         map.put("title""G3");  
  28.         map.put("info""google 3");  
  29.         map.put("img", R.drawable.i3);  
  30.         list.add(map);  
  31.           
  32.         return list;  
  33.     }  
  34.   
  35. }  

使用simpleAdapter的數據用一般都是HashMap構成的List,list的每一節對應ListView的每一行。HashMap的每個鍵值數據映射到佈局文件中對應id的組件上。因爲系統沒有對應的佈局文件可用,我們可以自己定義一個佈局vlist.xml。下面做適配,new一個SimpleAdapter參數一次是:this,佈局文件(vlist.xml),HashMap的 title 和 info,img。佈局文件的組件id,title,info,img。佈局文件的各組件分別映射到HashMap的各元素上,完成適配。

注意:

1、沒有MainActivity的XML佈局文件,只有一個佈局文件:list每一項的顯示樣式(vlist.xml)
2、沒有setContentView(R.layout.activity_main);因爲在使用SetListAdapter()以後,就會當前當的樣式在列表中顯示出來。

二、派生自BaseAdapter

但是有時候,列表不光會用來做顯示用,我們同樣可以在在上面添加按鈕。添加按鈕首先要寫一個有按鈕的xml文件,然後自然會想到用上面的方法定義一個適配器,然後將數據映射到佈局文件上。但是事實並非這樣,因爲按鈕是無法映射的,即使你成功的用佈局文件顯示出了按鈕也無法添加按鈕的響應,這時就要研究一下ListView是如何現實的了,而且必須要重寫一個類繼承BaseAdapter。下面的示例將顯示一個按鈕和一個圖片,兩行字如果單擊按鈕將刪除此按鈕的所在行。並告訴你ListView究竟是如何工作的。效果如下:


1、XML代碼

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:orientation="horizontal" android:layout_width="fill_parent"  
  4.     android:layout_height="fill_parent">  
  5.   
  6.     <ImageView android:id="@+id/img"   
  7.         android:layout_width="wrap_content"  
  8.         android:layout_height="wrap_content"   
  9.         android:layout_margin="5px"/>  
  10.   
  11.     <LinearLayout android:orientation="vertical"  
  12.         android:layout_width="wrap_content"   
  13.         android:layout_height="wrap_content">  
  14.   
  15.         <TextView android:id="@+id/title"   
  16.             android:layout_width="wrap_content"  
  17.             android:layout_height="wrap_content"   
  18.             android:textColor="#FFFFFF00"  
  19.             android:textSize="22px" />  
  20.         <TextView android:id="@+id/info"   
  21.             android:layout_width="wrap_content"  
  22.             android:layout_height="wrap_content"   
  23.             android:textColor="#FF00FFFF"  
  24.             android:textSize="13px" />  
  25.     </LinearLayout>  
  26.       
  27.     <Button android:id="@+id/view_btn"  
  28.         android:layout_width="wrap_content"  
  29.         android:layout_height="wrap_content"  
  30.         android:text="@string/s_view_btn"  
  31.         android:layout_gravity="bottom|right" />  
  32.       
  33. </LinearLayout>  

2、JAVA代碼

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. /** 
  2.  * @author harvic 
  3.  */  
  4. import java.util.ArrayList;  
  5. import java.util.HashMap;  
  6. import java.util.List;  
  7. import java.util.Map;  
  8.   
  9. import android.os.Bundle;  
  10. import android.app.AlertDialog;  
  11. import android.app.ListActivity;  
  12. import android.content.Context;  
  13. import android.content.DialogInterface;  
  14. import android.view.LayoutInflater;  
  15. import android.view.View;  
  16. import android.view.ViewGroup;  
  17. import android.widget.BaseAdapter;  
  18. import android.widget.Button;  
  19. import android.widget.ImageView;  
  20. import android.widget.TextView;  
  21.   
  22. public class MainActivity extends ListActivity {  
  23.   
  24.     @Override  
  25.     public void onCreate(Bundle savedInstanceState) {  
  26.         super.onCreate(savedInstanceState);  
  27.         MyAdapter adapter = new MyAdapter(this,getData());//獲取將要綁定的數據傳入其中  
  28.         setListAdapter(adapter);  
  29.     }  
  30.   
  31.     //要傳入的數據  
  32.     private List<Map<String, Object>> getData() {  
  33.         List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();  
  34.   
  35.         Map<String, Object> map = new HashMap<String, Object>();  
  36.         map.put("title""G1");  
  37.         map.put("info""google 1");  
  38.         map.put("img", R.drawable.i1);  
  39.         list.add(map);  
  40.   
  41.         map = new HashMap<String, Object>();  
  42.         map.put("title""G2");  
  43.         map.put("info""google 2");  
  44.         map.put("img", R.drawable.i2);  
  45.         list.add(map);  
  46.   
  47.         map = new HashMap<String, Object>();  
  48.         map.put("title""G3");  
  49.         map.put("info""google 3");  
  50.         map.put("img", R.drawable.i3);  
  51.         list.add(map);  
  52.           
  53.         return list;  
  54.     }  
  55.   
  56.     /** 
  57.      * listview中點擊按鍵彈出對話框 
  58.      */  
  59.     public void showInfo(){  
  60.         new AlertDialog.Builder(this)  
  61.         .setTitle("我的listview")  
  62.         .setMessage("介紹...")  
  63.         .setPositiveButton("確定"new DialogInterface.OnClickListener() {  
  64.             @Override  
  65.             public void onClick(DialogInterface dialog, int which) {  
  66.             }  
  67.         })  
  68.         .show();  
  69.           
  70.     }  
  71.       
  72.       
  73.     //ViewHolder靜態類  
  74.     public final class ViewHolder{  
  75.         public ImageView img;  
  76.         public TextView title;  
  77.         public TextView info;  
  78.         public Button viewBtn;  
  79.     }  
  80.       
  81.       
  82.     public class MyAdapter extends BaseAdapter{  
  83.   
  84.         private LayoutInflater mInflater;  
  85.         private List<Map<String, Object>> mData;          
  86.           
  87.         public MyAdapter(Context context,List<Map<String, Object>> mData){  
  88.             //根據context上下文加載佈局  
  89.             this.mInflater = LayoutInflater.from(context);  
  90.             //將傳入的數據保存在mData中  
  91.             this.mData=mData;  
  92.         }  
  93.         @Override  
  94.         public int getCount() {  
  95.             //How many items are in the data set represented by this Adapter.  
  96.             //在此適配器中所代表的數據集中的條目數  
  97.             return mData.size();  
  98.         }  
  99.   
  100.         @Override  
  101.         public Object getItem(int position) {  
  102.              // Get the data item associated with the specified position in the data set.  
  103.             //獲取數據集中與指定索引對應的數據項  
  104.             return position;  
  105.         }  
  106.   
  107.         @Override  
  108.         public long getItemId(int position) {  
  109.              //Get the row id associated with the specified position in the list.  
  110.             //獲取在列表中與指定索引對應的行id  
  111.             return position;  
  112.         }  
  113.   
  114.         @Override  
  115.         //Get a View that displays the data at the specified position in the data set.  
  116.         //獲取一個在數據集中指定索引的視圖來顯示數據  
  117.         public View getView(int position, View convertView, ViewGroup parent) {  
  118.               
  119.             ViewHolder holder = null;  
  120.               
  121.             //如果緩存convertView爲空,則需要創建View  
  122.             if (convertView == null) {  
  123.                 //自定義的一個類用來緩存convertview  
  124.                 holder=new ViewHolder();   
  125.                   
  126.                 //根據自定義的Item佈局加載佈局  
  127.                 convertView = mInflater.inflate(R.layout.vlist, null);  
  128.                   
  129.                 holder.img = (ImageView)convertView.findViewById(R.id.img);  
  130.                 holder.title = (TextView)convertView.findViewById(R.id.title);  
  131.                 holder.info = (TextView)convertView.findViewById(R.id.info);  
  132.                 holder.viewBtn = (Button)convertView.findViewById(R.id.view_btn);  
  133.                 //將設置好的佈局保存到緩存中,並將其設置在Tag裏,以便後面方便取出Tag  
  134.                 convertView.setTag(holder);  
  135.                   
  136.             }else {  
  137.                   
  138.                 holder = (ViewHolder)convertView.getTag();  
  139.             }  
  140.               
  141.               
  142.             holder.img.setBackgroundResource((Integer)mData.get(position).get("img"));  
  143.             holder.title.setText((String)mData.get(position).get("title"));  
  144.             holder.info.setText((String)mData.get(position).get("info"));  
  145.               
  146.             //爲BTN設置點擊監聽事件  
  147.             holder.viewBtn.setOnClickListener(new View.OnClickListener() {  
  148.                   
  149.                 @Override  
  150.                 public void onClick(View v) {  
  151.                     showInfo();                   
  152.                 }  
  153.             });  
  154.               
  155.               
  156.             return convertView;  
  157.         }  
  158.           
  159.     }  
  160.       
  161. }  
講解:

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

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



三、BaseAdapter講解

在前面講解了派生自BaseAdapter的listView的生成方法,但大家應該對BaseAdapter還是搞不明白,爲什麼要這樣做,其中的getView()函數爲什麼要這麼寫。下面我們單獨講講。

1、在繼承BaseAdapter時需要實現重寫裏面的很多方法,例如

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. class MyAdapter extends BaseAdapter  
  2.     {  
  3.         private Context context;  
  4.         public MyAdapter(Context context)  
  5.         {  
  6.             this.context = context;  
  7.         }  
  8.         @Override  
  9.         public int getCount() {  
  10.             // How many items are in the data set represented by this Adapter.(在此適配器中所代表的數據集中的條目數)  
  11.             return 0;  
  12.         }  
  13.   
  14.         @Override  
  15.         public Object getItem(int position) {  
  16.             // Get the data item associated with the specified position in the data set.(獲取數據集中與指定索引對應的數據項)  
  17.             return null;  
  18.         }  
  19.   
  20.         @Override  
  21.         public long getItemId(int position) {  
  22.             // Get the row id associated with the specified position in the list.(取在列表中與指定索引對應的行id)  
  23.             return 0;  
  24.         }  
  25.   
  26.         @Override  
  27.         public View getView(int position, View convertView, ViewGroup parent) {  
  28.             // Get a View that displays the data at the specified position in the data set.  
  29.             return null;  
  30.         }  
  31.           
  32.     }  

這裏面沒什麼難度,但是這個getView方法必須好好處理,也是最麻煩的

第一種:沒有任何處理,不建議這樣寫。如果數據量少看將就,但是如果列表項數據量很大的時候,會每次都重新創建View,設置資源,嚴重影響性能,所以從一開始就不要用這種方式

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. @Override  
  2.         public View getView(int position, View convertView, ViewGroup parent) {  
  3.             View item = mInflater.inflate(R.layout.list_item, null);  
  4.             ImageView img = (ImageView)item.findViewById(R.id.img)   
  5.             TextView title = (TextView)item.findViewById(R.id.title);  
  6.             TextView info = (TextView)item.findViewById(R.id.info);  
  7.             img.setImageResource(R.drawable.ic_launcher);  
  8.             title.setText("Hello");  
  9.             info.setText("world");  
  10.               
  11.             return item;  
  12.         }  

第二種:ListView優化:通過緩存convertView,這種利用緩存contentView的方式可以判斷如果緩存中不存在View才創建View,如果已經存在可以利用緩存中的View,提升了性能

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. public View getView(int position, View convertView, ViewGroup parent) {  
  2.     if(convertView == null)  
  3.     {  
  4.         convertView = mInflater.inflate(R.layout.list_item, null);  
  5.     }  
  6.       
  7.     ImageView img = (ImageView)convertView.findViewById(R.id.img)   
  8.     TextView title = (TextView)convertView.findViewById(R.id.title);  
  9.     TextView info = (TextView)ConvertView.findViewById(R.id.info);  
  10.     img.setImageResource(R.drawable.ic_launcher);  
  11.     title.setText("Hello");  
  12.     info.setText("world");  
  13.       
  14.     return convertView;  
  15. }  

第三種ListView優化:通過convertView+ViewHolder來實現,ViewHolder就是一個靜態類,使用 ViewHolder 的關鍵好處是緩存了顯示數據的視圖(View),加快了 UI 的響應速度。

當我們判斷 convertView == null  的時候,如果爲空,就會根據設計好的List的Item佈局(XML),來爲convertView賦值,並生成一個viewHolder來綁定converView裏面的各個View控件(XML佈局裏面的那些控件)。再用convertView的setTag將viewHolder設置到Tag中,以便系統第二次繪製ListView時從Tag中取出。(看下面代碼中)

如果convertView不爲空的時候,就會直接用convertView的getTag(),來獲得一個ViewHolder。

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. //在外面先定義,ViewHolder靜態類  
  2. static class ViewHolder  
  3. {  
  4.     public ImageView img;  
  5.     public TextView title;  
  6.     public TextView info;  
  7. }  
  8. //然後重寫getView  
  9.     @Override  
  10.     public View getView(int position, View convertView, ViewGroup parent) {  
  11.         ViewHolder holder;  
  12.         if(convertView == null)  
  13.         {  
  14.             holder = new ViewHolder();  
  15.             convertView = mInflater.inflate(R.layout.list_item, null);  
  16.             holder.img = (ImageView)item.findViewById(R.id.img)   
  17.             holder.title = (TextView)item.findViewById(R.id.title);  
  18.             holder.info = (TextView)item.findViewById(R.id.info);  
  19.             convertView.setTag(holder);  
  20.         }else  
  21.         {  
  22.             holder = (ViewHolder)convertView.getTag();  
  23.         }  
  24.         holder.img.setImageResource(R.drawable.ic_launcher);  
  25.         holder.title.setText("Hello");  
  26.         holder.info.setText("World");  
  27.                
  28.         return convertView;  
  29.     }  

到這裏,可能會有人問ViewHolder靜態類結合緩存convertView與直接使用convertView有什麼區別嗎,是否重複了

在這裏,官方給出瞭解釋

提升Adapter的兩種方法

To work efficiently the adapter implemented here uses two techniques:
-It reuses the convertView passed to getView() to avoid inflating View when it is not necessary

(譯:重用緩存convertView傳遞給getView()方法來避免填充不必要的視圖)
-It uses the ViewHolder pattern to avoid calling findViewById() when it is not necessary

(譯:使用ViewHolder模式來避免沒有必要的調用findViewById():因爲太多的findViewById也會影響性能)
ViewHolder類的作用
-The ViewHolder pattern consists in storing a data structure in the tag of the view
returned by getView().This data structures contains references to the views we want to bind data to,
thus avoiding calling to findViewById() every time getView() is invoked

(譯:ViewHolder模式通過getView()方法返回的視圖的標籤(Tag)中存儲一個數據結構,這個數據結構包含了指向我們

要綁定數據的視圖的引用,從而避免每次調用getView()的時候調用findViewById())

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章