天天看點

Android 中的 adapterArrayAdapter和SimpleCursorAdapter例子

Adapter介紹

Adapter是将資料綁定到UI界面上的橋接類。Adapter負責建立顯示每個項目的子View和提供對下層資料的通路。

支援Adapter綁定的UI控件必須擴充AdapterView抽象類。建立自己的繼承自AdapterView的控件和建立新的Adapter類來綁定它們是可能的。

一些Android提供的Adapter介紹

在多數情況下,你不需要白手建立自己的Adapter。Android提供了一系列Adapter來将資料綁定到UI Widget上。

因為Android負責提供資料和選擇用于顯示每個項目的View,是以Adapter能快速地修改要綁定的控件的外觀和功能。下面的清單顯示了兩個最有用和最通用的本地Adapter:

❑ ArrayAdapter

ArrayAdapter是一個綁定View到一組對象的通用類。預設情況下,ArrayAdapter綁定每個對象的toString值到在layout中預先定義的TextView控件上。可變通的,構造函數允許你使用更加複雜的layout或者通過重寫getView方法來擴充類進而使用TextView的替代物(如ImageView或嵌套的layout)。

❑ SimpleCursorAdapter

SimpleCursorAdapter綁定View到Content Provider查詢傳回的遊标上。指定一個XML layout定義,然後将資料集中的每一列的值綁定到layout中的一個View上。

接下來的章節将深入挖掘這些Adapter類的細節。例子中,提供了綁定資料到ListView上,盡管這個邏輯會和其他一些AdapterView類(如Spinner和Gallery)工作的一樣。

使用Adapter進行資料綁定

将Adapter應用到繼承自AdapterView類上,你需要調用View的setAdapter方法,傳入一個Adapter執行個體,如下面的片段所示:

ArrayList<String> myStringArray = new ArrayList<String>();

ArrayAdapter<String> myAdapterInstance;

int layoutID = android.R.layout.simple_list_item_1;

myAdapterInstance = new ArrayAdapter<String>(this, layoutID, myStringArray);

myListView.setAdapter(myAdapterInstance);

這個片段顯示了最簡單的情況,将數組中的字元串綁定到ListView中用于顯示每個項目的簡單TextView控件上。

接下來的第一個例子顯示了如何綁定一組複雜的對象到ListView上,使用一個自定義的layout。第二個例子顯示了如何使用SimpleCursorAdapter來綁定查詢結果到ListView中的自定義layout上。

在android開發中清單的使用是十分常見的。google對清單的封裝使清單既有顯示傳統文本清單的能力,也有加入了諸如選擇項、複選項等處理事件的能力。這裡寫一些我這幾天對這個問題的了解。

在android的api中,LIST和adapter都被放在了android.widget包内。包内的具體結構我這裡先不展示了,主要側重清單和adapter。adapter的作用就是将要在清單内顯示的資料和清單本身結合起來。清單本身隻完成顯示的作用,其實他就是繼承自VIEWGROUP類。但是他又有一個獨特的函數就是setAdapter()就是完成了view和adapter的結合。adapter如同其本身含義,其實就是一個擴充卡,他可以對要顯示的資料進行統一的封裝,主要是将資料變成view提供給list。

我們先來看看adapter的體系:

public interface Adapter----0層(表示繼承體系中的層次)

public interface ExpandableListAdapter---(無所謂層次因為沒有其他接口繼承實作它)

這是adapter的始祖,其他個性化的adapter均實作它并加入自己的接口。

public interface ListAdapter----1層

public interface SpinnerAdapter----1層

public interface WrapperListAdapter----2層(實作ListAdapter)

以上接口層面上的體系已經完了。可以看出來作為widget view的橋梁adapter其實隻分為2種:ListAdapter和SpinnerAdapter以及ExpandableListAdapter。也就是說所有widget也就是基于list和spinne與ExpandableList三種view形式的。

由于在實際使用時,我們需要将資料加入到Adapter,而以接口形式呈現的adapter無法儲存資料,于是Adapter就轉型為類的模式。

public abstract class BaseAdapter----2層(實作了ListAdapter和SpinnerAdapter)

以抽象類的形式出現構造了類型态下的頂層抽象,包容了List和Spinner

public class ArrayAdapter----3層

public class SimpleAdapter---3層

public class CursorAdapter----3層(CursorAdapter其後還有子類這裡先不探讨)

基本體系有了之後,讓我們看看頂層Adapter裡有哪些方法(隻列舉常用的):

abstract Object getItem(int position)

abstract int getCount()

abstract long getItemId(int position)

abstract int getItemViewType(int position)

abstract View getView(int position,View convertVeiw,ViewGroup parent)

以上是比較重要的方法,ArrayAdapter他們也是重新實作以上方法的。在實際的開發過程中,往往我們要自己做屬于自己的Adapter,以上方法都是需要重新實作的。

ArrayAdapter和SimpleCursorAdapter例子

  • 使用ArrayAdapter定制To-Do List

    這個例子将擴充To-Do List工程,以一個ToDoItem對象來儲存每一個項目,包含每個項目的建立日期。

    你将擴充ArrayAdapter類來綁定一組ToDoItem對象到ListView上,并定制用于顯示每一個ListView項目的layout。

    1. 傳回到To-Do List工程。建立一個新的ToDoItem類來儲存任務和任務的建立日期。重寫toString方法來傳回一個項目資料的概要。

    package com.paad.todolist; import java.text.SimpleDateFormat; import java.util.Date; public class ToDoItem { String task; Date created; public String getTask() { return task; } public Date getCreated() { return created; } public ToDoItem(String _task) { this(_task, new Date(java.lang.System.currentTimeMillis())); } public ToDoItem(String _task, Date _created) { task = _task; created = _created; } @Override public String toString() { SimpleDateFormat sdf = new SimpleDateFormat(“dd/MM/yy”); String dateString = sdf.format(created); return “(“ + dateString + “) “ + task; } }

    2. 打開ToDoList Activity,修改ArrayList和ArrayAdapter變量的類型,儲存ToDoItem對象而不是字元串。然後,你将修改onCreate方法來更新相應的變量初始化。你還需要更新onKeyListener處理函數來支援ToDoItem對象。

  1. private ArrayList<ToDoItem> todoItems;  
  2. private ListView myListView;   
  3. private EditText myEditText;   
  4. private ArrayAdapter<ToDoItem> aa;   
  5. @Override public void onCreate(Bundle icicle) {  
  6.  super.onCreate(icicle); // Inflate your view setContentView(R.layout.main); //    
  7.  Get references to UI widgets myListView = (ListView)findViewById(R.id.myListView);  
  8.     myEditText = (EditText)findViewById(R.id.myEditText);   
  9.     todoItems = new ArrayList<ToDoItem>();   
  10.     int resID = R.layout.todolist_item; aa = new ArrayAdapter<ToDoItem>(this, resID, todoItems);   
  11.     myListView.setAdapter(aa);  
  12.  myEditText.setOnKeyListener(new OnKeyListener() {   
  13. public boolean onKey(View v, int keyCode, KeyEvent event) {  
  14.  if (event.getAction() == KeyEvent.ACTION_DOWN)  
  15.    if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {   
  16.    ToDoItem newItem; newItem = new ToDoItem(myEditText.getText().toString()); todoItems.add(0, newItem);  
  17.     myEditText.setText(“”);   
  18.     aa.notifyDataSetChanged(); cancelAdd(); return true;   
  19.       }   
  20.     return false;   
  21.     }  
  22.   });  
  23.  registerForContextMenu(myListView);   
  24. }   

3. 如果你運作Activity,它将顯示每個to-do項目,如圖5-3所示。

Android 中的 adapterArrayAdapter和SimpleCursorAdapter例子

圖5-3

4. 現在,你可以建立一個自定義的layout來顯示每一個to-do項目。修改在第4章中建立的自定義layout,包含另外一個TextView,它将用于顯示每個to-do項目的建立日期。

Java代碼 

Android 中的 adapterArrayAdapter和SimpleCursorAdapter例子
  1. import java.text.SimpleDateFormat;    
  2. import android.content.Context;    
  3. import java.util.*;     
  4. import android.view.*;    
  5.  import android.widget.*;     
  6. public class ToDoItemAdapter extends ArrayAdapter<ToDoItem> { int resource; public ToDoItemAdapter(Context _context,int _resource, List<ToDoItem> _items) {     
  7. super(_context, _resource, _items); resource = _resource;    
  8.  }     
  9.  @Override public View getView(int position, View convertView,  ViewGroup parent) {     
  10.   LinearLayout todoView; ToDoItem item = getItem(position);     
  11.   String taskString = item.getTask();     
  12.   Date createdDate = item.getCreated();     
  13.   SimpleDateFormat sdf = new SimpleDateFormat(“dd/MM/yy”);     
  14.   String dateString = sdf.format(createdDate); if (convertView == null) { todoView = new LinearLayout(getContext());     
  15.   String inflater = Context.LAYOUT_INFLATER_SERVICE;     
  16.   LayoutInflater vi; vi = (LayoutInflater)getContext().getSystemService(inflater); vi.inflate(resource, todoView, true);     
  17. }     
  18. else {    
  19.  todoView = (LinearLayout) convertView;     
  20. }     
  21.   TextView dateView = (TextView)todoView.findViewById (R.id.rowDate);     
  22.  TextView taskView = (TextView)todoView.findViewById(R.id.row);      dateView.setText(dateString);    
  23.     taskView.setText(taskString);    
  24.     return todoView;     
  25.   }     
  26. }   

5. 建立一個新的類(ToDoItemAdapter),使用指定的ToDoItem變量來擴充一個ArrayAdapter。重寫getView方法來将ToDoItem對象中的task和date屬性指定給第4步建立的layout中的View。

Java代碼 

Android 中的 adapterArrayAdapter和SimpleCursorAdapter例子
  1. import java.text.SimpleDateFormat;    
  2. import android.content.Context;    
  3. import java.util.*;     
  4. import android.view.*;    
  5.  import android.widget.*;     
  6. public class ToDoItemAdapter extends ArrayAdapter<ToDoItem> { int resource; public ToDoItemAdapter(Context _context,int _resource, List<ToDoItem> _items) {     
  7. super(_context, _resource, _items); resource = _resource;    
  8.  }     
  9.  @Override public View getView(int position, View convertView,  ViewGroup parent) {     
  10.   LinearLayout todoView; ToDoItem item = getItem(position);     
  11.   String taskString = item.getTask();     
  12.   Date createdDate = item.getCreated();     
  13.   SimpleDateFormat sdf = new SimpleDateFormat(“dd/MM/yy”);     
  14.   String dateString = sdf.format(createdDate); if (convertView == null) { todoView = new LinearLayout(getContext());     
  15.   String inflater = Context.LAYOUT_INFLATER_SERVICE;     
  16.   LayoutInflater vi; vi = (LayoutInflater)getContext().getSystemService(inflater); vi.inflate(resource, todoView, true);     
  17. }     
  18. else {    
  19.  todoView = (LinearLayout) convertView;     
  20. }     
  21.   TextView dateView = (TextView)todoView.findViewById (R.id.rowDate);     
  22.  TextView taskView = (TextView)todoView.findViewById(R.id.row);      dateView.setText(dateString);    
  23.     taskView.setText(taskString);    
  24.     return todoView;     
  25.   }     
  26. }   

6. 最後,用ToDoItemAdapter替換ArrayAdapter的定義。

private ToDoItemAdapter aa;

在onCreate中,使用new ToDoItemAdapter來替換ArrayAdapter<String>的執行個體化。

aa = new ToDoItemAdapter(this, resID, todoItems);

7. 如果你運作Activity,它看起來如圖5-4的截圖。

Android 中的 adapterArrayAdapter和SimpleCursorAdapter例子

圖5-4

使用SimpleCursorAdapter

SimpleCursorAdapter允許你綁定一個遊标的列到ListView上,并使用自定義的layout顯示每個項目。

SimpleCursorAdapter的建立,需要傳入目前的上下文、一個layout資源,一個遊标和兩個數組:一個包含使用的列的名字,另一個(相同大小)數組包含View中的資源ID,用于顯示相應列的資料值。

下面的架構代碼顯示了如何構造一個SimpleCursorAdapter來顯示聯系人資訊:

Java代碼 

Android 中的 adapterArrayAdapter和SimpleCursorAdapter例子
  1. String uriString = “content://contacts/people/”;     
  2. Cursor myCursor = managedQuery(Uri.parse(uriString), null, null, null, null);     
  3. String[] fromColumns = new String[] {    
  4.     People.NUMBER,     
  5.     eople.NAME    
  6. };    
  7.  int[] toLayoutIDs = new int[] {     
  8.      R.id.nameTextView,    
  9.      R.id.numberTextView    
  10. };     
  11. SimpleCursorAdapter myAdapter; myAdapter = new SimpleCursorAdapter(this,R.layout.simplecursorlayout,myCursor,fromColumns,toLayoutIDs)  

SimpleCursorAdapter在本章前面的建立選擇聯系人的例子中使用過。你将在第6章學習到更多關于Content Provider和Cursor的内容,那裡你也将找到更多SimpleCursorAdapter的例子。

首先來看一下Adapter的體系結構:

一個Adapter的對象扮演一個橋梁的角色。這個橋梁連接配接着一個AdapterView和它所包含的資料。Adapter提供了一個通到資料項的途徑。Adapter還負責為在資料集裡的每個資料生項生成一個View。它有一個重要的方法:public abstract View getView (int position,View convertView,ViewGroup parent)。這個方法被setListAdapter(adapter)間接地調用。getView 方法的作用是得到一個View,這個view顯示資料項裡指定位置的資料,你可以或者手動建立一個view或者從一個XML layout中inflate。當這個view被inflated,它的父view(如GridView,ListView等)将要使用預設的 layout參數,除非你用inflate(int,android.view.ViewGroup,boolean)方法來指定一個根view并防止附着在根上。

下面分别講一下它的幾個常見的子類:

ListAdapter接口:繼承于Adapter。ListAdapter是一個ListView和list上的資料之間的橋梁。資料經常來自于一個Cursor,但這不是必須的。ListView能顯示任何資料,隻要它是被一個ListAdapter包裝的。

BaseAdapter抽象類:是一個實作了既能在ListView(實作了ListAdapter接口)和Spinner(實作了Spinner 接口)裡用的Adapter類的一般基類。

ArrayAdapter類:一個管理這樣的ListView的ListAdapter:這個ListView被一個數組所支援。這個數組可裝任意對象。預設狀态下,這個類預期能這樣:提供的資源id與一個單獨的TextView相關聯。如果你想用一個更複雜的layout,就要用包含了域id的構造函數。這個域id能夠與一個在更大的layout資源裡的TextView相關聯。它将被在數組裡的每個對象的toString()方法所填滿。你可以添加通常對象的lists或arrays。重寫你對象的toString()方法來決定list裡哪一個寫有資料的text将被顯示。如果想用一些其它的不同于TextView的view來顯示數組(比如ImageViews),或想有一些除了toString()傳回值所填在views裡的以外的資料,你就要重寫getView(int,View,ViewGroup)方法來傳回你想要的View類型。

SimpleAdapter類:一個使靜态資料和在XML中定義的Views對應起來的簡單adapter。你可以把list上的資料指定為一個 Map範型的ArrayList。ArrayList裡的每一個條目對應于list裡的一行。Maps包含着每一行的資料。你先要指定一個XML,這個 XML定義了用于顯示一行的view。你還要指定一個對應關系,這個對應關系是從Map的keys對應到指定的views。綁定資料到views發生在兩個階段:

如果一個simpleAdapter.ViewBinder是可用的,那麼 SetViewValue(android.view.View,Object,String)要被調用。如果傳回true,那麼綁定發生了。如果傳回 false,那麼如下views将被按順序地嘗試:

~實作了Checkable的View(如CheckBox),預期的綁定值是boolen

~TextView,預期的綁定值是String,并且SetViewText方法被調用

~ImageView,預期的綁定值是一個資源的id或String。并且SetViewImage方法被調用

如果沒有合适的綁定被發現,一個IllegalStateException被抛出。

下面是一個SimpleAdapter的例子:

xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent">

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:id="@+id/MyListView">

xmlns:android="http://schemas.android.com/apk/res/android"

android:id="@+id/widget0"

android:layout_width="fill_parent"

android:layout_height="fill_parent">

android:id="@+id/ItemImage"

android:layout_width="wrap_content"

android:layout_height="wrap_content">

android:id="@+id/ItemTitle"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="TextView"

android:layout_x="50px"

android:textSize="15pt">

android:id="@+id/ItemText"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="TextView"

android:layout_x="50px"

android:layout_y="40px">

android:id="@+id/ItemCheck"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_x="270px">

public class TestUIList extends Activity {

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

ListView list = (ListView) findViewById(R.id.MyListView);

ArrayList> lstImageItem = new ArrayList>();

String[] str1 = { "row one", "row two", "row three", "row four" };

String[] str2 = { "第一行", "第二行", "第三行", "第四行" };

for (int i = 0; i < str1.length; i++) {

HashMap map = new HashMap();

map.put("ItemImage", R.drawable.icon);

map.put("ItemTitle", str1[i]);

map.put("ItemText", str2[i]);

lstImageItem.add(map);

}

SimpleAdapter saImageItems = new SimpleAdapter(this,

lstImageItem,

R.layout.simple_list_item_2,

new String[] { "ItemImage", "ItemTitle", "ItemText" },

new int[] { R.id.ItemImage, R.id.ItemTitle, R.id.ItemText });

list.setAdapter(saImageItems);

}

}

如果你的ListView的每一行想實作被點選後有響應事件。最省事發的方法是繼承一個ListActivity。ListActivity是一個這樣的Activity:這個Activity能通過綁定到一個像array或cursor這樣的資料源來顯示一些items的list,并且當使用者選了一個item時,能夠暴露事件句柄。

ListActivity擁有一個ListView對象。這個ListView對象能夠被綁定到不同的資料源,特别是一個數組或者一個擁有查詢結果的Cursor。ListActivity有三種用法,分别是Binding,Screen Layout和Row Layout。下面僅讨論一下Screen Layout:

ListActivity有一個預設的layout。這個 layout是由一個在螢幕中央的、單獨的、全屏的list構成。然而,如果你想的話,你可以通過在onCreate()裡調用 setContentView()方法來設定你自己的view layout的方式制定螢幕layout。要這樣做,你自己的view必須包含一個id為“@android:id/android:list”(或者在代碼中有list對象)。

随意地,當你制定這個view是空的時,你能夠包含任何類型的view對象來顯示。這個“空list”通知者必須有一個 id“android:empty”。

注意,最後一定要調用setListAdapter(adapter)方法來把通過Adapter綁定了資料的這個List顯示出來。 setListAdapter方法間接調用了Adapter的getView方法,其作用是傳回你想要的view類型。而且當點選listView裡的 item時,會根據getView重畫這個ListView。例子可參見《Android SDK開發大全》中的“資料總管“的例子。

想要實作事件監聽,就要重寫 protected void onListItemClick(ListView l, View v, int position,long id)方法。

想要把在XML中自定義了一行的view逐行顯示在ListActivity中自定義的ListView中,并且在每行動态綁定資料的話,一般要自己寫一個MyAdapter類,這個Adapter繼承BaseAdapter并且其構造函數中至少有一個List參數來實作動态綁定資料。有兩個重要的步驟:

1)重寫getView方法,其中一重要步驟就是用items.get(position)方法來獲得被傳入的資料。其中items是一個 List,它被賦了傳入的List參數的值。position是這個資料在ListView中的行數。Get傳回的是E類型.即List中的模闆類型。

2)寫一個内部類private class ViewHolder。這個内部類隻有成員變量,它們就是你想在ListView中的一行裡要顯示的小View成分。

要想在Adapter中動态傳入其它類的資料,就要在構造函數中再增加一個(或更多)List參數。

最後我們給出自己些的MyAdapter配合ListActivity實作監聽事件的例子:

package com.li.android.myhome;

import java.util.ArrayList;

import java.util.List;

import android.app.AlertDialog;

import android.app.ListActivity;

import android.content.DialogInterface;

import android.os.Bundle;

import android.view.View;

import android.widget.ListView;

public class MyHome extends ListActivity

{

private List items = null;

protected void onCreate(Bundle icicle)

{

super.onCreate(icicle);

setContentView(R.layout.main);

items = new ArrayList();

String[] titles =

{ "預設主題", "主題-A", "主題-B", "中秋佳節", "粉紅女郎", "花樣年華" };

for (int i = 0; i < titles.length; i++)

{

String title = titles[i];

items.add(title);

}

setListAdapter(new MyAdapter(this, items));

}

@Override

protected void onListItemClick(ListView l, View v, int position,

long id)

{

new AlertDialog.Builder(MyHome.this).setItems(

R.array.items_my_dialog, new DialogInterface.OnClickListener()

{

public void onClick(DialogInterface dialog, int whichcountry)

{

}

})

.show();

}

}

package com.li.android.myhome;

import java.util.List;

import android.content.Context;

import android.graphics.Bitmap;

import android.graphics.BitmapFactory;

import android.view.LayoutInflater;

import android.view.View;

import android.view.ViewGroup;

import android.widget.BaseAdapter;

import android.widget.ImageView;

import android.widget.TextView;

public class MyAdapter extends BaseAdapter

{

private LayoutInflater mInflater;

private Bitmap icom_theme;

private Bitmap icon_selected32;

private List items;

public MyAdapter(Context context, List it)

{

mInflater = LayoutInflater.from(context);

items = it;

icom_theme = BitmapFactory.decodeResource(context.getResources(),

R.drawable.theme);

icon_selected32 = BitmapFactory.decodeResource(context

.getResources(), R.drawable.selected32);

}

public int getCount()

{

return items.size();

}

public Object getItem(int position)

{

return items.get(position);

}

public long getItemId(int position)

{

return position;

}

public View getView(int position, View convertView, ViewGroup parent)

{

ViewHolder holder;

convertView = mInflater.inflate(R.layout.file_row, null);

holder = new ViewHolder();

holder.text = (TextView) convertView.findViewById(R.id.text);

holder.mTheme = (ImageView) convertView.findViewById(R.id.theme);

holder.mSelected32 = (ImageView) convertView

.findViewById(R.id.selected32);

holder.mTheme.setImageBitmap(icom_theme);

String title = items.get(position);

holder.text.setText(title);

holder.mSelected32.setImageBitmap(icon_selected32);

return convertView;

}

private class ViewHolder

{

TextView text;

ImageView mTheme;

ImageView mSelected32;

}

}

xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

>

android:id="@android:id/list"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

>

xmlns:android="http://schemas.android.com/apk/res/android"

android:id="@+id/widget0"

android:layout_width="fill_parent"

android:layout_height="fill_parent" >

android:layout_width="wrap_content"

android:layout_height="wrap_content"

>

android:id="@+id/text"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:layout_x="50px"

android:textSize="15pt">

android:id="@+id/selected32"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_x="270px">