Android类库--与Adapter相关的类

来源:互联网 发布:淘宝没有指纹支付选项 编辑:程序博客网 时间:2024/06/08 01:30

<一>Adapter

Class Overview


An Adapter object acts as a bridgebetween an AdapterView and the underlying datafor that view. The Adapter provides access to the data items. TheAdapter is also responsible for making aViewfor each item in the data set.

Adapter对象扮演着适配器视图和底层数据之间桥梁的角色。Adapter提供了访问底层数据的通道,同时也负责为底层数据集中每一个数据项生成一个View对象.加粗这句话是Adapter的精髓所在。Adapter继承层次关系中之所以派生出如此多的Adapter子类,本质就在于底层数据类型的不同(其实Adapter底层数据主要是数组,集合,Cursor对象)和为数据项生成的View对象样式的不同。

下图直观的表达了适配器视图,适配器及底层数据三者的关系:

Android类库--与Adapter相关的类

下面以两种视图方式展现Adapter继承层次关系:

Android类库--与Adapter相关的类

Android类库--与Adapter相关的类

Android类库--与Adapter相关的类
适配器视图的层次关系图:
Android类库--与Adapter相关的类

Adapter方法和域:

由于所有的适配器皆由Adapter派生而来,所以最好对Adapter的每一个方法有所了解.

为了避免产生大量的View浪费内存,在Android中,AdapterView中的View是可回收的使用的。比如你有100项数据要显示,而你的屏幕一次只能显示10条数据,则只产生10个View,当往下拖动要显示第11个View时,会把第1个View的引用传递过去,更新里面的数据再显示,也就是说View可重用,只是更新视图中的数据用于显示新的一项,如果一个视图的视图类型是IGNORE_ITEM_VIEW_TYPE的话,则此视图不会被重用 
static final int IGNORE_ITEM_VIEW_TYPE =AdapterView.ITEM_VIEW_TYPE_IGNORE; 



注册一个Observer,当Adapter所表示的数据改变时会通知它,DataSetObserver是一个抽象类,定义了两个方法:onChanged与onInvalidated 
void registerDataSetObserver(DataSetObserverobserver); 

取消注册一个Observer 
void unregisterDataSetObserver(DataSetObserverobserver); 

返回Adapter所代表的数据集的数据项总数.
int getCount(); 

返回指定位置的数据项 
Object getItem(int position); 

返回指定位置的数据项的ID,大部分适配器数据项ID等于数据项位置,且ID随数据项的增删可变,但CursorAdapter及其子类除外。
long getItemId(int position); 

表示所有数据项的ID是否是稳定不变的,在BaseAdapter中默认返回了false,假设是不稳定的,在CursorAdapter中返回了true,Cursor中的_ID是不变的 
boolean hasStableIds(); 

为每一个数据项产生相应的视图对象 
View getView(int position, View convertView, ViewGroupparent); 

获得指定位置的视图对象的类型,使用一个整数来表示视图对象的类型,如果一个视图对象通过getView()方法可以被转化成另一个视图对象,那么这两个视图对象应该使用同一个类型。比如ArrayAdapter的getItemViewType()返回0,表示所有视图对象都是可重用的,即可以由另一个视图转化而来。
int getItemViewType(int position); 

返回的视图类型的数量。即getView()方法可以返回多少种类型的视图对象。(在HeaderViewListAdapter中可以包含Header和Footer,getView可以返回Header、Footer及Adapter中的视图,但其getViewTypeCount的实现只是调用了内部Adapter的的getViewTypeCount,忽略了Header、Footer中的ViewType) 
int getViewTypeCount(); 

判断Adapter是否不包含任何数据。该方法典型的实现是:returngetCount() == 0.但getCount()可以包括headers和footers,故在特殊的Adapter中该方法的可以有不同的实现.
boolean isEmpty(); 

<二>ListAdapter

Class Overview


Extended Adapter thatis the bridge between a ListView and the datathat backs the list. Frequently that data comes from a Cursor, butthat is not required. The ListView can display any data providedthat it is wrapped in a ListAdapter.

接口ListAdapter继承自Adapter,是ListView与底层数据之间的桥梁。大部分情况底层数据来自于Cursor对象,但并非一定要求这样。ListView(列表视图)可以显示任何由ListAdapte所封装的数据.

常用方法:
接口ListAdapter的方法大部分方继承自Adapter,相比Adapter只增加了两个用于ListView分栏效果的方法:

指示是否Adapter所有数据项都是使能的,如果true,则意味着所有的数据项都是可选中和单击的。但如果改方法的返回值会随时间而改变,则无法保证该方法会起作用.
abstract boolean areAllItemsEnabled()

如果指定位置的数据项不是分栏符则应该返回true,(分栏符是指不能被选中和点击的数据项),如果传进来的position参数是一个无效的参数,应该抛出一个ArrayIndexOutOfBoundsException异常.
abstract boolean isEnabled(int position)

Android类库--与Adapter相关的类

这两个API主要是用来实现适配器视图的分栏效果,即将列表分成多个类别,如上图所示将人名分为"男","女"两类,通过重写这两个Api可将男,女所在菜单项设为不能被选中(unselectable)和点击(unclicked).




<三>SpinnerAdaper

Class Overview


Extended Adapter thatis the bridge between a Spinner and its data. Aspinner adapter allows to define two different views: one thatshows the data in the spinner itself and one that shows the data inthe drop down list when the spinner is pressed.

接口SpinnerAdaper继承自Adapter,是Spinner与底层数据之间的桥梁。SpinnerAdapter允许定义两种不同的视图,其中一种视图用于在spinner自身中显示数据项,另一种视图用于当spinner被按下时弹出的下拉列表中显示数据项.也因此SpinnerAdapter增加了一个方法getDropDownView().

常用方法:

接口SpinnerAdaper的方法大部分方继承自Adapter,相比AdapterSpinnerAdaper只增加了getDropDowmView()方法。

该方法产生用于spinner下拉项的视图
abstract View getDropDownView(intposition, View convertView,ViewGroup parent)

<四>BaseAdapter

Class Overview


Common base class of commonimplementation for an Adapter that can be usedin bothListView (by implementing thespecialized ListAdapter interface} andSpinner (by implementing the specializedSpinnerAdapter interface. 

BaseAdapter是一个抽象类,是所有适配器的基类,基于BaseAdapter实现的适配器既可以用于ListView(通过实现ListAdapter接口)也可以用于Spinner(通过实现SpinnerAdapter接口).

类BaseAdapter对ListAdapter接口和SpinnerAdapter接口中的某些方法做了默认实现,其实现细节如下:

public abstractclass BaseAdapter implements ListAdapter, SpinnerAdapter {
   //提供一些方法,当数据改变时调用注册的DataSetObserver的回调函数
    privatefinal DataSetObservable mDataSetObservable = newDataSetObservable();
    publicboolean hasStableIds() {
       return false;
    }
    public voidregisterDataSetObserver(DataSetObserver observer) {
       mDataSetObservable.registerObserver(observer);
    }
    public voidunregisterDataSetObserver(DataSetObserver observer) {
       mDataSetObservable.unregisterObserver(observer);
    }
   //通知相关联的视图,相应的数据已经改变
    public voidnotifyDataSetChanged() {
       mDataSetObservable.notifyChanged();
    }
    public voidnotifyDataSetInvalidated() {
       mDataSetObservable.notifyInvalidated();
    }
    publicboolean areAllItemsEnabled() {
       return true;
    }
    publicboolean isEnabled(int position) {
       return true;
   }

   //默认情况getDropDownView()是通过getView()来产生下拉项视图的.

   public View getDropDownView(int position, View convertView,ViewGroup parent) {
       return getView(position, convertView, parent);
   }

   public int getItemViewType(int position) {
       return 0;
    }
    public intgetViewTypeCount() {
       return 1;
    }
    publicboolean isEmpty() {
       return getCount() == 0;
    }
}

由上可知,BaseAdapter将getView(),getItem(),getCount(),getItemId()四个方法留给了开发者来实现.接下来开始讨论具体的Adapter


<五>ArrayAdapter

类概述

类ArrayAdapter是一个具体的BaseAdapter,其对应的底层数据可以是任意类型的数组或List类型的集合,默认情况下该类期望提供的一个布局文件,该布局文件仅仅只包含一个TextView.你也可以指定一个Layout并指定该布局文件中一个类型为TextView的资源的ID.

默认情况下,ArrayAdapter为每一个数据项生成一个TextView视图对象,并将数据项toString()方法返回的值作为TextView内容.如果想使用其它的视图对象来显示数据项,比如ImageView又或则想显示除数据项toString()方法返回值以外的数据,可以通过覆写getView()方法.



构造器:

public ArrayAdapter(Context context, int textViewResourceId){
   init(context, textViewResourceId, 0, newArrayList<T>());
}
public ArrayAdapter(Context context, int resource, inttextViewResourceId) {
   init(context, resource, textViewResourceId, newArrayList<T>());
}
public ArrayAdapter(Context context, int textViewResourceId, T[]objects) {
   init(context, textViewResourceId, 0, Arrays.asList(objects));
}
public ArrayAdapter(Context context, int resource, inttextViewResourceId, T[] objects) {
   init(context, resource, textViewResourceId,Arrays.asList(objects));
}
public ArrayAdapter(Context context, int textViewResourceId,List<T> objects) {
   init(context, textViewResourceId, 0, objects);
}
public ArrayAdapter(Context context, int resource, inttextViewResourceId, List<T> objects){
   init(context, resource, textViewResourceId, objects);
}

最多有4个参数,分别为当前Context,layout(可省),TextView的资源ID与数据(可为数组或List),在构造函数中都调用了一个叫init的函数

private void init(Context context, int resource, inttextViewResourceId, List<T> objects){
      mContext = context;
      mInflater =(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
      mResource = mDropDownResource = resource;
      mObjects = objects;
      mFieldId = textViewResourceId;
   }
   
分别赋值给类中的私有域,mInflater为LayoutInflater,产生相应项的视图。

类中有两个域保存数据
private ArrayList<T>mOriginalValues;
private List<T> mObjects;


其中mOriginalValues用于过滤数据时保存过滤前的数据,将过滤后的数据存入mObjects。

在ArrayAdapter中还定义了add,insert,remove,clear函数用于改变数据,并定义了一个布尔变量mNotifyChange用于表示用这些函数改变数据后是否通知视图(调用notifyDataSetChanged,调用这个函数时会把mNotifyChange置为true。

一些函数的实现:

public int getCount() {
    returnmObjects.size();
}
public T getItem(int position) {
    returnmObjects.get(position);
}
public int getPosition(T item) {
    returnmObjects.indexOf(item);
}
public long getItemId(int position) {
    returnposition;
}
public View getView(int position, View convertView, ViewGroupparent) {
    returncreateViewFromResource(position, convertView, parent,mResource);
}
public View getDropDownView(int position, View convertView,ViewGroup parent) {
    returncreateViewFromResource(position, convertView, parent,mDropDownResource);
}

可以看到getView和getDropDownView都通过调用createViewFromResourse来产生视图。

private View createViewFromResource(int position, View convertView,ViewGroup parent,
          int resource) {
      View view;
      TextView text;
 
      if (convertView == null) {
          view = mInflater.inflate(resource, parent, false);
      } else {
          view = convertView;
      }
 
      try {
          if (mFieldId == 0) {
              //  If no custom field is assigned, assume thewhole resource is a TextView
              text = (TextView) view;
          } else {
              //  Otherwise, find the TextView field within thelayout
              text = (TextView) view.findViewById(mFieldId);
          }
      } catch (ClassCastException e) {
          Log.e("ArrayAdapter", "You must supply a resource ID for aTextView");
          throw new IllegalStateException(
                  "ArrayAdapter requires the resource ID to be a TextView", e);
      }
 
      T item = getItem(position);
      if (item instanceof CharSequence) {
          text.setText((CharSequence)item);
      } else {
          text.setText(item.toString());
      }
 
      return view;
   }
   
   
在createViewFromResource中,首先判断convertView是否存在,若不存在则inflate一个,然后判断mFieldID是否为0,若为0则表示传递给ArrayAdapter的资源ID为一TextView,否则是传递了一Layout,mFieldID为此Layout中TextView的ID。然后通过getItem取得相应位置的数据项,判断是否是CharSequence的实例,如果是直接setText,否则调用其toString()函数,可以ArrayAdapter默认只能给TextVext设置字符串,若要使用其他视图,需要重载getView或getDropDownView,一般情况下会继承BaseAdapter自定义自己的Adapter。
在ArrayAdapter中,还有一静态函数

public staticArrayAdapter<CharSequence>createFromResource(Context context,
           int textArrayResId, int textViewResId) {
       CharSequence[] strings =context.getResources().getTextArray(textArrayResId);
       return newArrayAdapter<CharSequence>(context,textViewResId, strings);
    }
    
读取资源文件中定义的字符数组作为数据生成ArrayAdapter,可以看到只能用TextView视图,而不可以指定一Layout再指定Layout中一个TextView的ID。

在ArrayAdapter中还定义了一个ArrayFilter,继承自Filter,用于过滤数据项(当ListView有焦点时,通过键盘输入字符来进行列表项的过滤),其过滤方法为传入一CharSequence,判断相应数据项是否以此CharSequence开头,若不是则用空格分割些数据项,判断分割后的各字符串是否以此CharSequence开头,若是则保留(若数据不是CharSequence则调用其toString())。如果传入的CharSequence为null或长度为0则不过滤。   


<六>CursorAdapter

Class Overview


Adapter that exposes data from aCursor to a ListViewwidget. The Cursor must include a column named "_id" or this classwill not work.

类CursorAdapter是一个抽象类,用于显示Cursor中的数据到ListView控件,Cursor中必须包含名为"_id"的列,否则该类无法工作.

在构造函数中可传递一参数autoRequery表示当cursor的数据改变时是否自动调用cursor的requery()以保持视图数据为最新的。

因为Cursor中的id是固定的,故此类中重写了hasStableIds(),返回true。

public boolean hasStableIds() {         return true; }

在CursorAdapter中,重写的getView及getDropDownView判断传入的convertView是否为null,若为null及相应地调用newView()或newDropDownView()来生成一个视图,而newDropDownView()只有一条语句returnnewView(context, cursor,parent);所以最后都是调用newView(),newView()为abstract的,需要由子类重写。

当通过newView()产生一个View之后,会调用 bindView(v, mContext,mCursor);将cursor中的数据绑定到newView()产生的View之中,此方法同样为abstract的。


为数据项生成视图
abstract newView(Context context,Cursor cursor,ViewGroup parent)
将Cursor中的数据绑定到视图
abstract void bindView(View view,Context context,Cursor cursor)
改变Cursor
changeCursor(Cursor cursor)
生成用于下拉项的视图
View newDropDownView(Contextcontext, Cursor cursor,ViewGroup parent){
   newView(context,cursor,parent)//该函数的默认行为
}



 
<七>ResourceCursorAdapter

如类名所示,该类继承自CursorAdapter,通过XML产生Views,该类只是简单地重写了一些函数,通过LayoutInflater.inflate将XML转换为View


@Overridepublic View newView(Context context, Cursor cursor, ViewGroup parent) {     return mInflater.inflate(mLayout, parent, false); }  @Overridepublic View newDropDownView(Context context, Cursor cursor, ViewGroup parent) {     return mInflater.inflate(mDropDownLayout, parent, false); }

<八>SimpleCursorAdapter

一个ResourceCursorAdapter的简单实现类,用于把cursor中相应的列映射为XML定义的视图中的TextView和ImageView。

该类中定义了一个接口

public static interface ViewBinder {
    booleansetViewValue(View view, Cursor cursor, int columnIndex);
}

 用于把cursor中的数据绑定到视图。

在SimpleCursorAdapter中重写了bindView,控制cursor到视图的绑定,其定义如下

@Override
    public voidbindView(View view, Context context, Cursor cursor) {
       final ViewBinder binder = mViewBinder;
       final int count = mTo.length;
       final int[] from = mFrom;
       final int[] to = mTo;
 
       for (int i = 0; i < count; i++) {
           final View v = view.findViewById(to[i]);
           if (v != null) {
               boolean bound = false;
               if (binder != null) {
                   bound = binder.setViewValue(v, cursor, from[i]);
               }
 
               if (!bound) {
                   String text = cursor.getString(from[i]);
                   if (text == null) {
                       text = "";
                   }
 
                   if (v instanceof TextView) {
                       setViewText((TextView) v, text);
                   } else if (v instanceof ImageView) {
                       setViewImage((ImageView) v, text);
                   } else {
                       throw new IllegalStateException(v.getClass().getName() + " is not a" +
                               " view that can be bounds by this SimpleCursorAdapter");
                   }
               }
           }
       }
    }

可以看到,首先检查类中的私有域mViewBinder是否为null(默认为null,可通过setViewBinder)设置,为不为null则通过binder.setViewValue(v,cursor, from[i]);进行绑定,这个函数若返回true则绑定成功,若返回false则通过SimpleCursorAdapter的规则绑定,判断相应的View是否为TextView或ImageView,若是则绑定,否则抛出异常。

由些可以看到,我们可以自定义一个类实现SimpleCursorAdapter.ViewBinder,然后通过setViewBinder来改变bindView的结果。


<九>SimpleAdapter

一个BaseAdapter的实现类,用于绑定数据到一个XML定义的视图中。数据类型为ArrayList<Map<String,?>>。

SimpleAdapter也实现了Filter接口用于数据的过滤,过滤方法类似ArrayAdapter,只是其数据类型为Map<String,?>,要判断Map中的每一项,若任意一顶符合要求就保留。

SimpleAdapter也是通过bindView函数进行数据的绑定,同SimpleCursorAdapter一样,SimpleAdapter也定义了一个相同的内部接口ViewBinder,在bindView中,首先判断是否通过setViewBinder设置了ViewBinder,若设置了则调用其setViewValue进行数据绑定,如果没有设置其setViewValue返回了false,则进行下面的处理:依次判断View是否为Checkable,TextView,ImageView并进行相应的处理,可见默认情况下SimpleAdapter也是处理TextView与ImageView,当然可以setViewBinder。


<十>WrapperListAdapter

继承自ListAdapder的接口,所以也是一个ListAdapter,同时里面嵌入了另一个ListAdapter,只定义了一个函数用于取得嵌入的ListAdapter

public ListAdapter getWrappedAdapter();


<十一>HeaderViewListAdapter

继承自WrapperListAdapter,当你使用的ListView有页首(Header Views)或页尾(FooterViews)时使用。此类被设计用来作为一个基类,一般不需要使用。

类中定义了两个ArrayList用于保存页首和页尾

ArrayList<ListView.FixedViewInfo>mHeaderViewInfos;
ArrayList<ListView.FixedViewInfo>mFooterViewInfos;

这两个域不为null,在构造函数中判断,如果给这两个域赋值的参数为null,则将他们赋于值HeaderViewListAdapter.EMPTY_INFO_LIST,其定义为
 

static finalArrayList<ListView.FixedViewInfo>EMPTY_INFO_LIST =
       newArrayList<ListView.FixedViewInfo>();

其中ListView.FixedViewInfo的定义为

public class FixedViewInfo {
    public Viewview;ListAdapter#getItem(int)}.
    publicObject data;
    publicboolean isSelectable;
}

该类重定义了getCount,areAllItemsEnabled等函数,把mHeaderViewInfos,mFooterViewInfos同时包括在内。而hasStableIds,getItemViewType与getViewTypeCount只考虑了其内嵌的ListAdapter


Adapter的使用方法主要有两种:
1使用具体的Adapter,比如ArrayAdapter,SimpleAdapter,SimpleCursorAdpater,HeadViewListAdapter,SimpleExpandableListAdapter,
2使用自定义的Adapter让自定Adapter继承BaseAdapter或BaseExpandableAdapter.
0 0
原创粉丝点击