http://blog.csdn.net/chunqiuwei/article/details/39934169
首先说说Adapter具体的类的继承关系,如下图
Adapte为接口它的实现类的对象作为AdapterView和View的桥梁,Adapter是装载了View(比如ListView和girdView要显示的数据)。相关View要显示的数据完全与View解耦。View要显示的数据从Adapter里面获取并展现出来;Adapter负责把真实的数据是配成一个个View(每一条数据对应一个View)让GirdView等类似的组件来显示这些是配好的一个个View,。也就是说View要显示什么数据取决于Adapter,View的变化(比如ListView删除一个或者增加一个Item)也取决于Adapter里面的数据的变化。这也就说明当Adapter的里面的数据如果发生变化的时候相应的View(如ListView)也得发生对应的变化。当Adapter里面的数据发生变化的时候必须通知View也发生变化,继而重绘View从而展示数据变化过的View.这是典型的观察者模式的应用。
Adapter是被观察的主题,被观察者监视,如果Adapter这个Subject状态发生了变化,主题会告知观察者,观察者通过回调一个函数得到通知,通知关联对象做出相应的更新。
既然是被观察者,用面向对象的思维来说,该主题必须持有观察者的对象用来让观察者观察,所以主题在观察者模式中也具备了注册和销毁观察者对象的责任
由此可以推出Adapter有以下两个主要的职责:
1) 把源数据适配成一个个View
2) 当数据发生变化的时候发送通知(向观察者发送通知,然后由观察者做出相应的响应,观察者模式),让相关组件(GirdView)做出在页面展现上的修改。
Adapter的部分代码如下:
- public interface Adapter {
-
-
-
-
- void registerDataSetObserver(DataSetObserver observer);
-
-
-
-
- void unregisterDataSetObserver(DataSetObserver observer);
-
-
-
-
-
- View getView(int position, View convertView, ViewGroup parent);
- }
可以看到adapter这个父接口定义了注册观察者的方法,下面就看看观察者的都做了哪些事情:这里的观察者是DataSetObserver抽象类的扩展类。该抽象类提供了两个方法:
- public abstract class DataSetObserver {
-
- public void onChanged() {
-
- }
- public void onInvalidated() {
-
- }
- }
那么,这个观察者是怎么和Adapter进行关联的呢?其实观察者对象是放在一个ArrayList集合里面去的。该集合封装在Observable这个抽象类。看看这个类的源码就可以明白:
- public abstract class Observable {
-
- protected final ArrayList mObservers = new ArrayList();
-
- public void registerObserver(T observer) {
- if (observer == null) {
- throw new IllegalArgumentException("The observer is null.");
- }
- synchronized(mObservers) {
- if (mObservers.contains(observer)) {
- throw new IllegalStateException("Observer " + observer + " is already registered.");
- }
- mObservers.add(observer);
- }
- }
-
- public void unregisterObserver(T observer) {
- if (observer == null) {
- throw new IllegalArgumentException("The observer is null.");
- }
- synchronized(mObservers) {
- int index = mObservers.indexOf(observer);
- if (index == -1) {
- throw new IllegalStateException("Observer " + observer + " was not registered.");
- }
- mObservers.remove(index);
- }
- }
-
- public void unregisterAll() {
- synchronized(mObservers) {
- mObservers.clear();
- }
- }
- }
通过源码可以知道Observervable的主要职责是添加观察者以及删除已经添加过的观察者!该抽象类还有一个子类DataSetObservable:该类在继承父类功能的基础上又提供了两个方法,这两个方法的作用就是向一系列观察者发送通知,以便让该类包含的
所有
观察者执行onChanged()或者onInvalidated()来执行特定的行为。源码如下:
- public class DataSetObservable extends Observable {
-
- public void notifyChanged() {
- synchronized(mObservers) {
- for (int i = mObservers.size() - 1; i >= 0; i--) {
- mObservers.get(i).onChanged();
- }
- }
- }
- public void notifyInvalidated() {
- synchronized (mObservers) {
- for (int i = mObservers.size() - 1; i >= 0; i--) {
- mObservers.get(i).onInvalidated();
- }
- }
- }
- }
通过上面的说明我们知道观察者对象的工作是由DataSetObservable来间接发出告知并执行观察者自己的onChange方法的。读到这可以发现,现在观察者还是没有和相应Adapter进行关联以及在数据发生变化的时候Adapter是怎么发送通知的,下面就以BaseAdapter进行说明,其部分源码如下:
- public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
-
- private final DataSetObservable mDataSetObservable = new DataSetObservable();
-
- public boolean hasStableIds() {
- return false;
- }
-
- public void registerDataSetObserver(DataSetObserver observer) {
- mDataSetObservable.registerObserver(observer);
- }
-
- public void unregisterDataSetObserver(DataSetObserver observer) {
- mDataSetObservable.unregisterObserver(observer);
- }
-
-
- public void notifyDataSetChanged() {
- mDataSetObservable.notifyChanged();
- }
- public void notifyDataSetInvalidated() {
- mDataSetObservable.notifyInvalidated();
- }
很显然,BaseAdapter包含了一个DataSetObservable类型的引用mDataSetObservable,通过前面的说明可知该引用所代表的对象里面封装了若干个观察者,具体注册观察者的方法就是BaseAdapter的 registerDataSetObserver方法。通过读该源码发现该类提供了一个notifyDataSetChanged()方法,当数据源或者Adapter里面的数据发生变化的时候要手动调用此方法来发起通知!!!!到此为止就找到了是什么方法来为观察者发送通知的,正是notifyDataSetChanged()方法。
以上只是沿着程序的脉络来说明当数据发生变化的时候是怎么通知观察者的。具体观察者都做了的onChange方法都做了什么并没有说明,这些由观察者不同的子类来实现的,这里先不做讨论。下面说说怎样让adapter里面的数据在view里面显示出来。
上面已经说明了Adapter的一个职责之一就是把数据源组织成一个个view并返回一个view的对象,具体怎么组织的是由Adapter的方法getView来实现的,该方法实在onMeasure()方法执行的时候被调用的,再具体的是在obtainView方法中调用。搞android开发的程序员都少不了和这个方法打交道,这里就不做赘述。
当把数据放入Adapter之后,通过GirdView(或者ListView这篇文章以GirdView为例)的setAdapter()方法把数据最终展现出来。或许细心的读者会发现在自己开发的过程中并没有在自己的Adapter添加观察者啊?只是简单的setAdapter()之后就什么也不用管了?其实不然,看看setAdapter都做些了什么就会知道
- public void setAdapter(ListAdapter adapter) {
-
- if (mAdapter != null && mDataSetObserver != null) {
- mAdapter.unregisterDataSetObserver(mDataSetObserver);
- }
-
-
- resetList();
- mRecycler.clear();
-
- mAdapter = adapter;
-
-
- mOldSelectedPosition = INVALID_POSITION;
-
- mOldSelectedRowId = INVALID_ROW_ID;
-
-
- super.setAdapter(adapter);
-
- if (mAdapter != null) {
-
- mOldItemCount = mItemCount;
-
- mItemCount = mAdapter.getCount();
-
- mDataChanged = true;
-
-
- checkFocus();
-
-
- mDataSetObserver = new AdapterDataSetObserver();
- mAdapter.registerDataSetObserver(mDataSetObserver);
-
-
- mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());
-
-
- int position;
-
-
- if (mStackFromBottom) {
-
- position = lookForSelectablePosition(mItemCount - 1, false);
- } else {
- position = lookForSelectablePosition(0, true);
- }
-
- setSelectedPositionInt(position);
-
- setNextSelectedPositionInt(position);
-
- checkSelectionChanged();
- } else {
- checkFocus();
-
- checkSelectionChanged();
- }
-
- requestLayout();
- }
通过上面的源码可以发现,每次调用setAdapter的时候都会注册AdapterDataSetObserver对象(上面代码33行),这样就可以在adapter发生变化的时候进行响应的处理。
那么看看这个具体的观察者到底都做了些什么:
- class AdapterDataSetObserver extends AdapterView.AdapterDataSetObserver {
- @Override
- public void onChanged() {
-
- super.onChanged();
- if (mFastScroller != null) {
- mFastScroller.onSectionsChanged();
- }
- }
-
- @Override
- public void onInvalidated() {
- super.onInvalidated();
- if (mFastScroller != null) {
- mFastScroller.onSectionsChanged();
- }
- }
- }
看看onChange()方法里面调用了父类的方法onChange()方法,主要的响应数据变化的逻辑就在父类的onChange()方法里面,先买看看父类的该方法的具体实现:
- class AdapterDataSetObserver extends DataSetObserver {
-
- private Parcelable mInstanceState = null;
-
- @Override
- public void onChanged() {
- mDataChanged = true;
- mOldItemCount = mItemCount;
- mItemCount = getAdapter().getCount();
- if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
- && mOldItemCount == 0 && mItemCount > 0) {
- AdapterView.this.onRestoreInstanceState(mInstanceState);
- mInstanceState = null;
- } else {
- rememberSyncState();
- }
- checkFocus();
- requestLayout();
- }
- }
最终走到会执行requestLayout()来重新布局页面达到响应数据变化的目的。至此,已经完成了对adapter数据变化来改变当前View的变化的说明。
下面说说使用案例:
有如下效果图
效果图1
注意上图的12个数据时保存在GirdView里面的,当我点击编辑的时候页面变化成如下图所示的情况:
效果图2
下面具体说说这种效果的具体实现。
1)Adapter的代码如下:
- public class CollectionItemAdapter extends BaseAdapter {
-
- private Vector collections;
- public static final int EDIT_STATUS = 0;
- public static final int UNEDIT_STATUS = -1;
- private int delePosition = UNEDIT_STATUS;
-
- public int getDelePosition() {
- return delePosition;
- }
-
- public void setDelePosition(int delePosition) {
- this.delePosition = delePosition;
- }
-
- public Vector getCollections() {
- return collections;
- }
-
- public void setCollections(Vector collections) {
- this.collections = collections;
- }
-
- @Override
- public int getCount() {
- if(collections != null){
- return collections.size();
- }
-
- return 0;
- }
-
- @Override
- public Collection getItem(int position) {
- if(collections !=null){
- return collections.get(position);
- }
-
- return null;
- }
-
- @Override
- public long getItemId(int position) {
-
- return position;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- ViewItem viewItem = null;
- if(convertView==null){
- viewItem = new ViewItem();
- convertView = App.getLayoutInflater().inflate(R.layout.collection_item, null);
- viewItem.img = (ImageView)convertView.findViewById(R.id.item_img);
- viewItem.name = (TextView)convertView.findViewById(R.id.item_name);
- viewItem.editBg = (ImageView)convertView.findViewById(R.id.collection_edit_bg);
- convertView.setTag(viewItem);
- }else {
- viewItem = (ViewItem) convertView.getTag();
- }
- viewItem.img.setImageResource(R.drawable.no_pic_small);
- Collection collection = this.getItem(position);
- viewItem.name.setText(collection.getName());
- ImageLoader.getInstance().displayImage(collection.getPicUrl(), viewItem.img, App.getOptionsSmall());
- viewItem.img.setVisibility(View.VISIBLE);
- if(delePosition==EDIT_STATUS){
-
- viewItem.editBg.setVisibility(View.VISIBLE);
-
- }else{
-
- viewItem.editBg.setVisibility(View.GONE);
- }
- return convertView;
- }
-
- private static class ViewItem {
- ImageView img;
- TextView name;
- ImageView editBg;
- public String toString(){
- return name.getText().toString();
- }
- }
-
- }
注意该Adapter有一个delePosition 用来标志是否处于编辑状态,同时在getView方法里面对该字段进行了判断:当处于非编辑状态的时候运行结果为效果图1,当点击编辑的时候delePoition为编辑状态,此时的页面效果为效果图2.也就是说如果想是实现这个功能只需要改变Adapter对象的这个字段进行设置然后调用notifyDataSetChanged()方法通知观察者就行了,所以当点击编辑的时候的响应事件为:
-
- collectionAdapter.setDelePosition(CollectionItemAdapter.UNEDIT_STATUS);
-
- collectionAdapter.notifyDataSetChanged();
2)collection_item.xml配置文件如下
- xml version="1.0" encoding="utf-8"?>
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent" >
-
- <RelativeLayout
- android:layout_width="@dimen/wiki_item_w"
- android:layout_height="@dimen/wiki_item_h" >
-
- <RelativeLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:padding="@dimen/pading_17" >
- <ImageView
- android:id="@+id/item_img"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:focusable="false"
- android:src="@drawable/test" />
- RelativeLayout>
-
-
- <RelativeLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:padding="@dimen/pading_15" >
-
- <ImageView
- android:id="@+id/item_bg_img"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:focusable="false"
- android:src="@drawable/item_txt_bg" />
- RelativeLayout>
-
- <ImageView
- android:id="@+id/collection_focus"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@drawable/item_focus_selecter" />
-
-
- <RelativeLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:padding="@dimen/pading_19" >
- <ImageView
- android:id="@+id/collection_edit_bg"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:focusable="false"
- android:background="@drawable/record_collection_edit_selecter"
- android:visibility="gone" />
- RelativeLayout>
-
- <tv.huan.epg.vod.qclt.ui.widget.ScrollForeverTextView
- android:id="@+id/item_name"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_marginBottom="@dimen/collection_item_name_margin_left"
- android:layout_marginLeft="@dimen/collection_item_name_margin_right"
- android:layout_marginRight="@dimen/collection_item_name_margin_bottom"
- android:ellipsize="marquee"
- android:gravity="center"
- android:marqueeRepeatLimit="marquee_forever"
- android:singleLine="true"
- android:textColor="@drawable/font_color_selector"
- android:textSize="@dimen/font_20" />
- RelativeLayout>
-
- RelativeLayout>
另外在编辑的时候点击某一个item可以删除,也就是删除Adapter类里面的Vector里面的数据,删除过后同样调用notifyDatasetChanged()方法进行通知即可
略作总结:因为数据都在Adapter里面,所以说Adapter是观察者要观察的主题(SubJect)或者说被观察的对象,Adapter负责注册和销毁观察者,当数据发生变化的时候通知观察者更新ListView.观察者的作用就是当一个对象的状态发生变化的时候,能够自动通知其他对象,以便其他对象做出对应的更新操作。
其中在观察者模式中,Subject是主动变化,而观察者只能根据主题的变化而做出相应的变更。