Android 进阶 (十四)Android Adapter 详解
一、前言
Android 是完全遵循 MVC 模式设计的框架,Activity
是Controller
,layout
是View
。因为layout
五花八门,很多数据都不能直接绑定上去,所以 Android 引入了Adapter
这个机制作为复杂数据展示的转换载体,所以各种Adapter
只不过是转换的方式和能力不一样而已。
Adapter
是将数据绑定到 UI 界面上的桥接类。Adapter
负责创建显示每个项目的子View
和提供对下层数据的访问。
在多数情况下,你不需要创建自己的Adapter
。Android
提供了一系列Adapter
来将数据绑定到UI Widget
上。
因为Android
负责提供数据和选择用于显示每个项目的View
,所以Adapter
能快速地修改要绑定的控件的外观和功能。
Adapter
是用来帮助填充数据的中间桥梁,比如通过它将数据填充到ListView, GridView, Gallery
。
而 android 提供了以下几种:Adapter:ArrayAdapter,BaseAdapter,CursorAdapter, HeaderViewListAdapter,ListAdapter,ResourceCursorAdapter,SimpleAdapter, SimpleCursorAdapter, SpinnerAdapter, WrapperListAdapter
。
根据数据来源形式的不同可以选择不同的Adapter
,比如数据来源于一个Arraylist
就使用 BaseAdapter,SimpleAdapter,而数据来源于通过查询数据库获得 Cursor 那就使用。
几种常用的Adapter
:
二、Adapter 体系介绍
首先来看一下Adapter
的体系结构:
一个 Adapter 的对象扮演一个桥梁的角色。这个桥梁连接着一个AdapterView
和它所包含的数据。Adapter
提供了一个通到数据项的途径。Adapter
还负责为在数据集里的每个数据生项生成一个 View。它有一个重要的方法:
这个方法被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 类型。
SimpleCursorAdapter 类:SimpleCursorAdapter 绑定 View 到 Content Provider 查询返回的游标上。指定一个 XML layout 定义,然后将数据集中的每一列的值绑定到 layout 中的一个 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
被抛出。
不论是那种适配器模式,也不管是Listview
也好还是 gridview 也好,对他们填充数据,都是分三步走。
第一:创建一个数据填充的对象,可以是 ListView, GridView, Gallery。
第二步:创建一个数据填充器,可以是 BaseAdapter、SimpleAdapter,也可以是与数据库相关联的CursorAdapter
。
例如:SimpleAdapter
可以使用系统封装好的,你也可以自己去继承一个 Simpleadapter,来重写其中的方法。继承 simpleadapter
的好处在于,你可以对 listitem 中每个单一的控件设置监听事件等等一系列操作。如果用的是系统封装好的就有点爱莫能助了。
直接使用系统封装的:
重写系统的simpleadpter
:
第三步:将数据填充到对象中去
这样就完成了数据填充器的数据填充。
下面是一个SimpleAdapter
的例子:
如果你的 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
。想要实现事件监听,就要重写 protected void onListItemClick(ListView l, View v, int position,long id)
方法。
想要把在 XML 中自定义了一行的 view 逐行显示在ListActivity
中自定义的 ListView 中,并且在每行动态绑定数据的话,一般要自己写一个MyAdapter
类,这个 Adapter 继承BaseAdapter
并且其构造函数中至少有一个 List 参数来实现动态绑定数据。有两个重要的步骤:
重写 getView 方法,其中一重要步骤就是用 items.get(position)方法来获得被传入的数据。其中 items 是一个 List,它被赋了传入的 List 参数的值。position 是这个数据在 ListView 中的行数。Get 返回的是 E 类型.即 List 中的模板类型。
写一个内部类
private class ViewHolder
。这个内部类只有成员变量,它们就是你想在ListView
中的一行里要显示的小 View 成分。
三、Adapter 应用实战
要想在Adapter
中动态传入其它类的数据,就要在构造函数中再增加一个(或更多)List 参数。
最后给出自己写的 MyAdapter 配合 ListActivity 实现监听事件的例子:
四、Adapter 应用总结
Adapter
在 Android 中占据一个重要的角色,它是数据和UI(View)
之间一个重要的纽带。在常见的View(ListView,GridView)
等地方都需要用到Adapter
。下图直观的表达了Data、Adapter、View
三者的关系。
版权声明: 本文为 InfoQ 作者【No Silver Bullet】的原创文章。
原文链接:【http://xie.infoq.cn/article/17d29c7e42311d1eeb7145b26】。文章转载请联系作者。
评论