Android 中的監聽器模式與觀察者模式


1、   觀察者模式與監聽器機制

1.1 觀察者模式

1.2 監聽器(Listener)機制

代碼的基本框架:

* 被監控着

 

package com.wonders.group;

import java.util.Collection;

 

public class ModelTie {

    private Collection<Object> dataSet;

 

    public interface DataSetSupervioer {

        public void onChange();

    }

 

    private DataSetSupervioer dataSetChangeListener;

 

    public void setDataSetChangeListener(DataSetSupervioer dataSetChangeListener) {

        this.dataSetChangeListener = dataSetChangeListener;

    }

 

    public void notifyDataSetChange() {

        if (null != dataSetChangeListener) {

            dataSetChangeListener.onChange();

        }

    }

   

    public Collection<Object> getDataSet() {

        return dataSet;

    }

 

    public ModelTie setDataSet(Collection<Object> dataSet) {

        this.dataSet = dataSet;

        this.notifyDataSetChange(); // 數據設置完畢要通知監聽器進行更新操作    

        return this;

    }

}

 

* 監控者

package com.wonders.group;

import java.util.Collection;

import java.util.Iterator;

 

import com.wonders.group.ModelTie.DataSetSupervioer;

 

public class PresentationTie {

    private ModelTie model;

 

    public PresentationTie() {

        super();

       

        // 添加監聽器

        model.setDataSetChangeListener(new DataSetSupervioer() {

            public void onChange() {

                // 填寫一些前置操作,如更新數據

                DisplayModel(); // 重新繪制

                // 填寫一些后置操作,如更新狀態

            }

        });

    }

 

    public void DisplayModel() {

        Collection<Object> collection = model.getDataSet();

        if (collection != null) {

            for (Iterator iterator = collection.iterator(); iterator.hasNext();) {

                System.out.println(((Object) iterator.next()).toString());

                // 其他等等操作

            }

        }

    }

 

    public ModelTie getModel() {

        return model;

    }

 

    public void setModel(ModelTie model) {

        this.model = model;

    }

}

 

2、   ArrayAdapter的觀察者實現機制

以下僅羅列關鍵代碼:

public class ArrayAdapter<T> extends BaseAdapter implements Filterable {

 

private boolean mNotifyOnChange = true;

 

    /**

     * Adds the specified object at the end of the array.

     */

    public void add(T object) {

        if (mOriginalValues != null) {

            synchronized (mLock) {

                mOriginalValues.add(object);

                if (mNotifyOnChange) notifyDataSetChanged();

            }

        } else {

            mObjects.add(object);

            if (mNotifyOnChange) notifyDataSetChanged();

        }

    }

 

    /**

     * Inserts the specified object at the specified index in the array.

     */

    public void insert(T object, int index) {

        if (mOriginalValues != null) {

            synchronized (mLock) {

                mOriginalValues.add(index, object);

                if (mNotifyOnChange) notifyDataSetChanged();

            }

        } else {

            mObjects.add(index, object);

            if (mNotifyOnChange) notifyDataSetChanged();

        }

    }

 

    /**

     * Removes the specified object from the array.

     */

    public void remove(T object) {

        if (mOriginalValues != null) {

            synchronized (mLock) {

                mOriginalValues.remove(object);

            }

        } else {

            mObjects.remove(object);

        }

        if (mNotifyOnChange) notifyDataSetChanged();

    }

 

    /**

     * Remove all elements from the list.

     */

    public void clear() {

        if (mOriginalValues != null) {

            synchronized (mLock) {

                mOriginalValues.clear();

            }

        } else {

            mObjects.clear();

        }

        if (mNotifyOnChange) notifyDataSetChanged();

    }

 

    /**

     * Sorts the content of this adapter using the specified comparator.

     */

    public void sort(Comparator<? super T> comparator) {

        Collections.sort(mObjects, comparator);

        if (mNotifyOnChange) notifyDataSetChanged();

    }

 

    @Override

    public void notifyDataSetChanged() {

        super.notifyDataSetChanged();   // 關鍵代碼,這個notifyDataSetChanged()是從父類BaseAdapter繼承過來的,所以看看在父類中它干了些什么

        mNotifyOnChange = true;

}

 

}

 

/**

 * Common base class of common implementation for an {@link Adapter} that can be

 * used in both {@link ListView} (by implementing the specialized

 * {@link ListAdapter} interface} and {@link Spinner} (by implementing the

 * specialized {@link SpinnerAdapter} interface.

 */

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {

    private final DataSetObservable mDataSetObservable = new DataSetObservable();

 

    public void registerDataSetObserver(DataSetObserver observer) {

這對方法用來注冊或注銷

觀察ArrayAdapter的觀察者的

 

        mDataSetObservable.registerObserver(observer);

    }

 

    public void unregisterDataSetObserver(DataSetObserver observer) {

        mDataSetObservable.unregisterObserver(observer);

    }

 

    /**

     * Notifies the attached View that the underlying data has been changed

     * and it should refresh itself.

     */

    public void notifyDataSetChanged() {

        mDataSetObservable.notifyChanged();  // 關鍵代碼:說明調的是成員變量mDataSetObservable的方法,所以進入DataSetObservable看看具體是如何操作的

    }

 

    public void notifyDataSetInvalidated() {

        mDataSetObservable.notifyInvalidated();

    }

}

 

 

package android.database;

 

/**

 * A specialization of Observable for DataSetObserver that provides methods for

 * invoking the various callback methods of DataSetObserver.

 */

public class DataSetObservable extends Observable<DataSetObserver> {

    /**

     * Invokes onChanged on each observer. Called when the data set being observed has

     * changed, and which when read contains the new state of the data.

     */

    public void notifyChanged() {

        synchronized(mObservers) {

            for (DataSetObserver observer : mObservers) { // 這里的mObservers是哪來的呢?繼續追蹤,但首先可以判斷是來自Observable<DataSetObserver>的。進入看看

                observer.onChanged();

            }

        }

    }

 

    /**

     * Invokes onInvalidated on each observer. Called when the data set being monitored

     * has changed such that it is no longer valid.

     */

    public void notifyInvalidated() {

        synchronized (mObservers) {

            for (DataSetObserver observer : mObservers) {

                observer.onInvalidated();

            }

        }

    }

}

 

 

public abstract class Observable<T> {

    /**

     * The list of observers.  An observer can be in the list at most

     * once and will never be null.

     */

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

        }

    }

   

    public void unregisterAll() {

        synchronized(mObservers) {

            mObservers.clear();

        }       

    }

}

 

對於DataSetObserver基類,我們也給出代碼:

public abstract class DataSetObserver {

    public void onChanged() {}

    public void onInvalidated() {}

}

 

 

綜合起來分析就是,ArrayAdapter使自己具備被觀察的能力的方法是,ArrayAdapter內部有一個private final DataSetObservable mDataSetObservable = new DataSetObservable()的變量,這個變量一方面維護着一個保存觀察者的數據結構,另一方面提供registerDataSetObserver(DataSetObserver observer)和unregisterDataSetObserver(DataSetObserver observer)來管理觀察自己的對象;而當ArrayAdapter綁定數的據發生變化時,它會調用內部的notifyDataSetChanged()方法,但這個方法最終是調用mDataSetObservable的notifyChanged()方法。在該方法里,該方法會逐一審視有哪些觀察者在觀察我,然后調用觀察者的觀察方法onChanged()。

 

3、   ListView觀察ArrayAdapter的數據集的機制

通過以上分析可以知道,ListView要實現觀察ArrayAdapter,需要將自己注冊到ArrayAdapter的DataSetObservable mDataSetObservable里去,注冊的方法是調用ArrayAdapter的registerDataSetObserver(DataSetObserver observer)方法。

那ListView是如何將自己注冊上去的呢?具體過程如下:

 

public class ListView extends AbsListView {

/**

     * Sets the data behind this ListView.

     *

     * The adapter passed to this method may be wrapped by a {@link WrapperListAdapter},

     * depending on the ListView features currently in use. For instance, adding

     * headers and/or footers will cause the adapter to be wrapped.

     *

     * @param adapter The ListAdapter which is responsible for maintaining the

     *        data backing this list and for producing a view to represent an

     *        item in that data set.

     */

    @Override

    public void setAdapter(ListAdapter adapter) {

        if (null != mAdapter) {

            mAdapter.unregisterDataSetObserver(mDataSetObserver); // 關鍵的成員變量,繼承自AbsListView,等下去看看AbsListView關於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;

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

            }

 

            if (mChoiceMode != CHOICE_MODE_NONE &&

                    mAdapter.hasStableIds() &&

                    mCheckedIdStates == null) {

                mCheckedIdStates = new LongSparseArray<Boolean>();

            }

 

        } else {

            mAreAllItemsSelectable = true;

            checkFocus();

            // Nothing selected

            checkSelectionChanged();

        }

 

        if (mCheckStates != null) {

            mCheckStates.clear();

        }

       

        if (mCheckedIdStates != null) {

            mCheckedIdStates.clear();

        }

 

        requestLayout();

}

}

 

 

public abstract class AbsListView extends AdapterView<ListAdapter> implements TextWatcher,

        ViewTreeObserver.OnGlobalLayoutListener, Filter.FilterListener,

        ViewTreeObserver.OnTouchModeChangeListener {

 

   /**

     * Should be used by subclasses to listen to changes in the dataset

     */

    AdapterDataSetObserver mDataSetObserver; // mDataSetObserver就是在這里定義的。那我們再看看AdapterDataSetObserver是什么類型的數據,看看當數據發生變化的時候,該類會進行什么樣的動作。

 

    /**

     * The adapter containing the data to be displayed by this view

     */

ListAdapter mAdapter;

}

 

值得注意的是,AdapterDataSetObserver是AdapterView里的一個內部類(http://www.androidjavadoc.com/1.1_r1_src/android/widget/class-use/AdapterView.AdapterDataSetObserver.html ),具體我們查看下代碼:

class AdapterDataSetObserver extends DataSetObserver {

 

        private Parcelable mInstanceState = null;

 

        @Override

        public void onChanged() {

            mDataChanged = true;

            mOldItemCount = mItemCount;

            mItemCount = getAdapter().getCount();

 

            // Detect the case where a cursor that was previously invalidated has

            // been repopulated with new data.

            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()) {

                // Remember the current state for the case where our hosting activity is being

                // stopped and later restarted

                mInstanceState = AdapterView.this.onSaveInstanceState();

            }

 

            // Data is invalid so we should reset our state

            mOldItemCount = mItemCount;

            mItemCount = 0;

            mSelectedPosition = INVALID_POSITION;

            mSelectedRowId = INVALID_ROW_ID;

            mNextSelectedPosition = INVALID_POSITION;

            mNextSelectedRowId = INVALID_ROW_ID;

            mNeedSync = false;

            checkSelectionChanged();

 

            checkFocus();

            requestLayout();

        }

 

        public void clearSavedState() {

            mInstanceState = null;

        }

    }

 

最終調用的代碼(from View.class)

    /**

     * Call this when something has changed which has invalidated the

     * layout of this view. This will schedule a layout pass of the view

     * tree.

     */

    public void requestLayout() {

        if (ViewDebug.TRACE_HIERARCHY) {

            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.REQUEST_LAYOUT);

        }

 

        mPrivateFlags |= FORCE_LAYOUT;

 

        if (mParent != null && !mParent.isLayoutRequested()) {

            mParent.requestLayout();

        }

    }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM