learningITwell 2010-10-12
Adapter是将数据绑定到UI界面上的桥接类。Adapter负责创建显示每个项目的子View和提供对下层数据的访问。
支持Adapter绑定的UI控件必须扩展AdapterView抽象类。创建自己的继承自AdapterView的控件和创建新的Adapter类来绑定它们是可能的。
一些Android提供的Adapter介绍
在多数情况下,你不需要白手创建自己的Adapter。Android提供了一系列Adapter来将数据绑定到UIWidget上。
因为Android负责提供数据和选择用于显示每个项目的View,所以Adapter能快速地修改要绑定的控件的外观和功能。下面的列表显示了两个最有用和最通用的本地Adapter:
❑ArrayAdapter
ArrayAdapter是一个绑定View到一组对象的通用类。默认情况下,ArrayAdapter绑定每个对象的toString值到在layout中预先定义的TextView控件上。可变通的,构造函数允许你使用更加复杂的layout或者通过重写getView方法来扩展类从而使用TextView的替代物(如ImageView或嵌套的layout)。
❑SimpleCursorAdapter
SimpleCursorAdapter绑定View到ContentProvider查询返回的游标上。指定一个XMLlayout定义,然后将数据集中的每一列的值绑定到layout中的一个View上。
接下来的章节将深入挖掘这些Adapter类的细节。例子中,提供了绑定数据到ListView上,尽管这个逻辑会和其他一些AdapterView类(如Spinner和Gallery)工作的一样。
使用Adapter进行数据绑定
将Adapter应用到继承自AdapterView类上,你需要调用View的setAdapter方法,传入一个Adapter实例,如下面的片段所示:
ArrayList<String>myStringArray=newArrayList<String>();
ArrayAdapter<String>myAdapterInstance;
intlayoutID=android.R.layout.simple_list_item_1;
myAdapterInstance=newArrayAdapter<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的体系:
publicinterfaceAdapter----0层(表示继承体系中的层次)
publicinterfaceExpandableListAdapter---(无所谓层次因为没有其他接口继承实现它)
这是adapter的始祖,其他个性化的adapter均实现它并加入自己的接口。
publicinterfaceListAdapter----1层
publicinterfaceSpinnerAdapter----1层
publicinterfaceWrapperListAdapter----2层(实现ListAdapter)
以上接口层面上的体系已经完了。可以看出来作为widgetview的桥梁adapter其实只分为2种:ListAdapter和SpinnerAdapter以及ExpandableListAdapter。也就是说所有widget也就是基于list和spinne与ExpandableList三种view形式的。
由于在实际使用时,我们需要将数据加入到Adapter,而以接口形式呈现的adapter无法保存数据,于是Adapter就转型为类的模式。
publicabstractclassBaseAdapter----2层(实现了ListAdapter和SpinnerAdapter)
以抽象类的形式出现构造了类型态下的顶层抽象,包容了List和Spinner
publicclassArrayAdapter----3层
publicclassSimpleAdapter---3层
publicclassCursorAdapter----3层(CursorAdapter其后还有子类这里先不探讨)
基本体系有了之后,让我们看看顶层Adapter里有哪些方法(只列举常用的):
abstractObjectgetItem(intposition)
abstractintgetCount()
abstractlonggetItemId(intposition)
abstractintgetItemViewType(intposition)
abstractViewgetView(intposition,ViewconvertVeiw,ViewGroupparent)
以上是比较重要的方法,ArrayAdapter他们也是重新实现以上方法的。在实际的开发过程中,往往我们要自己做属于自己的Adapter,以上方法都是需要重新实现的。
ArrayAdapter和SimpleCursorAdapter例子
使用ArrayAdapter定制To-DoList
这个例子将扩展To-DoList工程,以一个ToDoItem对象来储存每一个项目,包含每个项目的创建日期。
你将扩展ArrayAdapter类来绑定一组ToDoItem对象到ListView上,并定制用于显示每一个ListView项目的layout。
1.返回到To-DoList工程。创建一个新的ToDoItem类来保存任务和任务的创建日期。重写toString方法来返回一个项目数据的概要。
packagecom.paad.todolist;
importjava.text.SimpleDateFormat;
importjava.util.Date;
publicclassToDoItem{
Stringtask;
Datecreated;
publicStringgetTask()
{
returntask;
}
publicDategetCreated()
{
returncreated;
}
publicToDoItem(String_task)
{
this(_task,newDate(java.lang.System.currentTimeMillis()));
}
publicToDoItem(String_task,Date_created)
{
task=_task;
created=_created;
}
@Override
publicStringtoString()
{
SimpleDateFormatsdf=newSimpleDateFormat(“dd/MM/yy”);
StringdateString=sdf.format(created);return“(“+dateString+“)“+task;
}
}
2.打开ToDoListActivity,修改ArrayList和ArrayAdapter变量的类型,储存ToDoItem对象而不是字符串。然后,你将修改onCreate方法来更新相应的变量初始化。你还需要更新onKeyListener处理函数来支持ToDoItem对象。
1.privateArrayList<ToDoItem>todoItems;
2.privateListViewmyListView;
3.privateEditTextmyEditText;
4.privateArrayAdapter<ToDoItem>aa;
5.@OverridepublicvoidonCreate(Bundleicicle){
6.super.onCreate(icicle);//InflateyourviewsetContentView(R.layout.main);//
7.GetreferencestoUIwidgetsmyListView=(ListView)findViewById(R.id.myListView);
8.myEditText=(EditText)findViewById(R.id.myEditText);
9.todoItems=newArrayList<ToDoItem>();
10.intresID=R.layout.todolist_item;aa=newArrayAdapter<ToDoItem>(this,resID,todoItems);
11.myListView.setAdapter(aa);
12.myEditText.setOnKeyListener(newOnKeyListener(){
13.publicbooleanonKey(Viewv,intkeyCode,KeyEventevent){
14.if(event.getAction()==KeyEvent.ACTION_DOWN)
15.if(keyCode==KeyEvent.KEYCODE_DPAD_CENTER){
16.ToDoItemnewItem;newItem=newToDoItem(myEditText.getText().toString());todoItems.add(0,newItem);
17.myEditText.setText(“”);
18.aa.notifyDataSetChanged();cancelAdd();returntrue;
19.}
20.returnfalse;
21.}
22.});
23.registerForContextMenu(myListView);
24.}
3.如果你运行Activity,它将显示每个to-do项目,如图5-3所示。
图5-3
4.现在,你可以创建一个自定义的layout来显示每一个to-do项目。修改在第4章中创建的自定义layout,包含另外一个TextView,它将用于显示每个to-do项目的创建日期。
Java代码
1.importjava.text.SimpleDateFormat;
2.
3.importandroid.content.Context;
4.
5.importjava.util.*;
6.
7.importandroid.view.*;
8.
9.importandroid.widget.*;
10.
11.publicclassToDoItemAdapterextendsArrayAdapter<ToDoItem>{intresource;publicToDoItemAdapter(Context_context,int_resource,List<ToDoItem>_items){
12.
13.super(_context,_resource,_items);resource=_resource;
14.
15.}
16.
17.@OverridepublicViewgetView(intposition,ViewconvertView,ViewGroupparent){
18.
19.LinearLayouttodoView;ToDoItemitem=getItem(position);
20.
21.StringtaskString=item.getTask();
22.
23.DatecreatedDate=item.getCreated();
24.
25.SimpleDateFormatsdf=newSimpleDateFormat(“dd/MM/yy”);
26.
27.StringdateString=sdf.format(createdDate);if(convertView==null){todoView=newLinearLayout(getContext());
28.
29.Stringinflater=Context.LAYOUT_INFLATER_SERVICE;
30.
31.LayoutInflatervi;vi=(LayoutInflater)getContext().getSystemService(inflater);vi.inflate(resource,todoView,true);
32.
33.}
34.
35.else{
36.
37.todoView=(LinearLayout)convertView;
38.
39.}
40.
41.TextViewdateView=(TextView)todoView.findViewById(R.id.rowDate);
42.
43.TextViewtaskView=(TextView)todoView.findViewById(R.id.row);dateView.setText(dateString);
44.
45.taskView.setText(taskString);
46.
47.returntodoView;
48.
49.}
50.
51.}
importjava.text.SimpleDateFormat;
importandroid.content.Context;
importjava.util.*;
importandroid.view.*;
importandroid.widget.*;
publicclassToDoItemAdapterextendsArrayAdapter<ToDoItem>{intresource;publicToDoItemAdapter(Context_context,int_resource,List<ToDoItem>_items){
super(_context,_resource,_items);resource=_resource;
}
@OverridepublicViewgetView(intposition,ViewconvertView,ViewGroupparent){
LinearLayouttodoView;ToDoItemitem=getItem(position);
StringtaskString=item.getTask();
DatecreatedDate=item.getCreated();
SimpleDateFormatsdf=newSimpleDateFormat(“dd/MM/yy”);
StringdateString=sdf.format(createdDate);if(convertView==null){todoView=newLinearLayout(getContext());
Stringinflater=Context.LAYOUT_INFLATER_SERVICE;
LayoutInflatervi;vi=(LayoutInflater)getContext().getSystemService(inflater);vi.inflate(resource,todoView,true);
}
else{
todoView=(LinearLayout)convertView;
}
TextViewdateView=(TextView)todoView.findViewById(R.id.rowDate);
TextViewtaskView=(TextView)todoView.findViewById(R.id.row);dateView.setText(dateString);
taskView.setText(taskString);
returntodoView;
}
}
5.创建一个新的类(ToDoItemAdapter),使用指定的ToDoItem变量来扩展一个ArrayAdapter。重写getView方法来将ToDoItem对象中的task和date属性指定给第4步创建的layout中的View。
Java代码
1.importjava.text.SimpleDateFormat;
2.
3.importandroid.content.Context;
4.
5.importjava.util.*;
6.
7.importandroid.view.*;
8.
9.importandroid.widget.*;
10.
11.publicclassToDoItemAdapterextendsArrayAdapter<ToDoItem>{intresource;publicToDoItemAdapter(Context_context,int_resource,List<ToDoItem>_items){
12.
13.super(_context,_resource,_items);resource=_resource;
14.
15.}
16.
17.@OverridepublicViewgetView(intposition,ViewconvertView,ViewGroupparent){
18.
19.LinearLayouttodoView;ToDoItemitem=getItem(position);
20.
21.StringtaskString=item.getTask();
22.
23.DatecreatedDate=item.getCreated();
24.
25.SimpleDateFormatsdf=newSimpleDateFormat(“dd/MM/yy”);
26.
27.StringdateString=sdf.format(createdDate);if(convertView==null){todoView=newLinearLayout(getContext());
28.
29.Stringinflater=Context.LAYOUT_INFLATER_SERVICE;
30.
31.LayoutInflatervi;vi=(LayoutInflater)getContext().getSystemService(inflater);vi.inflate(resource,todoView,true);
32.
33.}
34.
35.else{
36.
37.todoView=(LinearLayout)convertView;
38.
39.}
40.
41.TextViewdateView=(TextView)todoView.findViewById(R.id.rowDate);
42.
43.TextViewtaskView=(TextView)todoView.findViewById(R.id.row);dateView.setText(dateString);
44.
45.taskView.setText(taskString);
46.
47.returntodoView;
48.
49.}
50.
51.}
importjava.text.SimpleDateFormat;
importandroid.content.Context;
importjava.util.*;
importandroid.view.*;
importandroid.widget.*;
publicclassToDoItemAdapterextendsArrayAdapter<ToDoItem>{intresource;publicToDoItemAdapter(Context_context,int_resource,List<ToDoItem>_items){
super(_context,_resource,_items);resource=_resource;
}
@OverridepublicViewgetView(intposition,ViewconvertView,ViewGroupparent){
LinearLayouttodoView;ToDoItemitem=getItem(position);
StringtaskString=item.getTask();
DatecreatedDate=item.getCreated();
SimpleDateFormatsdf=newSimpleDateFormat(“dd/MM/yy”);
StringdateString=sdf.format(createdDate);if(convertView==null){todoView=newLinearLayout(getContext());
Stringinflater=Context.LAYOUT_INFLATER_SERVICE;
LayoutInflatervi;vi=(LayoutInflater)getContext().getSystemService(inflater);vi.inflate(resource,todoView,true);
}
else{
todoView=(LinearLayout)convertView;
}
TextViewdateView=(TextView)todoView.findViewById(R.id.rowDate);
TextViewtaskView=(TextView)todoView.findViewById(R.id.row);dateView.setText(dateString);
taskView.setText(taskString);
returntodoView;
}
}
6.最后,用ToDoItemAdapter替换ArrayAdapter的定义。
privateToDoItemAdapteraa;
在onCreate中,使用newToDoItemAdapter来替换ArrayAdapter<String>的实例化。
aa=newToDoItemAdapter(this,resID,todoItems);
7.如果你运行Activity,它看起来如图5-4的截图。
图5-4
使用SimpleCursorAdapter
SimpleCursorAdapter允许你绑定一个游标的列到ListView上,并使用自定义的layout显示每个项目。
SimpleCursorAdapter的创建,需要传入当前的上下文、一个layout资源,一个游标和两个数组:一个包含使用的列的名字,另一个(相同大小)数组包含View中的资源ID,用于显示相应列的数据值。
下面的框架代码显示了如何构造一个SimpleCursorAdapter来显示联系人信息:
Java代码
1.StringuriString=“content://contacts/people/”;
2.CursormyCursor=managedQuery(Uri.parse(uriString),null,null,null,null);
3.String[]fromColumns=newString[]{
4.People.NUMBER,
5.People.NAME
6.};
7.int[]toLayoutIDs=newint[]{
8.R.id.nameTextView,
9.R.id.numberTextView
10.};
11.
12.SimpleCursorAdaptermyAdapter;myAdapter=newSimpleCursorAdapter(this,R.layout.simplecursorlayout,myCursor,fromColumns,toLayoutIDs)
StringuriString=“content://contacts/people/”;
CursormyCursor=managedQuery(Uri.parse(uriString),null,null,null,null);
String[]fromColumns=newString[]{
People.NUMBER,
eople.NAME
};
int[]toLayoutIDs=newint[]{
R.id.nameTextView,
R.id.numberTextView
};
SimpleCursorAdaptermyAdapter;myAdapter=newSimpleCursorAdapter(this,R.layout.simplecursorlayout,myCursor,fromColumns,toLayoutIDs)
SimpleCursorAdapter在本章前面的创建选择联系人的例子中使用过。你将在第6章学习到更多关于ContentProvider和Cursor的内容,那里你也将找到更多SimpleCursorAdapter的例子。
前几篇介绍了设计模式的特性并且详细讲解了4种创建型模式,创建型模式是负责如何产生对象实例的,接下来讲讲结构型模式。结构型模式是解析类和对象的内部结构和外部组合,通过优化程序结构解决模块之间的耦合问题。