建立型設計模式之Builder模式
1 Builder模式概念
1.1 介紹
Builder模式是一步一步建立一個複雜物件的建立型模式,它允許使用者在不知道內部構建細節的情況下,可以更精細地控制物件的構造流程。該模式是為了將構建複雜物件的過程和它的部件解耦,使得構建過程和部件的表示隔離開來。
Builder模式,在於分工明確,一個抽象建造者,一個具體的建造者,一個指揮者,當然還需要具體的產品。那麼我們以一個軟體產品為例:技術主管就是抽象建造者,他和產品經理溝通,知道要做一個什麼樣的產品;而程式設計師就是具體的勞動者,技術主管說咋做你就咋做;而指揮者就是公司的產品經理,負責和使用者溝通,瞭解客戶的需求。
1.2 定義
將一個複雜物件的構建與它的表示分離,使得同樣的構建過程可以建立不同的表示。
1.3 使用場景
- 相同的方法,不同的執行順序,產生不同的事件結果時;
- 多個部件或零件,都可以裝配到一個物件中,但是產生的執行結果又不相同時;
- 產品類非常複雜,或者產品類中的呼叫順序不同產生了不同的效能,這個時候使用建造者模式非常合適;
- 當初始化一個物件特別複雜,如引數多,且很多引數都具有預設值時。
2 Builder模式UML類圖通用
角色介紹:
- Product——產品類 : 產品的抽象類。
- Builder——抽象類, 規範產品的組建,一般是由子類實現具體的元件過程。
- ConcreteBuilder——具體的構建者。
- Director——指揮者,統一組裝過程(可省略)。
3 通用模式程式碼
(1)產品類
/**
* 產品類
*/
public class Product {
public void doSomething() {
// 獨立業務處理
}
}
(2)抽象建造者類
/**
* 抽象建造者
* 如果有多個產品類就有幾個具體的建造者,而且這多個產品類具有相同介面或抽象類
*/
public abstract class Builder {
// setPart方法是零件的配置,設定產品的不同零件,或者不同的裝配順序,以產生不同的產品
public abstract void setPart();
// 建造產品
public abstract Product buildProduct();
}
(3)具體建造者類
/**
* 具體建造者
*/
public class ConcreteProduct extends Builder {
private Product product = new Product();
// 設定產品零件
public void setPart() {
// 產品類內的邏輯處理
}
// 組建一個產品
public Product buildProduct() {
return product;
}
}
(4)指揮者(導演)類
/**
* 指揮者類
* 指揮者類起到封裝的作用,避免高層模組深入到建造者內部的實現類
*/
public class Director {
// 構建不同的產品
private Builder builder = new ConcreteProduct();
public Product getAProduct() {
builder.setPart();
// 設定不同的零件,產生不同的產品
return builder.buildProduct();
}
}
4 Builder模式變種(常用)
4.1 普通
public class Person {
private String name;
private int age;
private double height;
private double weight;
public Person(String name) {
this.name = name;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(String name, int age, double height) {
this.name = name;
this.age = age;
this.height = height;
}
public Person(String name, int age, double height, double weight) {
this.name = name;
this.age = age;
this.height = height;
this.weight = weight;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
}
建立各個需要的物件:
Person p1=new Person();
Person p2=new Person("張三");
Person p3=new Person("李四",18);
Person p4=new Person("王五",21,180);
Person p5=new Person("趙六",17,170,65.4);
可以想象一下這樣建立的壞處,最直觀的就是四個引數的建構函式的最後面的兩個引數到底是什麼意思,可讀性不怎麼好,如果不點選看原始碼,不知道哪個是weight哪個是height。還有一個問題就是當有很多引數時,編寫這個建構函式就會顯得異常麻煩,這時候如果換一個角度,試試Builder模式,你會發現程式碼的可讀性一下子就上去了。
4.2 使用Builder模式
public class Person {
private String name;
private int age;
private double height;
private double weight;
private Person(Builder builder){
this.name=builder.name;
this.age=builder.age;
this.height=builder.height;
this.weight=builder.weight;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
static class Builder{
private String name;
private int age;
private double height;
private double weight;
public Builder setName(String name) {
this.name=name;
return this;
}
public Builder setAge(int age) {
this.age=age;
return this;
}
public Builder setHeight(double height) {
this.height=height;
return this;
}
public Builder setWeight(double weight) {
this.weight=weight;
return this;
}
public Person build(){
return new Person(this);// build()返回Person物件
}
}
}
在Builder類裡定義了一份與Person類一模一樣的變數,通過一系列的成員函式進行設定屬性值,但是返回值都是this,也就是都是Builder物件,最後提供了一個build函式用於建立Person物件,返回的是Person物件,對應的建構函式在Person類中進行定義,也就是建構函式的入參是Builder物件,然後依次對自己的成員變數進行賦值,對應的值都是Builder物件中的值。此外Builder類中的成員函式返回Builder物件自身的另一個作用就是讓它支援鏈式呼叫,使程式碼可讀性大大增強。於是可以這樣建立Person類。
Person person = new Person().Builder()
.setName("張三")
.setAge(18)
.setHeight(178.5)
.setWeight(67.4)
.build();
4.3 總結一下
(1)定義一個靜態內部類Builder,內部的成員變數和外部類一樣
Builder類通過一系列的方法用於成員變數的賦值,並返回當前物件本身(this)。
(2)Builder類提供一個build方法或者create方法用於建立對應的外部類,該方法內部呼叫了外部類的一個私有建構函式,該建構函式的引數就是內部類Builder。
(3)外部類提供一個私有建構函式供內部類呼叫,在該建構函式中完成成員變數的賦值,取值為Builder物件中對應的值。
5 Builder模式實戰
(1)產品類——探探App
/**
* 產品類——探探App
*/
public class Product {
public static final String ANDROID = "android";
public static final String IOS = "ios";
private String appName;
private String appFuction;
private String appSystem;
public String getAppName() {
return appName;
}
public void setAppName(String appName) {
this.appName = appName;
}
public String getAppFuction() {
return appFuction;
}
public void setAppFuction(String appFuction) {
this.appFuction = appFuction;
}
public String getAppSystem() {
return appSystem;
}
public void setAppSystem(String appSystem) {
this.appSystem = appSystem;
}
@Override
public String toString() {
return "Product [appName=" + appName + ", appFuction=" + appFuction
+ ", appSystem=" + appSystem + "]";
}
}
(2)抽象建造者類——技術主管(可省略)
/**
* 抽象建造者類——技術主管
*/
public abstract class Build {
public abstract Build setAppName(String appName);
public abstract Build setAppFuction(String appFuction);
public abstract Build setAppSystem(String appSystem);
public abstract Product build();
}
(3)具體建造者類——全棧程式設計師
/**
* 具體建造者類——全棧程式設計師
*/
public class WorkBuilder extends Build {
private Product product;// 產品類
private InnerProduct innerProduct = new InnerProduct();// 產品緩衝類
@Override
public Build setAppName(String appName) {
innerProduct.setAppName(appName);
return this;
}
@Override
public Build setAppFuction(String appFuction) {
innerProduct.setAppFuction(appFuction);
return this;
}
@Override
public Build setAppSystem(String appSystem) {
innerProduct.setAppSystem(appSystem);
return this;
}
@Override
public Product build() {
product = new Product();
product.setAppName(innerProduct.getAppName());
product.setAppFuction(innerProduct.getAppFuction());
product.setAppSystem(innerProduct.getAppSystem());
return product;
}
/**
* 產品緩衝類
*/
private class InnerProduct {
private String appName;
private String appFuction;
private String appSystem;
public String getAppName() {
return appName;
}
public void setAppName(String appName) {
this.appName = appName;
}
public String getAppFuction() {
return appFuction;
}
public void setAppFuction(String appFuction) {
this.appFuction = appFuction;
}
public String getAppSystem() {
return appSystem;
}
public void setAppSystem(String appSystem) {
this.appSystem = appSystem;
}
}
}
(4)指揮者類——產品經理(可省略)
/**
* 指揮者類——產品經理
*/
public class Director {
public static Product create(String system) {
return new WorkBuilder().setAppSystem(system).setAppName("探探").setAppFuction("劃一劃找妹子").build();
}
}
(5)客戶端——客戶
/**
* 客戶端——客戶
*/
public class Client {
public static void main(String[] args){
// 客戶:我需要一個可以搖一搖找妹子的軟體
// 產品經理:分析得出就做一個探探
// 技術主管:appName:探探,系統:ios,android功能:搖一搖找妹子
Product android = Director.create(Product.ANDROID);
Product ios = Director.create(Product.IOS);
System.out.println(android);
System.out.println(ios);
/**
* 變異來的建造者可以只需要具體建造者,抽象的不要了,指揮者也可以不要了
*/
// 程式設計師覺得太累了,工資又少,乾的最多,最後決定自己出去單幹。
WorkBuilder niubiProgremer = new WorkBuilder();
Product androidBest = niubiProgremer.setAppName("探探").setAppSystem(Product.ANDROID).setAppFuction("搖一搖,找妹子").build();
Product iosBest = niubiProgremer.setAppName("探探").setAppSystem(Product.IOS).setAppFuction("搖一搖,找妹子").build();
System.out.println("-------------------------------------------------------------------------------------");
System.out.println(androidBest);
System.out.println(iosBest);
}
}
(6)結果
Product [appName=探探, appFuction=劃一劃找妹子, appSystem=android]
Product [appName=探探, appFuction=劃一劃找妹子, appSystem=ios]
-------------------------------------------------------------------------------------
Product [appName=探探, appFuction=搖一搖,找妹子, appSystem=android]
Product [appName=探探, appFuction=搖一搖,找妹子, appSystem=ios]
6 Android原始碼中的Builder模式
在Android原始碼中,我們最常用到的Builder模式就是AlertDialog.Builder, 使用該Builder來構建複雜的AlertDialog物件。
6.1 AlertDialog時序圖分析
6.2 AlertDialog原始碼分析
在Android原始碼中,我們最常用到的Builder模式就是AlertDialog.Builder, 使用該Builder來構建複雜的AlertDialog物件。簡單示例如下 :
(1)showDialog——顯示基本的AlertDialog
// 顯示基本的AlertDialog
private void showDialog(Context context) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setIcon(R.drawable.icon);
builder.setTitle("Title");
builder.setMessage("Message");
builder.setPositiveButton("Button1",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
setTitle("點選了對話方塊上的Button1");
}
});
builder.setNeutralButton("Button2",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
setTitle("點選了對話方塊上的Button2");
}
});
builder.setNegativeButton("Button3",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
setTitle("點選了對話方塊上的Button3");
}
});
builder.create().show(); // 構建AlertDialog, 並且顯示
}
從類名可以看出這是一個Builder模式,通過Builder物件來組裝Dialog的各個部分,如:Title\Button\Message等,將Dialog的構造和表示進行分離。
結果如圖所示 :
(2)AlertDialog
// AlertDialog
public class AlertDialog extends AppCompatDialog implements DialogInterface {
// Controller, 接受Builder成員變數P中的各個引數
private AlertController mAlert;
// 建構函式
protected AlertDialog(Context context, int theme) {
this(context, theme, true);
}
// 構造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;
// 屬性省略
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() {
// 4:呼叫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)程式碼段。我們看看AlertController的實現 :
(3)AlertController
class AlertController {
private final Context mContext;
private final AppCompatDialog mDialog;
private final Window mWindow;
private CharSequence mTitle;
private CharSequence mMessage;
private ListView mListView;
private View mView;
private int mViewLayoutResId;
private int mViewSpacingLeft;
private int mViewSpacingTop;
private int mViewSpacingRight;
private int mViewSpacingBottom;
private boolean mViewSpacingSpecified = false;
private Button mButtonPositive;
private CharSequence mButtonPositiveText;
private Message mButtonPositiveMessage;
private Button mButtonNegative;
private CharSequence mButtonNegativeText;
private Message mButtonNegativeMessage;
private Button mButtonNeutral;
private CharSequence mButtonNeutralText;
private Message mButtonNeutralMessage;
private NestedScrollView mScrollView;
private int mIconId = 0;
private Drawable mIcon;
private ImageView mIconView;
private TextView mTitleView;
private TextView mMessageView;
private View mCustomTitleView;
private ListAdapter mAdapter;
private int mCheckedItem = -1;
private int mAlertDialogLayout;
private int mButtonPanelSideLayout;
private int mListLayout;
private int mMultiChoiceItemLayout;
private int mSingleChoiceItemLayout;
private int mListItemLayout;
private int mButtonPanelLayoutHint = AlertDialog.LAYOUT_HINT_NONE;
private Handler mHandler;
/**
* installContent(),極為重要,它呼叫了Window物件的setContentView,這個setContentView與Activity中一樣,實際上Activity最終也是呼叫Window物件的setContentView函式。
*/
public void installContent() {
// 設定視窗
final int contentView = selectContentView();
// 設定視窗的內容檢視佈局
mDialog.setContentView(contentView);
// 初始化AlertDialog其他子檢視的內容
setupView();// setupView
}
private int selectContentView() {
if (mButtonPanelSideLayout == 0) {
return mAlertDialogLayout;
}
if (mButtonPanelLayoutHint == AlertDialog.LAYOUT_HINT_SIDE) {
return mButtonPanelSideLayout;
}
return mAlertDialogLayout;// AlertDialog的佈局id
}
/**
* setupView()
*/
private void setupView() {
// 獲取並初始化內容區域
final View parentPanel = mWindow.findViewById(R.id.parentPanel);
final View defaultTopPanel = parentPanel.findViewById(R.id.topPanel);
final View defaultContentPanel = parentPanel.findViewById(R.id.contentPanel);
final View defaultButtonPanel = parentPanel.findViewById(R.id.buttonPanel);
final ViewGroup customPanel = (ViewGroup) parentPanel.findViewById(R.id.customPanel);
setupCustomContent(customPanel);
// 自定義內容檢視區域
final View customTopPanel = customPanel.findViewById(R.id.topPanel);
final View customContentPanel = customPanel.findViewById(R.id.contentPanel);
final View customButtonPanel = customPanel.findViewById(R.id.buttonPanel);
final ViewGroup topPanel = resolvePanel(customTopPanel, defaultTopPanel);
final ViewGroup contentPanel = resolvePanel(customContentPanel, defaultContentPanel);
final ViewGroup buttonPanel = resolvePanel(customButtonPanel, defaultButtonPanel);
setupContent(contentPanel);
setupButtons(buttonPanel);
setupTitle(topPanel);
final boolean hasCustomPanel = customPanel != null
&& customPanel.getVisibility() != View.GONE;
final boolean hasTopPanel = topPanel != null
&& topPanel.getVisibility() != View.GONE;
final boolean hasButtonPanel = buttonPanel != null
&& buttonPanel.getVisibility() != View.GONE;
// Only display the text spacer if we don't have buttons.
if (!hasButtonPanel) {
if (contentPanel != null) {
final View spacer = contentPanel.findViewById(R.id.textSpacerNoButtons);
if (spacer != null) {
spacer.setVisibility(View.VISIBLE);
}
}
}
if (hasTopPanel) {
if (mScrollView != null) {
mScrollView.setClipToPadding(true);
}
}
// Update scroll indicators as needed.
if (!hasCustomPanel) {
final View content = mListView != null ? mListView : mScrollView;
if (content != null) {
final int indicators = (hasTopPanel ? ViewCompat.SCROLL_INDICATOR_TOP : 0)
| (hasButtonPanel ? ViewCompat.SCROLL_INDICATOR_BOTTOM : 0);
setScrollIndicators(contentPanel, content, indicators,
ViewCompat.SCROLL_INDICATOR_TOP | ViewCompat.SCROLL_INDICATOR_BOTTOM);
}
}
final ListView listView = mListView;
if (listView != null && mAdapter != null) {
listView.setAdapter(mAdapter);
final int checkedItem = mCheckedItem;
if (checkedItem > -1) {
listView.setItemChecked(checkedItem, true);
listView.setSelection(checkedItem);
}
}
}
/**
* AlertController.AlertParams 持有AlertController的所有屬性,在呼叫builder裡的設定屬性方法時,就是給AlertController.AlertParams做一個快取。
*/
public static class AlertParams {
public final Context mContext;
public final LayoutInflater mInflater;
public int mIconId = 0;
public Drawable mIcon;
public int mIconAttrId = 0;
public CharSequence mTitle;
public View mCustomTitleView;
public CharSequence mMessage;
public CharSequence mPositiveButtonText;
public DialogInterface.OnClickListener mPositiveButtonListener;
public CharSequence mNegativeButtonText;
public DialogInterface.OnClickListener mNegativeButtonListener;
public CharSequence mNeutralButtonText;
public DialogInterface.OnClickListener mNeutralButtonListener;
public boolean mCancelable;
public DialogInterface.OnCancelListener mOnCancelListener;
public DialogInterface.OnDismissListener mOnDismissListener;
public DialogInterface.OnKeyListener mOnKeyListener;
public CharSequence[] mItems;
public ListAdapter mAdapter;
public DialogInterface.OnClickListener mOnClickListener;
public int mViewLayoutResId;
public View mView;
public int mViewSpacingLeft;
public int mViewSpacingTop;
public int mViewSpacingRight;
public int mViewSpacingBottom;
public boolean mViewSpacingSpecified = false;
public boolean[] mCheckedItems;
public boolean mIsMultiChoice;
public boolean mIsSingleChoice;
public int mCheckedItem = -1;
public DialogInterface.OnMultiChoiceClickListener mOnCheckboxClickListener;
public Cursor mCursor;
public String mLabelColumn;
public String mIsCheckedColumn;
public boolean mForceInverseBackground;
public AdapterView.OnItemSelectedListener mOnItemSelectedListener;
public OnPrepareListViewListener mOnPrepareListViewListener;
public boolean mRecycleOnMeasure = true;
public AlertParams(Context context) {
mContext = context;
mCancelable = true;
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
/**
* 最主要的方法是apply(),實際上就是把P中的引數挨個的設定到AlertController中, 也就是AlertDialog中的mAlert物件。從AlertDialog的各個setter方法中我們也可以看到,實際上也都是呼叫了mAlert對應的setter方法。
*/
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);
}
// 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);
}
} else if (mViewLayoutResId != 0) {
dialog.setView(mViewLayoutResId);
}
/*
dialog.setCancelable(mCancelable);
dialog.setOnCancelListener(mOnCancelListener);
if (mOnKeyListener != null) {
dialog.setOnKeyListener(mOnKeyListener);
}
*/
}
}
}
(5)呼叫create()和show()方法(該函式在Dialog中)
new AlertDialog.Builder(this).setTitle("標題").setIcon(R.mipmap.ic_launcher).setMessage("測試").create().show();
public class Dialog implements DialogInterface.... {
public void create() {
if (!mCreated) {
dispatchOnCreate(null);
}
}
/**
* show()主要作用:
* (1)通過dispatchOnCreate函式來呼叫AlertDialog的onCreate函式;
* (2)然後呼叫AlertDialog的onStart函式
* (3)最後將Dialog的DecorView新增到WindowManaget中。
*/
public void show() {
if (mShowing) {
if (mDecor != null) {
if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);
}
mDecor.setVisibility(View.VISIBLE);
}
return;
}
mCanceled = false;
// 1.onCreate呼叫
if (!mCreated) {
dispatchOnCreate(null);// dispatchOnCreate
}
// 2.onStart
onStart();
// 3.獲取DecorView
mDecor = mWindow.getDecorView();
// 程式碼省略
// 4.獲取佈局引數
WindowManager.LayoutParams l = mWindow.getAttributes();
if ((l.softInputMode
& WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
WindowManager.LayoutParams nl = new WindowManager.LayoutParams();
nl.copyFrom(l);
nl.softInputMode |=
WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
l = nl;
}
try {
// 5.將mDecor新增到WindowManager中
mWindowManager.addView(mDecor, l);
mShowing = true;
// 傳送一個顯示Dialog的訊息
sendShowMessage();
} finally {
}
}
// dispatchOnCreate()
void dispatchOnCreate(Bundle savedInstanceState) {
if (!mCreated) {
onCreate(savedInstanceState);// 接著看AlertDialog的onCreate函式
mCreated = true;
}
}
}
(6)AlertDialog的onCreate方法
public class AlertDialog extends AppCompatDialog implements DialogInterface {
private final AlertController mAlert;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);// Dialog的onCreate空實現
mAlert.installContent();// 呼叫了AlertController的installContent()方法
}
}
(7)AlertController的installContent()和setupView()方法
class AlertController {
private int mAlertDialogLayout;
// 建構函式
public AlertController(Context context, AppCompatDialog di, Window window) {
mContext = context;
mDialog = di;
mWindow = window;
mHandler = new ButtonHandler(di);
final TypedArray a = context.obtainStyledAttributes(null, R.styleable.AlertDialog,R.attr.alertDialogStyle, 0);
// AlertDialog的佈局id,也就是AlertDialog_android_layout.xml的佈局
mAlertDialogLayout = a.getResourceId(R.styleable.AlertDialog_android_layout, 0);
mButtonPanelSideLayout = a.getResourceId(R.styleable.AlertDialog_buttonPanelSideLayout, 0);
mListLayout = a.getResourceId(R.styleable.AlertDialog_listLayout, 0);
mMultiChoiceItemLayout = a.getResourceId(R.styleable.AlertDialog_multiChoiceItemLayout, 0);
mSingleChoiceItemLayout = a
.getResourceId(R.styleable.AlertDialog_singleChoiceItemLayout, 0);
mListItemLayout = a.getResourceId(R.styleable.AlertDialog_listItemLayout, 0);
a.recycle();
/* We use a custom title so never request a window title */
di.supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
}
/**
* installContent(),極為重要,它呼叫了Window物件的setContentView,這個setContentView與Activity中一樣,實際上Activity最終也是呼叫Window物件的setContentView函式。
*/