適配器,做爲android應用層的開發中,具備很重要的做用。在諸如ListView,gallery等sdk中提供的展現批量數據的控件中,起到一個適配數據源的做用。sdk中已經爲咱們提供了一個簡單的而且適用性很廣的適配器SimpleAdapter,該類就是繼承自抽象類BaseAdapter實現的一個具體的適配器,而且能接收 List<? extends Map<String, ?>>形式的數據源格式的數據。可是不少狀況下,咱們使用ListView等容器的時候,每每要在渲染每一條數據的時候作一些咱們本身想作的事情,此時就要實現BaseAdapter來實現本身的一個Adapter。
在進入正文以前,先說一下關於抽象類與接口的區別,如下摘自百度知道。
你選擇使用接口和抽象類的依據是什麼? 接口和抽象類的概念不同。接口是對動做的抽象,抽象類是對根源的抽象。抽象類表示的是,這個對象是什麼。接口表示的是,這個對象能作什麼。好比,男人,女人,這兩個類(若是是類的話……),他們的抽象類是人。說明,他們都是人。人能夠吃東西,狗也能夠吃東西,你能夠把「吃東西」定義成一個接口,而後讓這些類去實現它.因此,在高級語言上,一個類只能繼承一個類(抽象類)(正如人不可能同時是生物和非生物),可是能夠實現多個接口(吃飯接口、走路接口)。第一點. 接口是抽象類的變體,接口中全部的方法都是抽象的。而抽象類是聲明方法的存在而不去實現它的類。 第二點. 接口能夠繼承,抽象類不行 第三點. 接口定義方法,不能實現,而抽象類能夠實現部分方法。 第四點. 接口中基本數據類型爲static 而抽類象不是的。當你關注一個事物的本質的時候,用抽象類;當你關注一個操做的時候,用接口。 接口能夠實現也能夠繼承,抽象類不行 抽象類的功能要遠超過接口,可是,定義抽象類的代價高。由於高級語言來講(從實際設計上來講也是)每一個類只能繼承一個類。在這個類中,你必須繼承或編寫出其全部子類的全部共性。雖然接口在功能上會弱化許多,可是它只是針對一個動做的描述。並且你能夠在一個類中同時實現多個接口。在設計階段會下降難度的。
上面說的很清楚了,接口是對動做的抽象,而抽象類是對根源的抽象。抽象類表示的是這個對象是什麼,而接口表示的是這個對象能作什麼。同時在我閱讀BaseAdapter源碼的時候也有些感悟,BaseAdapter是一個抽象類,它實現了ListAdapter和SpinnerAdapter接口,而這兩個接口又實現了Adapter接口。咱們在構造本身的Adapter的時候常常要必須實現的getCount()和getView()方法都是在Adapter接口中定義的。
在大牛們寫的源碼中,每每都是接口與抽象類同時使用。有關於BaseAdapter中的源碼,則把最基本的一些功能(能作什麼)抽象爲一個接口,放在最底層,然後經過繼承這個接口再抽象一些子接口,當有抽象類實現了這些子接口的時候,能夠選擇性的(這是抽象類容許的)實現接口中定義的功能,做爲一個具備統一功能的更具體的可是一樣以抽象的形式而存在,最後咱們要構造這種通過抽象的類型的具體類的時候,則須要將抽象類沒有實現的功能所有實現(這是必須的),甚至能夠重寫抽象類中已經實現的某些方法(通常狀況下沒必要這樣作),這時咱們能夠經過實例化咱們本身構造的具體類來完成一些工做了。
這種層級關係讓我體驗到了從無到有的過程,也深入的理解了什麼叫抽象到具體的過程,在這種模型的基礎上,咱們能夠從容的擴展和具象化。沒必要擔憂功能的丟失或者偏離設計此類型的初衷,由簡化繁須要強大的根基,抽象類和接口的結合就是面向對象變成最強大的根基。
下面貼出源碼來具體看
既然上面說到了由簡化繁,那麼就從最基本的接口開始
一、Adapterjava
/* * Copyright (C) 2006 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.widget; import android.database.DataSetObserver; import android.view.View; import android.view.ViewGroup; /** * An Adapter object acts as a bridge between an {@link AdapterView} and the * underlying data for that view. The Adapter provides access to the data items. * The Adapter is also responsible for making a {@link android.view.View} for * each item in the data set. * * @see android.widget.ArrayAdapter * @see android.widget.CursorAdapter * @see android.widget.SimpleCursorAdapter */ public interface Adapter { /** * Register an observer that is called when changes happen to the data used by this adapter. * * @param observer the object that gets notified when the data set changes. */ void registerDataSetObserver(DataSetObserver observer); /** * Unregister an observer that has previously been registered with this * adapter via {@link #registerDataSetObserver}. * * @param observer the object to unregister. */ void unregisterDataSetObserver(DataSetObserver observer); /** * How many items are in the data set represented by this Adapter. * * @return Count of items. */ int getCount(); /** * Get the data item associated with the specified position in the data set. * * @param position Position of the item whose data we want within the adapter's * data set. * @return The data at the specified position. */ Object getItem(int position); /** * Get the row id associated with the specified position in the list. * * @param position The position of the item within the adapter's data set whose row id we want. * @return The id of the item at the specified position. */ long getItemId(int position); /** * Indicates whether the item ids are stable across changes to the * underlying data. * * @return True if the same id always refers to the same object. */ boolean hasStableIds(); /** * Get a View that displays the data at the specified position in the data set. You can either * create a View manually or inflate it from an XML layout file. When the View is inflated, the * parent View (GridView, ListView...) will apply default layout parameters unless you use * {@link android.view.LayoutInflater#inflate(int, android.view.ViewGroup, boolean)} * to specify a root view and to prevent attachment to the root. * * @param position The position of the item within the adapter's data set of the item whose view * we want. * @param convertView The old view to reuse, if possible. Note: You should check that this view * is non-null and of an appropriate type before using. If it is not possible to convert * this view to display the correct data, this method can create a new view. * Heterogeneous lists can specify their number of view types, so that this View is * always of the right type (see {@link #getViewTypeCount()} and * {@link #getItemViewType(int)}). * @param parent The parent that this view will eventually be attached to * @return A View corresponding to the data at the specified position. */ View getView(int position, View convertView, ViewGroup parent); /** * An item view type that causes the {@link AdapterView} to ignore the item * view. For example, this can be used if the client does not want a * particular view to be given for conversion in * {@link #getView(int, View, ViewGroup)}. * * @see #getItemViewType(int) * @see #getViewTypeCount() */ static final int IGNORE_ITEM_VIEW_TYPE = AdapterView.ITEM_VIEW_TYPE_IGNORE; /** * Get the type of View that will be created by {@link #getView} for the specified item. * * @param position The position of the item within the adapter's data set whose view type we * want. * @return An integer representing the type of View. Two views should share the same type if one * can be converted to the other in {@link #getView}. Note: Integers must be in the * range 0 to {@link #getViewTypeCount} - 1. {@link #IGNORE_ITEM_VIEW_TYPE} can * also be returned. * @see #IGNORE_ITEM_VIEW_TYPE */ int getItemViewType(int position); /** * <p> * Returns the number of types of Views that will be created by * {@link #getView}. Each type represents a set of views that can be * converted in {@link #getView}. If the adapter always returns the same * type of View for all items, this method should return 1. * </p> * <p> * This method will only be called when when the adapter is set on the * the {@link AdapterView}. * </p> * * @return The number of types of Views that will be created by this adapter */ int getViewTypeCount(); static final int NO_SELECTION = Integer.MIN_VALUE; /** * @return true if this adapter doesn't contain any data. This is used to determine * whether the empty view should be displayed. A typical implementation will return * getCount() == 0 but since getCount() includes the headers and footers, specialized * adapters might want a different behavior. */ boolean isEmpty(); }
而後是繼承自這個接口的兩個接口
二、ListAdapter、SpinnerAdapter
(因爲內容很是少,則粘在一塊兒)android
package android.widget; /** * Extended {@link Adapter} that is the bridge between a {@link ListView} * and the data that backs the list. Frequently that data comes from a Cursor, * but that is not * required. The ListView can display any data provided that it is wrapped in a * ListAdapter. */ public interface ListAdapter extends Adapter { /** * Indicates whether all the items in this adapter are enabled. If the * value returned by this method changes over time, there is no guarantee * it will take effect. If true, it means all items are selectable and * clickable (there is no separator.) * * @return True if all items are enabled, false otherwise. * * @see #isEnabled(int) */ public boolean areAllItemsEnabled(); /** * Returns true if the item at the specified position is not a separator. * (A separator is a non-selectable, non-clickable item). * * The result is unspecified if position is invalid. An {@link ArrayIndexOutOfBoundsException} * should be thrown in that case for fast failure. * * @param position Index of the item * * @return True if the item is not a separator * * @see #areAllItemsEnabled() */ boolean isEnabled(int position); }
import android.view.View; import android.view.ViewGroup; /** * Extended {@link Adapter} that is the bridge between a * {@link android.widget.Spinner} and its data. A spinner adapter allows to * define two different views: one that shows the data in the spinner itself * and one that shows the data in the drop down list when the spinner is * pressed. */ public interface SpinnerAdapter extends Adapter { /** * Gets a {@link android.view.View} that displays in the drop down popup * the data at the specified position in the data set. * * @param position index of the item whose view we want. * @param convertView the old view to reuse, if possible. Note: You should * check that this view is non-null and of an appropriate type before * using. If it is not possible to convert this view to display the * correct data, this method can create a new view. * @param parent the parent that this view will eventually be attached to * @return a {@link android.view.View} corresponding to the data at the * specified position. */ public View getDropDownView(int position, View convertView, ViewGroup parent); }
這裏BaseAdapter用到的是觀察者模式,在ListView的setAdapter裏註冊一個AdapterDataSetObserver觀察者,至關於在使用按鈕時候,對按鈕set一個OnClickListener。這裏的AdapterDataSetObserver是一個內部類,這個類繼承自DataSetObserver這個抽象類,而且重寫了onChanged()和onInvalidated()方法,而OnClickListener則是一個接口的子類,實現了這個接口的匿名類或者是實體類,重寫了OnClick()方法。
三、BaseAdapterexpress
import android.database.DataSetObservable; import android.database.DataSetObserver; import android.view.View; import android.view.ViewGroup; /** * 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 boolean hasStableIds() { return false; } public void registerDataSetObserver(DataSetObserver observer) { mDataSetObservable.registerObserver(observer); } public void unregisterDataSetObserver(DataSetObserver observer) { mDataSetObservable.unregisterObserver(observer); } /** * Notifies the attached observers that the underlying data has been changed * and any View reflecting the data set should refresh itself. */ public void notifyDataSetChanged() { mDataSetObservable.notifyChanged(); } /** * Notifies the attached observers that the underlying data is no longer valid * or available. Once invoked this adapter is no longer valid and should * not report further data set changes. */ public void notifyDataSetInvalidated() { mDataSetObservable.notifyInvalidated(); } public boolean areAllItemsEnabled() { return true; } public boolean isEnabled(int position) { return true; } public View getDropDownView(int position, View convertView, ViewGroup parent) { return getView(position, convertView, parent); } public int getItemViewType(int position) { return 0; } public int getViewTypeCount() { return 1; } public boolean isEmpty() { return getCount() == 0; } }
四、觀察者抽象類DataSetObserverapache
package android.database; /** * Receives call backs when a data set has been changed, or made invalid. The typically data sets * that are observed are {@link Cursor}s or {@link android.widget.Adapter}s. * DataSetObserver must be implemented by objects which are added to a DataSetObservable. */ public abstract class DataSetObserver { /** * This method is called when the entire data set has changed, * most likely through a call to {@link Cursor#requery()} on a {@link Cursor}. */ public void onChanged() { // Do nothing } /** * This method is called when the entire data becomes invalid, * most likely through a call to {@link Cursor#deactivate()} or {@link Cursor#close()} on a * {@link Cursor}. */ public void onInvalidated() { // Do nothing } }
五、實現了功能的具體觀察者AdapterDataSetObserver----實則是抽象類AdapterView的一個內部類,而ListView類繼承自抽象類AbsListView,AbsListView繼承自抽象類AdapterView,其中這兩個抽象類的其餘實現咱們不關注,只關注有關於ListView的地方。app
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; checkFocus(); requestLayout(); } public void clearSavedState() { mInstanceState = null; } }
六、觀察者操做類的抽象類Observable<T>
爲了方便閱讀,刪去了註釋,只看方法名比較容易理解,這裏主要實現的是觀察者的註冊,實則是在一個ArrayList中不斷的添加觀察者DataSetObserver實體less
package android.database; import java.util.ArrayList; 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); } } public void unregisterAll() { synchronized(mObservers) { mObservers.clear(); } } }
七、觀察者操做類的具體實現DataSetObservable
能夠看到,這裏定義了notifyChanged方法的實現,經過同步代碼塊逐條的對集合中的每一條數據進行OnChanged方法調用。ide
package android.database; /** * A specialization of {@link Observable} for {@link DataSetObserver} * that provides methods for sending notifications to a list of * {@link DataSetObserver} objects. */ public class DataSetObservable extends Observable<DataSetObserver> { /** * Invokes {@link DataSetObserver#onChanged} on each observer. * Called when the contents of the data set have changed. The recipient * will obtain the new contents the next time it queries the data set. */ public void notifyChanged() { synchronized(mObservers) { // since onChanged() is implemented by the app, it could do anything, including // removing itself from {@link mObservers} - and that could cause problems if // an iterator is used on the ArrayList {@link mObservers}. // to avoid such problems, just march thru the list in the reverse order. for (int i = mObservers.size() - 1; i >= 0; i--) { mObservers.get(i).onChanged(); } } } /** * Invokes {@link DataSetObserver#onInvalidated} on each observer. * Called when the data set is no longer valid and cannot be queried again, * such as when the data set has been closed. */ public void notifyInvalidated() { synchronized (mObservers) { for (int i = mObservers.size() - 1; i >= 0; i--) { mObservers.get(i).onInvalidated(); } } } }
八、最後就是ListView中setAdapter方法的實現了,小小的setAdapter方法中涉及到了BaseAdapter的引用,DataSetObserver的引用。
這裏其實是經過入參傳入了咱們本身實現的Adapter,一個繼承自BaseAdapter的Adapter,而且在方法中已經實例化了一個上文提到的AdapterDataSetObserver,一個繼承自DataSetObserver的具體觀察者。ui
@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();//AdapterDataSetObserver是ListView的基類AdapterView的內部類 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();//更新視圖 }