在开发中,给数据容器视图适配数据是最经常用到的,最经常用的适配器有BaseAdapter,ArrayAdapter......,很多人用的都很熟悉了。虽然如此,我相信大部分人对Adpter适配器的大家族还是很模糊的,比如:很经常在实现一个Apdate子类的时候,不知道使用ArrayAdapter还是BaseAdapter好,因为对它们的区别不清楚,所以造成了选择误区。为了了解这个大家族,接下来会讲解到Adapter,ListAdapter,SpinnerAdapter,BaseAdapter,ArrayAdapter,CursorAdapter这些最经常使用的数据适配器。首先先看一下下图:
这个图反应了上述几个接口或者类之间的继承或实现关系,比如ArrayAdapter是BaseAdapter的子类。接下来,让我们一个一个来了解它们的区别。
一、Adapter:
Adapter是所有Adapter子类的父类,它是一个接口,里面定义了很多基本的方法。通常,Adapter是作为连接AdapterView(ListView...)和数据集的桥梁。它可以获取数据集合中的每一项并于AdapterView(ListView.....)相应的视图项一一对应起来,将数据显示。
Adapter主要方法解析:
int getCount():
获取与当前Adapter连接的数据集合的项数总和。
Object getItem(int Posistion):
获取与当前Adapter绑定的数据集中指定下标的项。
long getItemId(int position):
获取与当前Adapter绑定的数据集中指定下标的项所在的row id(即在list中的下标)
View getView(int position,View convertView,ViewGroup parent):
这个方法是实现Adapter子类的难点和最重要的地方,往往最容易出现问题的地方也是这里,所以要特别注意。这个方法目的在于获取一个用于展示当前数据集中指定下标的数据项的视图。通常有两种方法来获取View,自己创建一个View或者用XML布局填充来获得一个View。通常情况下,在填充一个View的时候,如果没有在LayoutInflater.inflate方法中指定View的根视图的话,会默认的将当前View附加到父视图(ListView....)中,并用默认的布局参数填充它。
重要参数说明:
convertView:这个参数在优化Adapter数据填充的效率方面是非常有用的。它表示一个可复用的旧视图,通常情况下,在使用它之前,需要先判断它是否为空,为空的话,需要先给他填充一个视图。不为空的话就可以复用了。其实在Adapter中,一个AdapterView填充数据的时候可以指定多个布局的(很多人以为只能是一种布局),所以在我们进行获取convertView显示数据的时候,应该配合getItemViewType方法来判断用于填充当前的数据项的视图是什么(如果只有一种视图,就没必要进行这些判断了)。只有这样才可以在合适的地方显示合适的数据。稍后会用详细的例子说明,暂时我们知道有这样的功能就行。
parent:当前的视图所依赖的父视图,默认会使用它提供的布局参数。
int getItemViewType(int position):
通过它返回的结果可以用来判断用于填充数据集中指定下标的数据项所使用的视图类型。此方法返回的值应该介于0-(getViewTypeCount返回的结果-1)之间。
int getViewTypeCount():
返回getView可能返回的视图的类型的总和,如果getView每次返回的结果都是一样的,这个方法返回1。adpterView视图有几个不一样的视图,这个方法就返回多少。
在Adapter接口中其实可以注册观察对象来观察当前Adapter所关联的数据集是否发生改变,当数据集发生变化时(包括数据集数据的改变或者数据集变得不可用),就会通知观察者,如果我们对数据集的变化高度敏感,就需要关注DataSetObserver这个抽象类,这里大概介绍一下,这个类是一个抽象类,定义了两个回调方法,在数据集发生改变时,会回调相关的方法,这两个方法分别是:onChanged,onInvalidated.前者在数据集发生变化时会被调用,比如重新查询了数据导致数据集合发生了改变,后者在数据集变的不可用的时候会被调用,比如Cursor数据源被关闭了。
Adapter有两个方法,可以进行注册观察者和解绑观察者。
void registerDataSetObserver(DataSetObserver observer):
给当前的Adapter注册一个observer对象,在绑定的数据集发生变化时进行回调。
void unregisterDataSetObserver(DataSetObserver observer):
解除之前在此Adapter中注册的observer的监听,之后将不再关注数据集变化。
对于Adapter接口就介绍这么多,那么问题来了,我们可不可以自己自定义一个类直接实现Adapter接口呢。答案是可以的(AdapterViewAnimator的setAdapter方法可以用到,但是这是进行动画切换的,我们几乎不会接触到),但是这么做,很可能会没有用处,为什么?因为普遍的数据视图容器,比如ListView,GridView,setAdapter方法要求的参数类别是ListAdapter,而Gallery,Spinner的setAdapter方法要求的参数类别是SpinnerAdapter,所以直接实现Adapter接口的类无法在这些数据视图容器里工作。所以接下来我们关注一下ListAdapter和SpinnerAdapter,看看这两个接口里面做了什么。
二、ListAdapter
ListAdapter是一个继承了Adapter的接口,用于作为ListView和GridView连接数据的桥梁,对于ListAdater来说,它对于数据集的数据类型没有任何要求。对于这个接口来说,只是简单的拓展了两个方法,用于判断列表中的项是否可用。方法如下:
public boolean areAllItemsEnabled();
表示当前Adapter的所有项都是可用的(可选择和可点击触发事件)。
boolean isEnable(int position)
判断Adpter中指定的项是否可用。
运用Adapter和ListAdapter的知识,接下来做一个简单的例子,用一个ListView呈现1-17的数据,其中奇数就使用靠左边的TextView,偶数就使用靠右边的TextView。
首先看奇数和偶数的布局:
偶数:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tvContent"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_gravity="right"
android:layout_marginLeft="100dp"
android:background="#efefef"
android:gravity="center"
android:text="even"
android:textSize="20dp" />
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tvContent"
android:background="#aaaabb"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_gravity="left"
android:layout_marginRight="100dp"
android:gravity="center"
android:text="Odd"
android:textSize="20dp" />
</LinearLayout>
<resources>
<string name="app_name">Adapter</string>
<string-array name="content" >
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
<item>5</item>
<item>6</item>
<item>7</item>
<item>8</item>
<item>9</item>
<item>10</item>
<item>11</item>
<item>12</item>
<item>13</item>
<item>14</item>
<item>15</item>
<item>16</item>
<item>17</item>
</string-array>
</resources&