天天看點

進階控件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;
				}
		}	);