天天看點

Re:Android ListView觀察者的使用

第二期項目結束,繼續自己的部落格生涯。上周六給自己立過一個flag,對自己的承諾一定兌現,我就是注定成為大牛的偶豆扣。

Android開發中肯定少不了ListView,它總是配合BaseAdapter一起。每次我們更新資料的時候就會調用baseAdapter.notifyDataSetChanged()方法。實際其中就是用到了觀察者模式。那今天我們就來看看ListView中是如何使用觀察者模式的。

讓我們先看看BaseAdapter的源碼,不重要的代碼忽略。但是其中有個hasStableIds()方法預設傳回false,後面要用到。

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
    //一個被觀察者執行個體
    private final DataSetObservable mDataSetObservable = new DataSetObservable();
    
    //注冊觀察者
    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執行個體,他繼承自Observable,讓我們來看看它們怎麼寫的

public abstract class Observable<T> {
    /**
     * 一個泛型T的觀察者集合,
     */
    protected final ArrayList<T> mObservers = new ArrayList<T>();

    /**
     * 添加觀察者到集合
     */
    public void registerObserver(T observer) {
        synchronized(mObservers) {//對象同步鎖
            mObservers.add(observer);
        }
    }

    /**
     * 移除觀察者
     */
    public void unregisterObserver(T observer) {
        synchronized(mObservers) {
            mObservers.remove(index);
        }
    }

    /**
     * 移除所有觀察者
     */
    public void unregisterAll() {
        synchronized(mObservers) {
            mObservers.clear();
        }
    }
}
           
public class DataSetObservable extends Observable<DataSetObserver> {
    /**
     * 通知所有觀察者資料改變
     */
    public void notifyChanged() {
        synchronized(mObservers) {//mObservers繼承自Observable<T>
            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();
            }
        }
    }
}
           

典型的被觀察者的寫法,其實就是用一個集合來存儲觀察者,然後對這個集合進行操作。

listView.setAdapter(baseAdapter);//給listview設定擴充卡
           

接下來我們看看setAdapter的方法到底幹了什麼

@Override
    public void setAdapter(ListAdapter adapter) {
        //對觀察者模式不重要的代碼省略...
        if (mAdapter != null) {
            ...
            mDataSetObserver = new AdapterDataSetObserver();
            mAdapter.registerDataSetObserver(mDataSetObserver);
	    ...
        } else {...}
        requestLayout();//重新布局
    }
           

還記得BaseAdapter中的registerDataSetObserver方法嗎?接下來看看AdapterDataSetObserver這個觀察者是怎麼寫的

class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
        @Override
        public void onChanged() {
            super.onChanged();
            if (mFastScroll != null) {
                mFastScroll.onSectionsChanged();
            }
        }

        @Override
        public void onInvalidated() {
            super.onInvalidated();
            if (mFastScroll != null) {
                mFastScroll.onSectionsChanged();
            }
        }
    }
           

AdapterDataSetObserver這個類隻是單純的設定了一下ListView的快速滾動,那我們去他的父類看看。它是AdapterView的一個内部類。

class AdapterDataSetObserver extends DataSetObserver {

        @Override
        public void onChanged() {
            mDataChanged = true;//改變狀态
            mOldItemCount = mItemCount;//記錄下上次的數量
            mItemCount = getAdapter().getCount();//記錄下這次的數量
		//還記得hasStableIds預設傳回什麼嗎?
            if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
                    && mOldItemCount == 0 && mItemCount > 0) {
                ....
            } else {
                rememberSyncState();//記住各種狀态和資料(頂部偏移量等)
            }
            checkFocus();//檢測焦點
            requestLayout();//view的方法,重新布局
        }

        @Override
        public void onInvalidated() {
            ...//重置了所有的位置狀态
        }
    }
           

源碼就看到這裡,基本上邏輯已經搞清楚了。setAdapter的時候會注冊一個觀察者。notifyDataSetChanged的時候會通知觀測者,可以讓AdapterView調用相應的邏輯,最後重新整理資料。