###1. 概述設計模式
上一期的熱修復相對來講有點難度,我其實也沒往深裏說若是實在看不懂能夠看看視頻,其實最主要的仍是思路代碼也就那麼幾行,這一期咱們又迴歸到設計模式,相對來講要簡單很多,這一期要講的是一行代碼如何顯示全部彈出框效果。bash
視頻地址:http://pan.baidu.com/s/1gfwZfF1markdown
相關文章:app
Builder設計模式 - 構建整個項目的萬能Dialog函數
Builder設計模式 - 構建整個應用的NavigationBar
###2. 模式介紹oop
模式的定義ui
將一個複雜對象的構建與它的表示分離,使得不一樣的構建過程能夠建立不一樣的顯示,但其根本仍是不變。this
模式的使用場景spa
###3. UML類圖
角色介紹
###4. 模式的簡單實現
簡單實現的介紹
電腦的組裝過程較爲複雜,步驟繁多,可是順序倒是不固定的。下面咱們以組裝電腦爲例來演示一下簡單且經典的builder模式。
實現源碼
package com.dp.example.builder; /** * Computer產品抽象類, 爲了例子簡單, 只列出這幾個屬性 * * @author mrsimple * */ public abstract class Computer { protected int mCpuCore = 1; protected int mRamSize = 0; protected String mOs = "Dos"; protected Computer() { } // 設置CPU核心數 public abstract void setCPU(int core); // 設置內存 public abstract void setRAM(int gb); // 設置操做系統 public abstract void setOs(String os); @Override public String toString() { return "Computer [mCpuCore=" + mCpuCore + ", mRamSize=" + mRamSize + ", mOs=" + mOs + "]"; } } package com.dp.example.builder; /** * Apple電腦 */ public class AppleComputer extends Computer { protected AppleComputer() { } @Override public void setCPU(int core) { mCpuCore = core; } @Override public void setRAM(int gb) { mRamSize = gb; } @Override public void setOs(String os) { mOs = os; } } package com.dp.example.builder; package com.dp.example.builder; /** * builder抽象類 * */ public abstract class Builder { // 設置CPU核心數 public abstract void buildCPU(int core); // 設置內存 public abstract void buildRAM(int gb); // 設置操做系統 public abstract void buildOs(String os); // 建立Computer public abstract Computer create(); } package com.dp.example.builder; public class ApplePCBuilder extends Builder { private Computer mApplePc = new AppleComputer(); @Override public void buildCPU(int core) { mApplePc.setCPU(core); } @Override public void buildRAM(int gb) { mApplePc.setRAM(gb); } @Override public void buildOs(String os) { mApplePc.setOs(os); } @Override public Computer create() { return mApplePc; } } package com.dp.example.builder; public class Director { Builder mBuilder = null; /** * * @param builder */ public Director(Builder builder) { mBuilder = builder; } /** * 構建對象 * * @param cpu * @param ram * @param os */ public void construct(int cpu, int ram, String os) { mBuilder.buildCPU(cpu); mBuilder.buildRAM(ram); mBuilder.buildOs(os); } } /** * 經典實現較爲繁瑣 * * @author mrsimple * */ public class Test { public static void main(String[] args) { // 構建器 Builder builder = new ApplePCBuilder(); // Director Director pcDirector = new Director(builder); // 封裝構建過程, 4核, 內存2GB, Mac系統 pcDirector.construct(4, 2, "Mac OS X 10.9.1"); // 構建電腦, 輸出相關信息 System.out.println("Computer Info : " + builder.create().toString()); } } 複製代碼
經過Builder來構建產品對象, 而Director封裝了構建複雜產品對象對象的過程,不對外隱藏構建細節。
###5. Android源碼中的模式實現
在Android源碼中,咱們最經常使用到的Builder模式就是AlertDialog.Builder, 使用該Builder來構建複雜的AlertDialog對象。簡單示例以下 :
//顯示基本的AlertDialog private void showDialog(Context context) { AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setIcon(R.drawable.icon); builder.setTitle("頭部"); builder.setMessage("內容"); builder.setPositiveButton("Button1", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { setTitle("點擊了對話框上的Button1"); } }) .setNeutralButton("Button2", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { setTitle("點擊了對話框上的Button2"); } }); builder.create().show(); // 構建AlertDialog, 而且顯示 } 複製代碼
效果就不演示了沒什麼好看的,若是是v7包中的AlertDialog還看得下去,若是是v4包中的慘目忍睹。下面咱們看看AlertDialog的相關源碼,你看的可能和個人不同但大體差很少你懂的:
// AlertDialog public class AlertDialog extends Dialog implements DialogInterface { // Controller, 接受Builder成員變量P中的各個參數 private AlertController mAlert; // 構造函數 protected AlertDialog(Context context, int theme) { this(context, theme, true); } // 4 : 構造AlertDialog AlertDialog(Context context, int theme, boolean createContextWrapper) { super(context, resolveDialogTheme(context, theme), createContextWrapper); mWindow.alwaysReadCloseOnTouchAttr(); mAlert = new AlertController(getContext(), this, getWindow()); } // 實際上調用的是mAlert的setTitle方法 @Override public void setTitle(CharSequence title) { super.setTitle(title); mAlert.setTitle(title); } // 實際上調用的是mAlert的setCustomTitle方法 public void setCustomTitle(View customTitleView) { mAlert.setCustomTitle(customTitleView); } public void setMessage(CharSequence message) { mAlert.setMessage(message); } // AlertDialog其餘的代碼省略 // ************ Builder爲AlertDialog的內部類 ******************* public static class Builder { // 1 : 存儲AlertDialog的各個參數, 例如title, message, icon等. private final AlertController.AlertParams P; // 屬性省略 /** * Constructor using a context for this builder and the {@link AlertDialog} it creates. */ public Builder(Context context) { this(context, resolveDialogTheme(context, 0)); } public Builder(Context context, int theme) { P = new AlertController.AlertParams(new ContextThemeWrapper( context, resolveDialogTheme(context, theme))); mTheme = theme; } // Builder的其餘代碼省略 ...... // 2 : 設置各類參數 public Builder setTitle(CharSequence title) { P.mTitle = title; return this; } public Builder setMessage(CharSequence message) { P.mMessage = message; return this; } public Builder setIcon(int iconId) { P.mIconId = iconId; return this; } public Builder setPositiveButton(CharSequence text, final OnClickListener listener) { P.mPositiveButtonText = text; P.mPositiveButtonListener = listener; return this; } public Builder setView(View view) { P.mView = view; P.mViewSpacingSpecified = false; return this; } // 3 : 構建AlertDialog, 傳遞參數 public AlertDialog create() { // 調用new AlertDialog構造對象, 而且將參數傳遞個體AlertDialog final AlertDialog dialog = new AlertDialog(P.mContext, mTheme, false); // 5 : 將P中的參數應用的dialog中的mAlert對象中 P.apply(dialog.mAlert); dialog.setCancelable(P.mCancelable); if (P.mCancelable) { dialog.setCanceledOnTouchOutside(true); } dialog.setOnCancelListener(P.mOnCancelListener); if (P.mOnKeyListener != null) { dialog.setOnKeyListener(P.mOnKeyListener); } return dialog; } } } 複製代碼
能夠看到,經過Builder來設置AlertDialog中的title, message, button等參數, 這些參數都存儲在類型爲AlertController.AlertParams的成員變量P中,AlertController.AlertParams中包含了與之對應的成員變量。在調用Builder類的create函數時才建立AlertDialog, 而且將Builder成員變量P中保存的參數應用到AlertDialog的mAlert對象中,即P.apply(dialog.mAlert)代碼段。咱們看看apply函數的實現 :
public void apply(AlertController dialog) { if (mCustomTitleView != null) { dialog.setCustomTitle(mCustomTitleView); } else { if (mTitle != null) { dialog.setTitle(mTitle); } if (mIcon != null) { dialog.setIcon(mIcon); } if (mIconId >= 0) { dialog.setIcon(mIconId); } if (mIconAttrId > 0) { dialog.setIcon(dialog.getIconAttributeResId(mIconAttrId)); } } if (mMessage != null) { dialog.setMessage(mMessage); } if (mPositiveButtonText != null) { dialog.setButton(DialogInterface.BUTTON_POSITIVE, mPositiveButtonText, mPositiveButtonListener, null); } if (mNegativeButtonText != null) { dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mNegativeButtonText, mNegativeButtonListener, null); } if (mNeutralButtonText != null) { dialog.setButton(DialogInterface.BUTTON_NEUTRAL, mNeutralButtonText, mNeutralButtonListener, null); } if (mForceInverseBackground) { dialog.setInverseBackgroundForced(true); } // For a list, the client can either supply an array of items or an // adapter or a cursor if ((mItems != null) || (mCursor != null) || (mAdapter != null)) { createListView(dialog); } if (mView != null) { if (mViewSpacingSpecified) { dialog.setView(mView, mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight, mViewSpacingBottom); } else { dialog.setView(mView); } } } 複製代碼
實際上就是把P中的參數挨個的設置到AlertController中, 也就是AlertDialog中的mAlert對象。從AlertDialog的各個setter方法中咱們也能夠看到,實際上也都是調用了mAlert對應的setter方法。在這裏,Builder同時扮演了上文中提到的builder、ConcreteBuilder、Director的角色,簡化了Builder模式的設計。
###6. 構建整個應用的萬能Dialog AlertDialog其實在咱們的開發過程當中可能沒卵用,通常設計師設計出來的基本都是仿照的IOS的效果,這樣一來就算再好用也與咱們無緣。並且在咱們的開發過程當中效果千奇百怪時而這樣,時而那樣頭疼得很啊,接下來咱們就打算採用系統已經提供好的Builder設計模式構建整個應用的萬能Dialog,代碼能夠參考系統的AlertDialog,最終不管什麼複雜的效果一行能搞定算得上勉勉強強。
public static class Builder { private AlertController.AlertParams P; public Builder(Context context) { this(context, 0); } public Builder(Context context, int themeResId) { P = new AlertController.AlertParams(); P.themeResId = themeResId; P.context = context; } public Builder setText(int viewId, CharSequence text) { P.textArray.put(viewId, text); return this; } public Builder setOnClickListener(int viewId, View.OnClickListener listener) { P.clickArray.put(viewId, listener); return this; } public Builder setContentView(int layoutId) { P.view = null; P.layoutId = layoutId; return this; } public Builder setContentView(View view) { P.layoutId = 0; P.view = view; return this; } /** * Sets whether the dialog is cancelable or not. Default is true. * * @return This Builder object to allow for chaining of calls to set methods */ public Builder setCancelable(boolean cancelable) { P.cancelable = cancelable; return this; } /** * Sets the callback that will be called if the dialog is canceled. * <p> * <p>Even in a cancelable dialog, the dialog may be dismissed for reasons other than * being canceled or one of the supplied choices being selected. * If you are interested in listening for all cases where the dialog is dismissed * and not just when it is canceled, see * {@link #setOnDismissListener(OnDismissListener) setOnDismissListener}.</p> * * @return This Builder object to allow for chaining of calls to set methods * @see #setCancelable(boolean) * @see #setOnDismissListener(OnDismissListener) */ public Builder setOnCancelListener(OnCancelListener onCancelListener) { P.onCancelListener = onCancelListener; return this; } /** * Sets the callback that will be called when the dialog is dismissed for any reason. * * @return This Builder object to allow for chaining of calls to set methods */ public Builder setOnDismissListener(OnDismissListener onDismissListener) { P.onDismissListener = onDismissListener; return this; } /** * Sets the callback that will be called if a key is dispatched to the dialog. * * @return This Builder object to allow for chaining of calls to set methods */ public Builder setOnKeyListener(OnKeyListener onKeyListener) { P.onKeyListener = onKeyListener; return this; } /** * Creates an {@link AlertDialog} with the arguments supplied to this * builder. * <p/> * Calling this method does not display the dialog. If no additional * processing is needed, {@link #show()} may be called instead to both * create and display the dialog. */ public BaseDialog create() { // Context has already been wrapped with the appropriate theme. final BaseDialog dialog = new BaseDialog(P.context, P.themeResId); P.apply(dialog.mAlert); dialog.setCancelable(P.cancelable); if (P.cancelable) { dialog.setCanceledOnTouchOutside(true); } dialog.setOnCancelListener(P.onCancelListener); dialog.setOnDismissListener(P.onDismissListener); if (P.onKeyListener != null) { dialog.setOnKeyListener(P.onKeyListener); } return dialog; } /** * Creates an {@link AlertDialog} with the arguments supplied to this * builder and immediately displays the dialog. * <p/> * Calling this method is functionally identical to: * <pre> * AlertDialog dialog = builder.create(); * dialog.show(); * </pre> */ public BaseDialog show() { final BaseDialog dialog = create(); dialog.show(); return dialog; } } 複製代碼
class AlertController { private DialogViewHelper mViewHelper; private BaseDialog mDialog; private Window mWindow; public AlertController(BaseDialog dialog, Window window) { mDialog = dialog; mWindow = window; } /** * 獲取Dialog * @return */ public BaseDialog getDialog() { return mDialog; } /** * 獲取window * @return */ public Window getWindow() { return mWindow; } public DialogViewHelper getViewHelper() { return mViewHelper; } /** * 設置View的輔助 * @param viewHelper */ public void setDialogViewHelper(DialogViewHelper viewHelper) { this.mViewHelper = viewHelper; } /** * 設置文本 * @param viewId * @param text */ public void setText(int viewId, CharSequence text) { mViewHelper.setText(viewId, text); } /** * 設置點擊事件 * @param viewId * @param listener */ public void setOnClickListener(int viewId, View.OnClickListener listener) { mViewHelper.setOnClickListener(viewId, listener); } /** * 經過id獲取View * @param viewId * @param <T> * @return */ public <T extends View> T getView(int viewId) { return mViewHelper.getView(viewId); } } 複製代碼
之後咱們顯示任何的彈出框效果都只須要一行了:
@Override public void onClick(View v) { BaseDialog dialog = new BaseDialog.Builder(this) .setContentView(R.layout.detail_comment_dialog).fullWith() .fromBottom(false) .show(); } 複製代碼
不過這明明有四行,接下來咱們來講一下好處,Builder設計模式的好處就不說了網上太多了,咱們就只說在真正的開發中這麼作的好處:
視頻地址:http://pan.baidu.com/s/1gfwZfF1
相關文章: