標籤:
一、作用
適配器模式(Adapter):將一個類的介面轉換成客戶希望的另外一個介面,使得原本由於介面不相容而不能一起工作的那些類可以一起工作,而不需要去改變原始的類或者介面。
二、適用情境
1. 業務的介面與工作的類不相容,(比如:類中缺少實現介面的某些方法)但又需要兩者一起工作
2. 在現有介面和類的基礎上為新的業務需求提供介面
三、常見的使用方式
還是以Usb介面和Phone手機類的產品舉例子,假設設計的Phone類中有 call(), sms(), takeAlong()屬性方法,而在設計Usb介面時定義了 store(), takeAlong()的行為。如果現在有新的業務需求,需要產生 Xiaomi手機類具有 Phone類和Usb介面兩者功能,假設Phone類和Usb介面已經在業務上投入使用,很顯然,去修改原類中的方法和介面的行為去滿足現在的新業務需求是不可取的,那麼現在適配者模式就派上用場了。
適配器模式主要有兩種:類適配模式和對象適配模式
1.類適配模式
大致的意思是新的業務類Xiaomi通過繼承舊業務的類Phone並實現介面Usb來滿足新的業務的一種適配方式。
代碼如下:
Usb介面類
public interface Usb{ public void store(); public void takeAlong();}Phone 類
public class Phone { public void call(){ System.out.print("phone can call"); } public void sms(){ System.out.print("phone can send messages"); } public void takeAlong() { System.out.println("Phone takeAlong"); }}適配後的Xiaomi類
public class Xiaomi extends Phone implements Usb{ public void store() { System.out.println("store implements usb"); }}適配完成後 就可以使用xiaomi 類去完成一些功能
Xiaomi xm = new Xiaomi();xm.takeAlong();xm.store();xm.call();
2.對象適配模式
實現的方式很簡單,其實就是在適配的時候通過建構函式將舊的業務Phone 當作新的適配類(XiaomiWrapper)一個成員對象去處理,然後適配類只需要實現介面 Usb即可。
public class XiaomiWrapper implements Usb{ /** * 1.建立一個Wrapper類,持有原類的一個執行個體, * 2.在Wrapper類的方法中,調用執行個體的方法就行 */ private Phone phone; public XiaomiWrapper(Phone phone) { this.phone = phone; } @Override public void store() { } @Override public void takeAlong() { phone.takeAlong(); }}適配完後通過建構函式將原對象傳入即可。
XiaomiWrapper xiaomiWrapper = new XiaomiWrapper(new Phone());xiaomiWrapper.store();xiaomiWrapper.takeAlong();
Android 源碼中的適配器模式的很好運用就是listView 中使用Adapter來顯示資料。
// 代碼省略 ListView myListView = (ListView)findViewById(listview_id); // 設定適配器 myListView.setAdapter(new MyAdapter(context, myDatas));
適配器類// 適配器public class MyAdapter extends BaseAdapter{ private LayoutInflater mInflater; List<String> mDatas ; public MyAdapter(Context context, List<String> datas){ this.mInflater = LayoutInflater.from(context); mDatas = datas ; } @Override public int getCount() { return mDatas.size(); } @Override public String getItem(int pos) { return mDatas.get(pos); } @Override public long getItemId(int pos) { return pos; } // 解析、設定、緩衝convertView以及相關內容 @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; // Item View的複用 if (convertView == null) { holder = new ViewHolder(); convertView = mInflater.inflate(R.layout.my_listview_item, null); // 擷取title holder.title = (TextView)convertView.findViewById(R.id.title); convertView.setTag(holder); } else { holder = (ViewHolder)convertView.getTag(); } holder.title.setText(mDatas.get(position)); return convertView; } }那麼ListView是如何通過Adapter模式 ( 不止Adapter模式 )來運作的呢 ?
ListView繼承自AbsListView,Adapter定義在AbsListView中,我們看一看這個類。
public abstract class AbsListView extends AdapterView<ListAdapter> implements TextWatcher, ViewTreeObserver.OnGlobalLayoutListener, Filter.FilterListener, ViewTreeObserver.OnTouchModeChangeListener, RemoteViewsAdapter.RemoteAdapterConnectionCallback { ListAdapter mAdapter ; // 關聯到Window時調用的函數 @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); // 代碼省略 // 給適配器註冊一個觀察者,該模式下一篇介紹。 if (mAdapter != null && mDataSetObserver == null) { mDataSetObserver = new AdapterDataSetObserver(); mAdapter.registerDataSetObserver(mDataSetObserver); // Data may have changed while we were detached. Refresh. mDataChanged = true; mOldItemCount = mItemCount // 擷取Item的數量,調用的是mAdapter的getCount方法 mItemCount = mAdapter.getCount(); } mIsAttached = true; } /** * 子類需要覆寫layoutChildren()函數來布局child view,也就是Item View */ @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); mInLayout = true; if (changed) { int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { getChildAt(i).forceLayout(); } mRecycler.markChildrenDirty(); } if (mFastScroller != null && mItemCount != mOldItemCount) { mFastScroller.onItemCountChanged(mOldItemCount, mItemCount); } // 布局Child View layoutChildren(); mInLayout = false; mOverscrollMax = (b - t) / OVERSCROLL_LIMIT_DIVISOR; } // 擷取一個Item View View obtainView(int position, boolean[] isScrap) { isScrap[0] = false; View scrapView; // 從緩衝的Item View中擷取,ListView的複用機制就在這裡 scrapView = mRecycler.getScrapView(position); View child; if (scrapView != null) { // 代碼省略 child = mAdapter.getView(position, scrapView, this); // 代碼省略 } else { child = mAdapter.getView(position, null, this); // 代碼省略 } return child; } }AbsListView定義了集合視圖的架構,比如Adapter模式的應用、複用Item View的邏輯、布局Item View的邏輯等。子類只需要覆寫特定的方法即可實現集合視圖的功能,例如ListView。
ListView中的相關方法。
protected void layoutChildren() { // 代碼省略 try { super.layoutChildren(); invalidate(); // 代碼省略 // 根據配置模式來布局Item View switch (mLayoutMode) { case LAYOUT_SET_SELECTION: if (newSel != null) { sel = fillFromSelection(newSel.getTop(), childrenTop, childrenBottom); } else { sel = fillFromMiddle(childrenTop, childrenBottom); } break; case LAYOUT_SYNC: sel = fillSpecific(mSyncPosition, mSpecificTop); break; case LAYOUT_FORCE_BOTTOM: sel = fillUp(mItemCount - 1, childrenBottom); adjustViewsUpOrDown(); break; case LAYOUT_FORCE_TOP: mFirstPosition = 0; sel = fillFromTop(childrenTop); adjustViewsUpOrDown(); break; case LAYOUT_SPECIFIC: sel = fillSpecific(reconcileSelectedPosition(), mSpecificTop); break; case LAYOUT_MOVE_SELECTION: sel = moveSelection(oldSel, newSel, delta, childrenTop, childrenBottom); break; default: // 代碼省略 break; } } // 從上到下填充Item View [ 只是其中一種填充方式 ] private View fillDown(int pos, int nextTop) { View selectedView = null; int end = (mBottom - mTop); if ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) { end -= mListPadding.bottom; } while (nextTop < end && pos < mItemCount) { // is this the selected item? boolean selected = pos == mSelectedPosition; View child = makeAndAddView(pos, nextTop, true, mListPadding.left, selected); nextTop = child.getBottom() + mDividerHeight; if (selected) { selectedView = child; } pos++; } return selectedView; } // 添加Item View private View makeAndAddView(int position, int y, boolean flow, int childrenLeft, boolean selected) { View child; // 代碼省略 // Make a new view for this position, or convert an unused view if possible child = obtainView(position, mIsScrap); // This needs to be positioned and measured setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]); return child; }ListView覆寫了AbsListView中的layoutChilden函數,在該函數中根據配置模式來布局Item View。Item View的個數、樣式都通過Adapter對應的方法來擷取,擷取個數、Item View之後,將這些Item View布局到ListView對應的座標上,再加上Item View的複用機制,整個ListView就基本運轉起來了。
適配器模式的優點* 更好的複用性
系統需要使用現有的類,而此類的介面不符合系統的需要。那麼通過適配器模式就可以讓這些功能得到更好的複用。
* 更好的擴充性
在實現適配器功能的時候,可以調用自己開發的功能,從而自然地擴充系統的功能。
適配器模式的缺點過多的使用適配器,會讓系統非常零亂,不易整體進行把握。比如,明明看到調用的是A介面,其實內部被適配成了B介面的實現,一個系統如果太多出現這種情況,無異於一場災難。因此如果不是很有必要,可以不使用適配器,而是直接對系統進行重構。
本文參考:http://www.codeceo.com/article/android-listview-adapter-2.html
android 設計模式之適配器模式