1. 程式人生 > >Adapter模式實戰-重構鴻洋的Android建行圓形選單

Adapter模式實戰-重構鴻洋的Android建行圓形選單

                     

對於很多開發人員來說,炫酷的UI效果是最吸引他們注意力的,很多人也因為這些炫酷的效果而去學習一些比較知名的UI庫。而做出炫酷效果的前提是你必須對自定義View有所理解,作為90的小民自然也不例外。特別對於剛處在開發初期的小民,對於自定義View這件事覺得又神祕又帥氣,於是小民決定深入研究自定義View以及相關的知識點。

在此之前我們先來看看洋神的原版效果圖:

原文連結在這裡,感謝鴻洋的分享!!!

記得那是2014年的第一場雪,比以往時候來得稍晚一些。小民的同事洋叔是一位資深的研發人員,擅長寫UI特效,在開發領域知名度頗高。最近洋叔剛釋出了一個效果不錯的圓形選單,這個選單的每個Item環形排布,並且可以轉動。小民決定仿照洋叔的效果實現一遍,但是對於小民這個階段來說只要實現環形佈局就不錯了,轉動部分作為下個版本功能,就當作自定義View的練習了。

在google了自定義View相關的知識點之後,小民就寫好了這個圓形選單佈局檢視,我們一步一步來講解,程式碼如下:

// 圓形選單public class CircleMenuLayout extends ViewGroup {    // 圓形直徑    private int mRadius;    // 該容器內child item的預設尺寸    private static final float RADIO_DEFAULT_CHILD_DIMENSION = 1 / 4f;    // 該容器的內邊距,無視padding屬性,如需邊距請用該變數    private static final float
RADIO_PADDING_LAYOUT = 1 / 12f;    // 該容器的內邊距,無視padding屬性,如需邊距請用該變數    private float mPadding;    // 佈局時的開始角度    private double mStartAngle = 0;    // 選單項的文字    private String[] mItemTexts;    // 選單項的圖示    private int[] mItemImgs;    // 選單的個數    private int mMenuItemCount;    // 選單佈局資源id    private int mMenuItemLayoutId = R.layout.circle_menu_item;    // MenuItem的點選事件介面
    private OnItemClickListener mOnMenuItemClickListener;    public CircleMenuLayout(Context context, AttributeSet attrs) {        super(context, attrs);        // 無視padding        setPadding(0, 0, 0, 0);    }    // 設定選單條目的圖示和文字    public void setMenuItemIconsAndTexts(int[] images, String[] texts) {        if (images == null && texts == null) {            throw new IllegalArgumentException("選單項文字和圖片至少設定其一");        }        mItemImgs = images;        mItemTexts = texts;        // 初始化mMenuCount        mMenuItemCount = images == null ? texts.length : images.length;        if (images != null && texts != null) {            mMenuItemCount = Math.min(images.length, texts.length);        }        // 構建選單項        buildMenuItems();    }    // 構建選單項    private void buildMenuItems() {        // 根據使用者設定的引數,初始化menu item        for (int i = 0; i < mMenuItemCount; i++) {            View itemView = inflateMenuView(i);            // 初始化選單項            initMenuItem(itemView, i);            // 新增view到容器中            addView(itemView);        }    }    private View inflateMenuView(final int childIndex) {        LayoutInflater mInflater = LayoutInflater.from(getContext());        View itemView = mInflater.inflate(mMenuItemLayoutId, this, false);        itemView.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                if (mOnMenuItemClickListener != null) {                    mOnMenuItemClickListener.onClick(v, childIndex);                }            }        });        return itemView;    }    private void initMenuItem(View itemView, int childIndex) {        ImageView iv = (ImageView) itemView                .findViewById(R.id.id_circle_menu_item_image);        TextView tv = (TextView) itemView                .findViewById(R.id.id_circle_menu_item_text);        iv.setVisibility(View.VISIBLE);        iv.setImageResource(mItemImgs[childIndex]);        tv.setVisibility(View.VISIBLE);        tv.setText(mItemTexts[childIndex]);    }    // 設定MenuItem的佈局檔案,必須在setMenuItemIconsAndTexts之前呼叫    public void setMenuItemLayoutId(int mMenuItemLayoutId) {        this.mMenuItemLayoutId = mMenuItemLayoutId;    }    // 設定MenuItem的點選事件介面    public void setOnItemClickListener(OnItemClickListener listener) {        this.mOnMenuItemClickListener = listener;    }    // 程式碼省略}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94

小民的思路大致是這樣的,首先讓使用者通過setMenuItemIconsAndTexts函式將選單項的圖示和文字傳遞進來,根據這些圖示和文字構建選單項,選單項的佈局檢視由mMenuItemLayoutId儲存起來,這個mMenuItemLayoutId預設為circle_menu_item.xml,這個xml佈局為一個ImageView顯示在一個文字控制元件的上面。為了選單項的可定製型,小民還添加了一個setMenuItemLayoutId函式讓使用者可以設定選單項的佈局,希望使用者可以定製各種各樣的選單樣式。在使用者設定了選單項的相關資料之後,小民會根據使用者設定進來的圖示和文字數量來構建、初始化相等數量的選單項,並且將這些選單項新增到圓形選單CircleMenuLayout中。然後添加了一個可以設定使用者點選選單項的處理介面的setOnItemClickListener函式,使得選單的點選事件可以被使用者自定義處理。

在將選單項新增到CircleMenuLayout之後就是要對這些選單項進行尺寸丈量和佈局了,我們先來看丈量尺寸的程式碼,如下 :

    //設定佈局的寬高,並策略menu item寬高    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        // 丈量自身尺寸        measureMyself(widthMeasureSpec, heightMeasureSpec);        // 丈量選單項尺寸        measureChildViews();    }    private void measureMyself(int widthMeasureSpec, int heightMeasureSpec) {        int resWidth = 0;        int resHeight = 0;        // 根據傳入的引數,分別獲取測量模式和測量值        int width = MeasureSpec.getSize(widthMeasureSpec);        int widthMode = MeasureSpec.getMode(widthMeasureSpec);        int height = MeasureSpec.getSize(heightMeasureSpec);        int heightMode = MeasureSpec.getMode(heightMeasureSpec);        // 如果寬或者高的測量模式非精確值        if (widthMode != MeasureSpec.EXACTLY                || heightMode != MeasureSpec.EXACTLY) {            // 主要設定為背景圖的高度            resWidth = getSuggestedMinimumWidth();            // 如果未設定背景圖片,則設定為螢幕寬高的預設值            resWidth = resWidth == 0 ? getDefaultWidth() : resWidth;            resHeight = getSuggestedMinimumHeight();            // 如果未設定背景圖片,則設定為螢幕寬高的預設值            resHeight = resHeight == 0 ? getDefaultWidth() : resHeight;        } else {            // 如果都設定為精確值,則直接取小值;            resWidth = resHeight = Math.min(width, height);        }        setMeasuredDimension(resWidth, resHeight);    }    private void measureChildViews() {        // 獲得半徑        mRadius = Math.max(getMeasuredWidth(), getMeasuredHeight());        // menu item數量        final int count = getChildCount();        // menu item尺寸        int childSize = (int) (mRadius * RADIO_DEFAULT_CHILD_DIMENSION);        // menu item測量模式        int childMode = MeasureSpec.EXACTLY;        // 迭代測量        for (int i = 0; i < count; i++) {            final View child = getChildAt(i);            if (child.getVisibility() == GONE) {                continue;            }            // 計算menu item的尺寸;以及和設定好的模式,去對item進行測量            int makeMeasureSpec = -1;            makeMeasureSpec = MeasureSpec.makeMeasureSpec(childSize,                    childMode);            child.measure(makeMeasureSpec, makeMeasureSpec);        }        mPadding = RADIO_PADDING_LAYOUT * mRadius;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58

程式碼比較簡單,就是先測量CircleMenuLayout的尺寸,然後測量每個選單項的尺寸。尺寸獲取了之後就到了佈局這一步,這也是整個圓形選單的核心所在。程式碼如下 :

    // 佈局menu item的位置    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        final int childCount = getChildCount();        int left, top;        // menu item 的尺寸        int itemWidth = (int) (mRadius * RADIO_DEFAULT_CHILD_DIMENSION);        // 根據menu item的個數,計算item的佈局佔用的角度        float angleDelay = 360 / childCount;        // 遍歷所有選單項設定它們的位置        for (int i = 0; i < childCount; i++) {            final View child = getChildAt(i);            if (child.getVisibility() == GONE) {                continue;            }            // 選單項的起始角度            mStartAngle %= 360;            // 計算,中心點到menu item中心的距離            float distanceFromCenter = mRadius / 2f                                 - itemWidth / 2 - mPadding;            // distanceFromCenter cosa 即menu item中心點的left座標            left = mRadius / 2 + (int)Math.round(distanceFromCenter                      * Math.cos(Math.toRadians(mStartAngle))                       * - 1 / 2f * itemWidth);            // distanceFromCenter sina 即menu item的縱座標            top = mRadius / 2                         + (int) Math.round(distanceFromCenter                    * Math.sin( Math.toRadians(mStartAngle) )                     * - 1 / 2f * itemWidth);            // 佈局child view            child.layout(left, top,                     left + itemWidth, top + itemWidth);            // 疊加尺寸            mStartAngle += angleDelay;        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

onLayout函式看起來稍顯複雜,但它的含義就是將所有選單項按照圓弧的形式佈局。整個圓為360度,如果每個選單項佔用的角度為60度,那麼第一個選單項的角度為0~60,那麼第二個選單項的角度就是60~120,以此類推將所有選單項按照圓形佈局。首先要去計算每個選單項的left 和 top位置 ,計算公式的圖形化表示如圖所示。

上圖右下角那個小圓就是我們的選單項,那麼他的left座標就是mRadius / 2 + tmp * coas , top座標則是mRadius / 2 + tmp * sina 。這裡的tmp就是我們程式碼中的distanceFromCenter變數。到了這一步之後小民的第一版圓形選單算是完成了。 下面我們就來整合一下這個圓形選單。     建立一個工程之後,首先在佈局xml中新增圓形選單控制元件,程式碼如下 :

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:background="@drawable/bg"    android:gravity="center"    android:orientation="horizontal" ><com.dp.widgets.CircleMenuLayout        xmlns:android="http://schemas.android.com/apk/res/android"        android:id="@+id/id_menulayout"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:background="@drawable/circle_bg" /></LinearLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

為了更好的顯示效果,在佈局xml中我們為圓形選單的上一層以及圓形選單本書都添加了一個背景圖。然後在MainActivity中設定選單項資料以及點選事件等。程式碼如下所示 :

public class  MainActivity extends Activity {    private CircleMenuLayout mCircleMenuLayout;    // 選單標題    private String[] mItemTexts = new String[] {            "安全中心 ", "特色服務", "投資理財",            "轉賬匯款", "我的賬戶", "信用卡"    };    // 選單圖示    Private int[] mItemImgs = new int[] {            R.drawable.home_mbank_1_normal,            R.drawable.home_mbank_2_normal,               R.drawable.home_mbank_3_normal,            R.drawable.home_mbank_4_normal,               R.drawable.home_mbank_5_normal,            R.drawable.home_mbank_6_normal    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);            // 初始化圓形選單             mCircleMenuLayout = (CircleMenuLayout)                 findViewById(R.id.id_menulayout);            // 設定選單資料項            mCircleMenuLayout.setMenuItemIconsAndTexts(mItemImgs,                         mItemTexts);            // 設定選單項點選事件            mCircleMenuLayout.setOnItemClickListener(new            OnItemClickListener() {                @Override                public void onClick(View view, int pos) {                    Toast.makeText(MainActivity.this,                             mItemTexts[pos],                        Toast.LENGTH_SHORT).show();            }        });    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

執行效果如前文的動圖所示。

小民得意洋洋的蹦出了一個字:真酷!同時也為自己的學習能力感到驕傲,臉上寫滿了滿足與自豪,感覺自己又朝高階工程師邁近了一步。

“這不是洋叔寫的圓形選單嘛,小民也下載了?”整準備下班的主管看到這個UI效果問道。小民只好把其中的緣由、實現方式一一說給主管聽,小民還特地強調了CircleMenuLayout的可定製型,通過setMenuItemLayoutId函式設定選單項的佈局id,這樣選單項的UI效果就可以被使用者定製化了。主管掃視了小民的程式碼,似乎察覺出了什麼。於是轉身找來還在埋頭研究程式碼的洋叔,並且把小民的實現簡單介紹了一遍,洋叔老師在掃視了一遍程式碼之後就發現了其中的問題所在。

“小民吶,你剛才說使用者通過setMenuItemLayoutId函式可以設定選單項的UI效果。那麼問題來了,在你的CircleMenuLayout中預設實現的是circle_menu_item.xml的邏輯,比如載入選單項佈局之後會通過findViewById找到佈局中的各個子檢視,並且進行資料繫結。例如設定圖示和文字,但這是針對circle_menu_item.xml這個佈局的具體實現。如果使用者設定選單項佈局為other_menu_item.xml,並且每個選單項修改為就是一個Button,那麼此時他必須修改CircleMenuLayout中初始化選單項的程式碼。因為佈局變了,選單項裡面的子View型別也變化了,選單需要的資料也發生了變化。例如選單項不再需要圖示,只需要文字。這樣一來,使用者每換一種選單樣式就需要修改一次CircleMenuLayout類一次,並且設定選單資料的介面也需要改變。這樣就沒有定製型可言了嘛,而且明顯違反了開閉原則。反覆對CircleMenuLayout進行修改不免會引入各種各樣的問題……”洋叔老師果然一針見血,深刻啊!小民這才發現了問題所在,於是請教洋叔老師應該如何處理比較合適。

“這種情況你應該使用Adapter,就像ListView中的Adapter一樣,讓使用者來自定義選單項的佈局、解析、資料繫結等工作,你需要知道的僅僅是每個選單項都是一個View。這樣一來就將變化通過Adapter層隔離出去,你依賴的只是Adapter這個抽象。每個使用者可以有不同的實現,你只需要實現圓形選單的丈量、佈局工作即可。這樣就可以擁抱變化,可定製性就得到了保證。當然,你可以提供一個預設的Adapter,也就是使用你的 circle_menu_item.xml佈局實現的選單,這樣沒有定製需求的使用者就可以使用這個預設的實現了。”小民頻頻點頭,屢屢稱是。“這確實是我之前沒有考慮好,也是經驗確實不足,我再好好重構一下。”小民發現問題之後也承認了自己的不足,兩位前輩看小民這麼好學就陪著小民一塊重構程式碼。

在兩位前輩的指點下,經過不到五分鐘重構,小民的CircleMenuLayout成了下面這樣。

// 圓形選單public class CircleMenuLayout extends ViewGroup {    // 欄位省略    // 設定Adapter    public void setAdapter(ListAdapter mAdapter) {        this.mAdapter = mAdapter;    }    // 構建選單項    private void buildMenuItems() {        // 根據使用者設定的引數,初始化menu item        for (int i = 0; i < mAdapter.getCount(); i++) {            final View itemView = mAdapter.getView(i, null, this);            final int position = i;            itemView.setOnClickListener(new OnClickListener() {                @Override                public void onClick(View v) {                    if (mOnMenuItemClickListener != null) {                        mOnMenuItemClickListener.onClick(itemView, position);                    }                }            });            // 新增view到容器中            addView(itemView);        }    }    @Override    protected void onAttachedToWindow() {        if (mAdapter != null) {            buildMenuItems();        }        super.onAttachedToWindow();    }    // 丈量、佈局程式碼省略}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

現在的CircleMenuLayout把解析xml、初始化選單項的具體工作移除,添加了一個Adapter,在使用者設定了Adapter之後,在onAttachedToWindow函式中呼叫Adapter的getCount函式獲取選單項的數量,然後通過getView函式獲取每個View,最後將這些選單項的View新增到圓形選單中,圓形選單佈局再將他們佈局到特定的位置即可。

我們看現在使用CircleMenuLayout是怎樣的形式。首先定義了一個實體類MenuItem來儲存選單項圖示和文字的資訊,程式碼如下 :

static class MenuItem {    public int imageId;    public String title;    public MenuItem(String title, int resId) {            this.title = title;         imageId = resId;   }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

然後再實現一個Adapter,這個Adapter的型別就是ListAdapter。我們需要在getView中載入選單項xml、繫結資料等,相關程式碼如下 :

static class CircleMenuAdapter extends BaseAdapter {        List<MenuItem> mMenuItems;        public CircleMenuAdapter(List<MenuItem> menuItems) {            mMenuItems = menuItems;        }        // 載入選單項佈局,並且初始化每個選單        @Override        public View getView(final int position, View convertView, ViewGroup parent) {            LayoutInflater mInflater = LayoutInflater.from(parent.getContext());            View itemView = mInflater.inflate(R.layout.circle_menu_item, parent, false);            initMenuItem(itemView, position);            return itemView;        }        // 初始化選單項        private void initMenuItem(View itemView, int position) {            // 獲取資料項            final MenuItem item = getItem(position);             ImageView iv = (ImageView) itemView                    .findViewById(R.id.id_circle_menu_item_image);            TextView tv = (TextView) itemView                    .findViewById(R.id.id_circle_menu_item_text);            // 資料繫結            iv.setImageResource(item.imageId);            tv.setText(item.title);        }        // 省略獲取item count等程式碼    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

這與我們在ListView中使用Adapter是一致的,實現getView、getCount等函式,在getView中載入每一項的佈局檔案,並且繫結資料等。最終將選單View返回,然後這個View就會被新增到CircleMenuLayout中。這一步的操作原來是放在CircleMenuLayout中的,現在被獨立出來,並且通過Adapter進行了隔離。這樣就將易變的部分通過Adapter抽象隔離開來,即使使用者有成千上萬中選單項UI效果,那麼通過Adapter就可以很容易的進行擴充套件、實現,而不需要每次都修改CircleMenuLayout中的程式碼。CircleMenuLayout佈局類相當於提供了一個圓形佈局抽象,至於每一個子View是啥樣的它並不需要關心。通過Adapter隔離變化,擁抱變化,就是這麼簡單。

“原來ListView、RecyclerView通過一個Adapter是這個原因,通過Adapter將易變的部分獨立出去交給使用者處理。又通過觀察者模式將資料和UI解耦合,使得View與資料沒有依賴,一份資料可以作用於多個UI,應對UI的易變性。原來如此!”小民最後總結道。

例如,當我們的產品發生變化,需要將圓形選單修改為普通的ListView樣式,那麼我們要做的事很簡單,就是將xml佈局中的CircleMenuLayout修改為ListView,然後將Adapter設定給ListView即可。程式碼如下 :

public class MainActivity extends Activity {private ListView mListView;    List<MenuItem> mMenuItems = new ArrayList<MenuItem>();    @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        // 模擬資料        mockMenuItems();        mListView = (ListView) findViewById(R.id.id_menulayout);        // 設定介面卡        mListView.setAdapter(new CircleMenuAdapter(mMenuItems));        // 設定點選事件        mListView.setOnItemClickListener(new OnItemClickListener(){                @Override                public void onItemClick(AdapterView<?> parent,                     View view, int position, long id) {                             Toast.makeText(MainActivity.this,                        mMenuItems.get(position).title,                       Toast.LENGTH_SHORT).show();            }        });}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

這樣我們就完成了UI替換,成本很低,也基本不會引發其他錯誤。這也就是為什麼我們在CircleMenuLayout中要使用ListAdapter的原因,就是為了與現有的ListView、GridView等元件進行相容,當然我們也沒有啥必要重新再定義一個Adapter型別,從此我們就可以任意修改我們的選單Item樣式了,保證了這個元件的靈活性!! 替換為ListView的效果如下所示:

“走,我請兩位前輩吃烤魚去!”小民在重構完CircleMenuLayout之後深感收穫頗多,為了報答主管和洋叔的指點嚷嚷著要請吃飯。“那就走吧!”主管倒是爽快的答應了,洋叔老師也是立馬應允,三人收拾好電腦後就朝著樓下的巫山烤魚店走去。

20.9總結

Adapter模式的經典實現在於將原本不相容的介面融合在一起,使之能夠很好的進行合作。但是在實際開發中,Adapter模式也有一些靈活的實現。例如ListView中的隔離變化,使得整個UI架構變得更靈活,能夠擁抱變化。Adapter模式在開發中運用非常廣泛,因此掌握Adapter模式是非常必要的。