天天看点

高级控件AdapterView(二):通过Observer实现数据、视图同步刷新

    多条目控件AdapterView是仿MVC设计模式进行设计的,即AdapterView与数据是分离的,AdapterView并不直接操作数据,而是利用中间件适配器Adapter,实际上,完整的AdapterView显示是三个逻辑部分:AdapterView+Adapter+Data,Adapter相当于MVC中的控制器C,AdapterView相当于MVC中的视图V,顺便说一句,Android程序设计,也是利用MVC控制模式。既然视图与数据是分离的,那么当数据有更新时,视图显然无法自动更新,Adapter必须实时监控数据变化并刷新视图,这里用到的是Observer(观察者模式)。

一、什么是观察者模式

    所谓观察者模式,听起来很复杂高大上,实际上响应过程就如同一个点击事件处理过程(实际上也是按照观察者模式处理,但形式上有所改变),比如一个Button和一个监听器,Button就是被观察者(T extends Observable),监听器就是观察者(T extends DataSetObservable),当Button状态发生变化时,被观察者“贱贱”的向观察者发出通知,告知其状态改变,然后观察者做出相应的处理。观察者模式比较容易混淆的就是这个“贱贱”,并不是观察者主动观察得知被观察者发生变化,而是被动的等待再作相应的处理。

    为实现观察者模式,Android设计两个抽象类,分别是DataSetObserver及Observable(可观察的,即被观察者),从英文字面上就直观看出谁是观察者,谁是被观察者。下面对此详细介绍。

二、DataSetObserver(观察者)抽象类与Observable(被观察者)抽象类继承体系

1、DataSetObserver

DataSetObserver类的API这样描述:DataSetObserver(观察者)通常设置给Cursor或Adapter,如果数据发生变化则回调该类方法。DataSetObserver类无法单独调用,必须配合DataSetObservable类使用。该类的实例即是Observer,观察者的刷新视图行为是由onChanged以及onInvalidated方法决定的,通常在AdapterView控件,如ListView中继承该类,并通过重写这两个方法,实现针对性的视图刷新。

public abstract class DataSetObserver {
    //当被观察的数据发生变化,回调该函数
    public void onChanged()
    //当被观察的数据失效,回调该函数
    public void onInvalidated()
           
<span style="font-family: FangSong_GB2312; font-weight: bold; background-color: rgb(255, 255, 255);">2、Observable(被观察者)抽象类继承体系</span>
           

2.1、Observable基类

这是一个抽象基类,该类主要提供对Observer进行注册或解除注册到ArrayList数据集(Observer必须绑定到某个数据集才能监测)。 源码如下:

<pre name="code" class="java">public abstract class Observable<T> {
    //观察者列表,不可重复
    protected final ArrayList<T> mObservers = new ArrayList<T>();

    //注册观察者observer,即将observer添加到mObservers中
    public void registerObserver(T observer) {

    //解除观察者observer,即将observer从mObservers中移除
    public void unregisterObserver(T observer) {
        
    //解除所有的observer,即清空mObservers列表
    public void unregisterAll() {
}
           

2.2、DataSetObservable类

这是一个可实例化类,通常在对ArrayList数据进行更新后,主动调用notifyChanged()函数,若ArrayList数据失效,比如给视图绑定新的数据源等等,调用notifyInvalidated,源码如下:

public class DataSetObservable extends Observable<DataSetObserver> {

    //当数据集发生变化,回调数据集每一个观察者DataSetObserver的onChanged函数,通常视图的刷新在onChanged中完成
    public void notifyChanged() {
        synchronized(mObservers) {
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onChanged();
            }
        }
    }

    //当数据集失效(比如关闭),回调数据集每一个观察者DataSetObserver的onInvalidated()函数
    public void notifyInvalidated() {
        synchronized (mObservers) {
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onInvalidated();
            }
        }
    }
}
           

3、小结

DataSetObserver与Observable之间的逻辑关系可以用一张图表示,很明显,被观察通过notifyChanged()与notifyInvalidated()方法通知并调用Observer方法。

高级控件AdapterView(二):通过Observer实现数据、视图同步刷新

三、AdapterView控件与Adapter利用观察者模式刷新界面

1、适配器Adapter中与DataSetObserver有关的方法

1.1、adapter中相关源码

//注册一个observer,观察Adapter关联的数据集是否发生变化(如增加、删除等等)
void registerDataSetObserver(DataSetObserver observer);
//取消注册observer
void unregisterDataSetObserver(DataSetObserver observer);
           

1.2、BaseAdapter中相关源码

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {   
    //获取DataSetObservable()对象
    private final DataSetObservable mDataSetObservable = new DataSetObservable();

    //注册一个DataSetObserver
    public void registerDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.registerObserver(observer);
    }
    //取消一个DataSetObserver
    public void unregisterDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.unregisterObserver(observer);
    }
    
    //通知观察数据集的observers,数据已经发生变化,并且执行与数据相关的视图刷新操作
     public void notifyDataSetChanged() {
        mDataSetObservable.notifyChanged();
    }


    //通知观察数据集的observers,数据已经失效,一旦调用该函数,表明Adapter也已经失效。
    public void notifyDataSetInvalidated() {
        mDataSetObservable.notifyInvalidated();
    }
           

1.3、小结

    Adapter主要是注册一个Observer,然后在数据有变化时,主动调用notifyDataSetChanged()或notifyInvalidated(),而决定数据在出现变化后的行为(主要是onchanged()方法决定),一般需要在AdapterView中进行个性定制。

2、AdapterView中与DataSetObserver有关的方法

2.1、AdapterView

AdapterView中写了一个继承DataSetObserver的内部类AdapterDataSetObserver,主要是重新onChanged(),onInvalidated()方法,以执行AdapterView的刷新视图操作

class AdapterDataSetObserver extends DataSetObserver {       
        @Override
        public void onChanged() {
            ...
            }

        @Override
        public void onInvalidated() {
            ...
            }
    }
           

2.2、ListView

ListView中,主要是setAdapter()方法将适配器Adapter绑定到ListView,并设置Adapter的观察者为AdapterDataSetObserver。

public void setAdapter(ListAdapter adapter) {
        mAdapter = adapter;
        
        if (mAdapter != null) {
            //注册Adapter的观察者AdapterDataSetObserver
            mDataSetObserver = new AdapterDataSetObserver();
            mAdapter.registerDataSetObserver(mDataSetObserver);
            mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());
    }
           

2.3、小结

AdapterView控件主要定义Observer行为,即Onchanged()和onInvalidated()方法,然后通过Adapter的注册方法registerDataSetObserver()对定制的mDataSetObserver注册。

3、小结

AdapterView中用一个内部类实现了Observer,Adapter中定义了Observable方法,所以AdapterView是观察者,Adapter是被观察者。

高级控件AdapterView(二):通过Observer实现数据、视图同步刷新

AdpaterView与Adapter是通过方法SetAdapter结合起来的,参考ListView的SetAdapter方法(如下),

@Override
    public void setAdapter(ListAdapter adapter) {
        if (mAdapter != null && mDataSetObserver != null) {
            mAdapter.unregisterDataSetObserver(mDataSetObserver);
        }

        resetList();
        mRecycler.clear();

        if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
            mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
        } else {
            mAdapter = adapter;
        }

        mOldSelectedPosition = INVALID_POSITION;
        mOldSelectedRowId = INVALID_ROW_ID;

        // AbsListView#setAdapter will update choice mode states.
        super.setAdapter(adapter);

        if (mAdapter != null) {
            mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
            mOldItemCount = mItemCount;
            mItemCount = mAdapter.getCount();
            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);

            if (mItemCount == 0) {
                // Nothing selected
                checkSelectionChanged();
            }
        } else {
            mAreAllItemsSelectable = true;
            checkFocus();
            // Nothing selected
            checkSelectionChanged();
        }

        requestLayout();
    }
           
mDataSetObserver = new AdapterDataSetObserver();
            mAdapter.registerDataSetObserver(mDataSetObserver);
           

这两行代码将AdapterView(观察者)注册到Adapter(被观察者)中,接下来如果Adapter中的数据发生变化,主动调用 adapter.notifyDataSetChanged()即可实现整个观察者流程。

四、简单Demo

实现长按长按ListView并删除所在项,主要代码如下:

listview.setOnItemLongClickListener(new OnItemLongClickListener() {
				@Override
				public boolean onItemLongClick(AdapterView<?> parent,
						View view, int position, long id) {
					
					//长按删除并动态更新listview
					datas.remove(position);					
					myAdapter.notifyDataSetChanged();
					return false;
				}
		}	);