Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 布局與控件(九)-ListView的Adapter們

布局與控件(九)-ListView的Adapter們

編輯:關於Android編程

第10節 ListView的Adapter

安卓系統為ListView設計了多種Adapter作為它的搭檔。每種Adapter不僅為ListView提供數據內容,也會告訴ListView如何展示這些數據-規定好列表項的長相。

這些Adapter都是從Adapter類繼承而來的,它們的關系如下:

\

這裡我們選擇性的介紹常見的2種Adapter-ArrayAdapterSimpleAdapter

10.1 ArrayAdapter

ArrayAdapter是最簡單的Adapter,我們在前面已經使用過它,

ListView lv = (ListView) findViewById(R.id.list_view);
String data[] = {"a", "b", "c"};
ArrayAdapter adapter = new ArrayAdapter(context, 
                android.R.layout.simple_list_item_1 , data);
lv.setAdapter(adapter);
lv.setOnItemClickListener(new OnItemClickListener() {

    @Override
    public void onItemClick(AdapterView parent, View view, int position, long id) {
        //添加需要響應的操作
    }
});

在創建ArrayAdapter的時候,

要聲明這個Adapter要接受什麼類型的數據,

new ArrayAdapter(......)

指定布局文件的layout id

android.R.layout.simple_list_item_1

這個布局就是一個TextView,只不過被指定了android:id@android:id/text1

注意這裡在給控件指定ID的時候用的是`@id/text1,而沒有用+。這表明,這個ID值是已經存在的,而不需要編譯器再單獨分配。

這樣ArrayAdapter會自動尋找這個布局文件中ID名字為text1的控件,並認為它就是可以用來顯示數據的TextView

如果不想用系統給TextView指定的ID,也可以給自己定義的布局的TextView取個名字,例如show_content_textview

然後向ArrayAdapter再傳入一個ID,告訴它將要顯示的文字,放到這個ID所指示的控件上,

ArrayAdapter adapter = new ArrayAdapter(context, 
                R.layout.custom_item_layout, 
                R.id.show_content_textview, 
                data);

傳入要顯示的數據內容,這個數據內容可以以數組的方式傳入,也可以以List的方式放入,

//數組方式傳入
String [] data = {"a","b","c"};
ArrayAdapter adapter = new ArrayAdapter(context, 
                android.R.layout.simple_list_item_1 , data);
//列表方式傳入
List data =new ArrayList();
data.add("a");
data.add("b");
data.add("c");
ArrayAdapter adapter = new ArrayAdapter(context, 
                android.R.layout.simple_list_item_1 , data);

10.2 SimpleAdapter

看得出ArrayAdapter是相當的簡單,只能在單一的TextView上顯示一條數據。如果有好幾條不同類型的數據要顯示到同一列表項的不同部位,那就不好辦了。SimpleAdapter正好能幫我們解決這樣的問題。雖然它叫Simple,不過不是說功能simple,而是使用很simple。

它的設計思路是,

每一條列表項上哪些控件希望顯示數據都要告訴SimpleAdaperSimpleAdaper只要知道這些控件的ID就可以了。所以在創建的時候,需要傳入這些控件的ID值(以數組的形式);

每一項待顯示的數據,要和列表上的控件一一對應。這就需要一個翻譯系統,給每個位置的數據取個名字,將待顯示數據挨個放入鍵值對的列表中(map);並將每個位置的數據名字和控件ID值對應起來;

SimpleAdapter在綁定數據到界面上的時候,就根據對應關系,一個一個把它們放上去。

\

例如,

准備要顯示的數據,讓每個數據項,和一個特定的名字(title pic content)對應上,

Map family = new HashMap();
family.put("title", "家");
family.put("pic", "http://xxx.xxx.com/family.jpg");
family.put("content", "I love my family");

Map dog = new HashMap();
dog.put("title", "狗");
dog.put("pic", "http://xxx.xxx.com/dog.jpg");
dog.put("content", "I love my dog");

List> dataList = new ArrayList>();
dataList.add(family);
dataList.add(dog);

將特定的名稱做成數組,和顯示區域的ID一一對應上,例如,

title <-> R.id.title_id
pic <-> R.id.pic_id
content <-> R.id.content_id

String [] from = {"title", "pic", "content"};
int [] to = {R.id.title_id, R.id.pic_id, R.id.content_id};

創建SimpleAdapter

adapter = new SimpleAdapter(this,  
      dataList, 
      android.R.layout.simple_list_item_1 ,
      from, 
      to);

10.3 自定義Adapter

雖然Android SDK為我們提供了好幾種現成的Adapter使用,但有時它們也並不能完全符合我們的要求,要麼用起來還是麻煩,要麼大材小用。另外,為了把ListView介紹的全面一些,我們准備自定義一個Adapter。

10.3.1 定義數據項的布局

為了讓列表的數據項按照我們設計的模樣顯示,我們需要為它設計一個布局,用展示的視頻列表為例,加以說明。

\

數據項的布局定義在res\layout\video_item.xml文件中,

數據項顯示在水平布局的LinearLayout中; 視頻縮略圖用ImageView控件顯示,給它的android:scaleType屬性設置center,讓縮略圖居中放置,背景設置成應用主題的色調colorPrimary; 其他視頻信息包含標題和創建時間,將它們豎直排列放在一個LinearLayout中,占用高度按照2:1分配,前者使用主題中較大的字體?android:attr/textAppearanceMedium,後者使用主題中較小的字體?android:attr/textAppearanceSmall; 至於各個組件之間的間隔,根據自己的視覺偏好調整就好了,用android:paddingandroid:layout_margin設置;


    

    

        

        

    

10.3.2 定義數據項內容

為每個視頻信息,定義一個數據結構VideoItem

public class VideoItem {

   String name;
   String path;
   Bitmap thumb;
   String createdTime;

   VideoItem(String strPath, String strName, String createdTime) {

       this.path = strPath;
       this.name = strName;
       ......
  }
}

10.3.3 定義Adapter

所有Adapter都是繼承自BaseAdapter的,我們自定義的Adapter也繼承自它。

繼承BaseAdapter,准備實現必須實現的基類函數;

public class VideoItemAdapter extends BaseAdapter {

    @Override
    public int getCount() {
        return 0;
    }

    @Override
    public Object getItem(int position) {
        return null ;
    }

    @Override
    public long getItemId(int position) {
        return 0;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        return null;
    }
}

創建構造函數,在構造函數中,保存好布局ID,以便以後使用,通過Context獲取Inflater,為以後數據項布局的創建做准備;保存要展示的數據項們;

private List mData;
private final LayoutInflater mInflater;
private final int mResource;

public VideoItemAdapter(Context context, int resId, List data)
{
    mData = data;
    mInflater = LayoutInflater.from(context);
    mResource = resId;
}

實現getCount()函數,返回當前數據項的個數,

@Override
public int getCount() {
    return mData != null ? mData.size() : 0;
}

實現getItem()函數,根據傳入的索引號,返回對應的數據項,

@Override
public Object getItem(int position) {
    return mData != null ? mData.get(position): null ;
}

實現getItemId()函數,根據傳入的索引號,返回對應項的id值,

@Override
public long getItemId(int position) {
    return position;
}

getView()函數中,創建數據項的布局,並為他們賦值,最後將這個布局返回給ListView,讓它顯示,

@Override
public View getView(int position, View convertView, ViewGroup parent) {

   if (convertView == null) {
       convertView = mInflater.inflate(mResource, parent, false);
   }

   VideoItem item = mData.get(position);

   TextView title = (TextView) convertView.findViewById(R.id.video_title);
   title.setText(item.name);

   TextView createTime = (TextView) convertView.findViewById(R.id.video_date);
   createTime.setText(item.createdTime);

   ImageView thumb = (ImageView) convertView.findViewById(R.id.vidoe_thumb);
   thumb.setImageBitmap(item.thumb);

   return convertView;
}

這裡的convertView就是數據項所代表的那個布局,當ListView剛創建,還沒有產生任何數據項的時候,它就是為null的,此時我們就需要創建一個布局,並通過getView()將這個布局返回給ListView

假如ListView上的數據項布局已經足夠了,那麼這裡傳入的convertView就不會再是“null”,而是之前的某個數據項布局,我們就不必為此重新創建了,只需要更新上面的內容就好。這樣提高了界面刷新的效率。

當然,這裡還能通過其他方法減少使用findViewById(),進一步提高效率,不過目前就不改進了,先把功能實現完成。


綜合以上內容,最後的代碼就是,

public class VideoItemAdapter extends BaseAdapter {

   private List mData;
   private final LayoutInflater mInflater;
   private final int mResource;

   public VideoItemAdapter(Context context, int resId, List data)
   {
       mData = data;
       mInflater = LayoutInflater.from(context);
       mResource = resId;
   }

   @Override
   public int getCount() {
       return mData != null ? mData.size() : 0;
   }

   @Override
   public Object getItem(int position) {
       return mData != null ? mData.get(position): null ;
   }

   @Override
   public long getItemId(int position) {
       return position;
   }

   @Override
   public View getView(int position, View convertView, ViewGroup parent) {
       if (convertView == null) {
           convertView = mInflater.inflate(mResource, parent, false);
       }

       VideoItem item = mData.get(position);

       TextView title = (TextView) convertView.findViewById(R.id.video_title);
       title.setText(item.name);

       TextView createTime = (TextView) convertView.findViewById(R.id.video_date);
       createTime.setText(item.createdTime);

       ImageView thumb = (ImageView) convertView.findViewById(R.id.vidoe_thumb);
       thumb.setImageBitmap(item.thumb);

       return convertView;
   }
}

10.3.4 使用自定義Adapter

在Video List的Activity創建之時,我們在onCreate()中創建並設置VideoAdapter

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_video_list);

    mVideoList = new ArrayList();
    mVideoListView = (ListView) findViewById(R.id.video_list);
    VideoItemAdapter adapter = new VideoItemAdapter(this, R.layout.video_item, mVideoList);
    mVideoListView.setAdapter(adapter);
    //設置數據項被點擊的監聽器
    mVideoListView.setOnItemClickListener(this);

}

當列表中的數據有變化時,在主線程中更新數據列表,並使用notifyDataSetChanged()刷新,

VideoItem data = xxx;

mVideoList.add(data);
VideoItemAdapter adapter = (VideoItemAdapter) mVideoListView.getAdapter();
adapter.notifyDataSetChanged();

10.3.5 改進提高效率

在前面實現自定義Adapter的getView()函數中,沒有每次都創建一個convertView,而是復用已有的布局,這樣就節省重新創建的資源。不過每次都使用findViewById()也會花掉不少的開銷。

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    //節省了資源
    if (convertView == null) {
     convertView = mInflater.inflate(mResource, parent, false);
    }

    VideoItem item = mData.get(position);

    //消耗了資源  
    TextView title = (TextView) convertView.findViewById(R.id.video_title);
    title.setText(item.name);

    //消耗了資源  
    TextView createTime = (TextView) convertView.findViewById(R.id.video_date);
    createTime.setText(item.createdTime);

    //消耗了資源   
    ImageView thumb = (ImageView) convertView.findViewById(R.id.vidoe_thumb);
    thumb.setImageBitmap(item.thumb);

    return convertView;
}

為此,我們可以引入一個數據結構,將這些控件保存下來,在使用的時候直接獲取,不需要進行耗時的findViewById()操作了。

創建一個數據類,准備存放控件,

private class Holder {
    public TextView title;
    public TextView createTime;
    public ImageView thumb;
}

創建布局的時候,通過創建Holder將控件保存起來,

@Override
public View getView(int position, View convertView, ViewGroup parent) {

    if (convertView == null) {
        convertView = mInflater.inflate(mResource, parent, false);
        Holder holder = new Holder();
        holder.title = (TextView) convertView.findViewById(R.id.video_title);
        holder.createTime = (TextView) convertView.findViewById(R.id.video_date);
        holder.thumb = (ImageView) convertView.findViewById(R.id.vidoe_thumb);
        //存放到View當中
        convertView.setTag(holder);
    }
    ......
}

獲取Holder,直接使用,提高效率,

@Override
public View getView(int position, View convertView, ViewGroup parent) {

    ......
    //從View當中取出Holder
    Holder holder = (Holder) convertView.getTag()

    VideoItem item = mData.get(position);
    holder.title.setText(item.name);
    holder.createTime.setText(item.createdTime);
    holder.thumb.setImageBitmap(item.thumb);

    return convertView;
}
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved