Android中ListView的訂閱模式

我們每次在使用ListView的時(shí)候,如果數(shù)據(jù)改變了,都會(huì)調(diào)用adapter的notifyDataSetChanged方法,那么我們來看看為什么調(diào)用該方法就可以刷新數(shù)據(jù)了。
首選我們要了解訂閱模式,因?yàn)閚otifyDataSetChanged就是使用該模式。先看下UML建模圖吧


我們先介紹下各個(gè)類:

  • Observer:抽象觀察者
  • ConcreteObserverA:觀察者對(duì)象實(shí)體
  • Observable:抽象被觀察者
  • ConcreteObservers:被觀察者實(shí)體

這是一個(gè)最簡(jiǎn)單的觀察者模式,目標(biāo)對(duì)象能夠添加和刪除觀察者,當(dāng)自己某種狀態(tài)或者行為發(fā)生改變時(shí),可通過notifyObservers通知注冊(cè)的觀察者進(jìn)行更新操作。

了解了訂閱模式,那我們看看ListView與Adapter是怎么實(shí)現(xiàn)的。先從我們最常調(diào)用的notifyDataSetChanged方法切入吧

    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();
        }

    }

BaseAdapter中有一個(gè)成員變量mDataSetObservable,我們的注冊(cè),注銷都是調(diào)用它的方法,那么我們看看它是什么

    public class DataSetObservable extends Observable<DataSetObserver> {
      
        public void notifyChanged() {
            synchronized(mObservers) {
                for (int i = mObservers.size() - 1; i >= 0; i--) {
                    mObservers.get(i).onChanged();
                }
            }
        }
    }

這里只有一個(gè)簡(jiǎn)單的方法notifyChanged,重要的東西應(yīng)該在它的父類。DataSetObservable是繼承Observable的,那我們看看Observable

    public abstract class Observable<T> {
        protected final ArrayList<T> mObservers = new ArrayList<T>();

        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);
            }
        }
    }

這時(shí)候我們就能看到被觀察者的影子了,有一個(gè)集合,來存儲(chǔ)觀察者的引用,等到數(shù)據(jù)變化時(shí)再遍歷通知,只是沒有notify方法,因?yàn)樗淖宇愄峁┝嗽摲椒ā?br> 所以DataSetObservable就是我們的被觀察者,它可以添加、刪除、通知觀察者。被觀察者我們找到了,那么觀察者有是誰呢?剛才有沒有注意到BaseAdapter
中有兩個(gè)方法,將一個(gè)DataSetObserver從我們的被觀察者DataSetObservable中注冊(cè)、注銷。那么DataSetObservable肯定是我們要找的
觀察者了,看看誰調(diào)用了這兩個(gè)方法;我們看看listView的SetAdapter,這里調(diào)用了adapter的注冊(cè)與注銷方法(把觀察者綁定到被觀察者上)

    public void setAdapter(ListAdapter adapter) {
        if (mAdapter != null && mDataSetObserver != null) {
            mAdapter.unregisterDataSetObserver(mDataSetObserver);//注銷
        }
            ...
        if (mAdapter != null) {
            ...
            mDataSetObserver = new AdapterDataSetObserver();
            mAdapter.registerDataSetObserver(mDataSetObserver);//重新注冊(cè)
            ...
        } else {
            ...
        }

        requestLayout();
    }

ListView先將之前注冊(cè)的DataSetObserver注銷掉,然后再new一個(gè)新的AdapterDataSetObserver注冊(cè)到我們的被觀察者上
這樣我們就找到了觀察者AdapterDataSetObserver,其實(shí)AdapterDataSetObserver是AbsListView中的內(nèi)部類

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

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

從AdapterDataSetObserver中我們沒有找到更新UI的跡象,那么我們看看它的父類

     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();
        }

        @Override
        public void onInvalidated() {
            mDataChanged = true;

            if (AdapterView.this.getAdapter().hasStableIds()) {
                mInstanceState = AdapterView.this.onSaveInstanceState();
            }

            mOldItemCount = mItemCount;
            mItemCount = 0;
            mSelectedPosition = INVALID_POSITION;
            mSelectedRowId = INVALID_ROW_ID;
            mNextSelectedPosition = INVALID_POSITION;
            mNextSelectedRowId = INVALID_ROW_ID;
            mNeedSync = false;

            checkFocus();
            requestLayout();
        }

        public void clearSavedState() {
            mInstanceState = null;
        }
    }

這里我們看到在listview中調(diào)用了requestLayout(),應(yīng)該就是這里更新的。先保留懸念,我們繼續(xù)理清類的關(guān)系

AdapterDataSetObserver最終是繼承自DataSetObserver

    
    public abstract class DataSetObserver {
        public void onChanged() {
            // Do nothing
        }
    
        public void onInvalidated() {
            // Do nothing
        }
    }

DataSetObserver跟我們UML圖中的Observer很像?到這里我們就找到了訂閱模式中所有的元素,讓我來列舉下

  • DataSetObserver:抽象觀察者
  • AdapterDataSetObserver:觀察者實(shí)體
  • Observable:抽象被觀察者
  • DataSetObservable:被觀察者實(shí)體

既然找到了訂閱模式中這些元素,那么我們分析下adapter與listView是如何使用訂閱模式的;
當(dāng)adapter中的數(shù)據(jù)發(fā)生改變時(shí),adapter會(huì)調(diào)用notifyDataSetChanged方法,這個(gè)方法會(huì)調(diào)用被觀察者DataSetObservable
的notifyChanged方法,這個(gè)方法又會(huì)遍歷調(diào)用所有觀察者AdapterDataSetObserver的onChanged方法。在AdapterDataSetObserver
父類onChanged方法中會(huì)重新計(jì)算adapter的數(shù)據(jù)個(gè)數(shù),并調(diào)用ListView的requestLayout方法重新布局,更新用戶界面。
至此就解釋了為什么adapter.notifyDataSetChanged方法會(huì)更新界面

ListView訂閱模式UML(關(guān)系指向不準(zhǔn),僅供參考)


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,936評(píng)論 25 709
  • 簡(jiǎn)介 在Android開發(fā)中ListView是比較常用的組件。 以列表的形式展示具體內(nèi)容。 并且能夠根據(jù)數(shù)據(jù)的長(zhǎng)度...
    上善若水Ryder閱讀 7,079評(píng)論 2 5
  • 前言我從去年開始使用 RxJava ,到現(xiàn)在一年多了。今年加入了 Flipboard 后,看到 Flipboard...
    占導(dǎo)zqq閱讀 9,304評(píng)論 6 151
  • 我們從呱呱墜地到哪個(gè)家庭這一刻開始就決定了我們的江湖地位,我們將得到的是父親的父親,母親的母親世代傳承的...
    創(chuàng)造者阿滿閱讀 654評(píng)論 0 2
  • 最近常??吹健凹热凰苓x擇在你一無所有的時(shí)候愛著你,這還不足夠嗎?”,突然莫名地想笑。 女孩子總是喜歡拿“他”一無...
    我若飛閱讀 368評(píng)論 0 0

友情鏈接更多精彩內(nèi)容