天天看點

android源碼分析-Dialog

今天給大家介紹android -Dialog源碼分析

Dialog 是所有對話框的基類,例如AlertDialog,我們要深入了解指導Dialog的用法,邏輯,必須要把Dilaog弄清楚,下面首先我們來看下Google對Dialog的類描述:
 /**
           
  • Base class for Dialogs.

    Note: Activities provide a facility to manage the creation, saving and

  • restoring of dialogs. See {@link Activity#onCreateDialog(int)},
  • {@link Activity#onPrepareDialog(int, Dialog)},
  • {@link Activity#showDialog(int)}, and {@link Activity#dismissDialog(int)}. If
  • these methods are used, {@link #getOwnerActivity()} will return the Activity
  • that managed this dialog.

    Often you will want to have a Dialog display on top of the current

  • input method, because there is no reason for it to accept text. You can
  • do this by setting the {@link WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM
  • WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM} window flag (assuming
  • your Dialog takes input focus, as it the default) with the following code:
  • getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
  • WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);</pre>
               

*/

通過上面的描述,我們知道有2點重要的資訊

1、Acitivity給我們提供了建立,預建立,展示,取消對話框的架構,在這個架構中我們不必擔心額外的異常,這寫方法包括:

onCreateDialog(int) :建立Dialog過程中要回調

onPrepareDialog(int, Dialog):準備建立對話框過程中要回調

showDialog(int):展示對話框

dismissDialog(int):取消對話框

android源碼分析-Dialog

這裡很簡單,大家可以自行閱讀源碼

2、WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM 這個是WindowManager裡面的flag标志位,當setFlag(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM );之後WindowManager中的EditText自動擷取控件,将會獲得焦點,有個相反的Flag就是,WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,設定之後WindowManager中的視圖将得不到任務焦點。

接下來我看看Dialog的構造方法:
           
public Dialog(Context context, int theme) {
        this(context, theme, true);
    }

    Dialog(Context context, int theme, boolean createContextThemeWrapper) {
        if (createContextThemeWrapper) {
            if (theme == 0) {
                TypedValue outValue = new TypedValue();
                context.getTheme().resolveAttribute(com.android.internal.R.attr.dialogTheme,
                        outValue, true);
                theme = outValue.resourceId;
            }
            mContext = new ContextThemeWrapper(context, theme);
        } else {
            mContext = context;
        }

        mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
        Window w = PolicyManager.makeNewWindow(mContext);
        mWindow = w;
        w.setCallback(this);
        w.setOnWindowDismissedCallback(this);
        w.setWindowManager(mWindowManager, null, null);
        w.setGravity(Gravity.CENTER);
        mListenersHandler = new ListenersHandler(this);
    }
           

Dialog的另外兩個構造方法最終都要調用的這個方法,隻不過在第三個參數為True,

public Dialog(Context context) {
        this(context, 0, true);
    }
           
public Dialog(Context context, int theme) {
        this(context, theme, true);
    }
           

,是以我們先來分析下這個構造方法,首先看到了這個createContextThemeWrapper 參數,這個參數為true的話 dialog會查找系統的主題資源檔案id,來初始化context。

如果theme==0,那麼就查找theme,

if (theme == 0) { TypedValue outValue = new TypedValue(); context.getTheme().resolveAttribute(com.android.internal.R.attr.dialogTheme, outValue, true); theme = outValue.resourceId; }

這裡有個類TypedValue ,這個類主要是動态的表示和存儲不同類型資料的容器,例如,系統資源resourceId,density,data,各種機關轉化,例如,sp,dip,px,等等,這個類裡面有個我們常用的方法:

public static float applyDimension(int unit, float value, DisplayMetrics metrics) { switch (unit) { case COMPLEX_UNIT_PX: return value; case COMPLEX_UNIT_DIP: return value * metrics.density; case COMPLEX_UNIT_SP: return value * metrics.scaledDensity; case COMPLEX_UNIT_PT: return value * metrics.xdpi * (1.0f/72); case COMPLEX_UNIT_IN: return value * metrics.xdpi; case COMPLEX_UNIT_MM: return value * metrics.xdpi * (1.0f/25.4f); } return 0; }

來實作不同機關下的轉化。

然後下面有這麼一句context.getTheme().resolveAttribute(com.android.internal.R.attr.dialogTheme,

outValue, true);

這句裡面其實是在查找resourceId,最後指派到TypedValue 裡面,Android系統裡面系統資源的查找主要有2個類來實作一個是,Resources,和AssetManager,他們的主要差別是,Resources主要是通過資源id來查找資源,例如各種getText(int id),getDrawable(int id),getXml…都是通過id來查找的,而AssetManager,則是通過檔案名字來查找的,

mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
           

獲得視窗管理者,負責對視窗中添加視圖和移除視圖,

Window w = PolicyManager.makeNewWindow(mContext);

           

在這裡建立了一個視窗類,我們可以看到這裡有個PolicyManager類,這其實是個建立視窗的工廠類,我們看看源碼:

public final class PolicyManager {
30    private static final String POLICY_IMPL_CLASS_NAME =
31        "com.android.internal.policy.impl.Policy";
32
33    private static final IPolicy sPolicy;
34
35    static {
36        // Pull in the actual implementation of the policy at run-time
37        try {
38            Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
39            sPolicy = (IPolicy)policyClass.newInstance();
40        } catch (ClassNotFoundException ex) {
41            throw new RuntimeException(
42                    POLICY_IMPL_CLASS_NAME + " could not be loaded", ex);
43        } catch (InstantiationException ex) {
44            throw new RuntimeException(
45                    POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);
46        } catch (IllegalAccessException ex) {
47            throw new RuntimeException(
48                    POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);
49        }
50    }
51
52    // Cannot instantiate this class
53    private PolicyManager() {}
54
55    // The static methods to spawn new policy-specific objects
56    public static Window makeNewWindow(Context context) {
57        return sPolicy.makeNewWindow(context);
58    }
59
60    public static LayoutInflater makeNewLayoutInflater(Context context) {
61        return sPolicy.makeNewLayoutInflater(context);
62    }
63
64    public static WindowManagerPolicy makeNewWindowManager() {
65        return sPolicy.makeNewWindowManager();
66    }
67
68    public static FallbackEventHandler More ...makeNewFallbackEventHandler(Context context) {
69        return sPolicy.makeNewFallbackEventHandler(context);
70    }
71}
           

在這個類裡面,我們首先會看到IPolicy,IPolicy是一個代理接口,

public interface IPolicy {
32    public Window makeNewWindow(Context context);
33
34    public LayoutInflater makeNewLayoutInflater(Context context);
35
36    public WindowManagerPolicy makeNewWindowManager();
37
38    public FallbackEventHandler makeNewFallbackEventHandler(Context context);
39}
           

這個接口有三個方法,makeNewWindow()建立一個新視窗,makeNewLayoutInflater建立LayoutInflater ,makeNewWindowManager 視窗管理代理,makeNewFallbackEventHandler,建立FallbackEventHandler 手機回退處理接口,FallbackEventHandler 的實作類有PhoneFallbackEventHandler

IPolicy 在static語句塊中用反射執行個體化了com.android.internal.policy.impl.Policy,然後通過這個執行個體來建立視窗操作。

com.android.internal.policy.impl.Policy,通過這個我們指導IPolicy 的實作類是Policy ,我們看看Policy

public class More ...Policy implements IPolicy {
35    private static final String TAG = "PhonePolicy";
36
37    private static final String[] preload_classes = {
38        "com.android.internal.policy.impl.PhoneLayoutInflater",
39        "com.android.internal.policy.impl.PhoneWindow",
40        "com.android.internal.policy.impl.PhoneWindow$1",
41        "com.android.internal.policy.impl.PhoneWindow$DialogMenuCallback",
42        "com.android.internal.policy.impl.PhoneWindow$DecorView",
43        "com.android.internal.policy.impl.PhoneWindow$PanelFeatureState",
44        "com.android.internal.policy.impl.PhoneWindow$PanelFeatureState$SavedState",
45    };
46
47    static {
48        // For performance reasons, preload some policy specific classes when
49        // the policy gets loaded.
50        for (String s : preload_classes) {
51            try {
52                Class.forName(s);
53            } catch (ClassNotFoundException ex) {
54                Log.e(TAG, "Could not preload class for phone policy: " + s);
55            }
56        }
57    }
58
59    public Window More ...makeNewWindow(Context context) {
60        return new PhoneWindow(context);
61    }
62
63    public LayoutInflater More ...makeNewLayoutInflater(Context context) {
64        return new PhoneLayoutInflater(context);
65    }
66
67    public WindowManagerPolicy More ...makeNewWindowManager() {
68        return new PhoneWindowManager();
69    }
70
71    public FallbackEventHandler More ...makeNewFallbackEventHandler(Context context) {
72        return new PhoneFallbackEventHandler(context);
73    }
74}在static裡面會提前加載會用到的類,到此我們知道了PolicyManager.makeNewWindow(mContext);最終是是Policy通過直接執行個體化了PhoneWindow來生成的,android中的window類是抽象類,定義了視窗的行為,和部分實作,他們的實作類為PhoneWindow,在這裡面我可以猜到,android系統要适配平闆的話應該是有PadWindow,如果是手表的話估計有WatchWindow,具體的PhoneWindow的邏輯會在以後的文章深入分析。
	繼續接到前面講,mWindow = w;設定dialog的mWindow ,w.setCallback(this);設定dialog中mWindow 的回調實作,這樣dialog就具有類window的行為,比如我們按back dialog就會關閉等等。
	w.setOnWindowDismissedCallback(this);設定dialog關閉行為回調,
	 mListenersHandler = new ListenersHandler(this);執行個體化ListenersHandler,ListenersHandler是繼承Handler,負責dialog的show,cancel,dismiss的消息接受回調,具體Handler,機制可自行查閱資料,這裡不在詳述。
	 下面我們來看看dialog是如何展示到Activity中的。
	 首先我們看看show()。
	 
``
 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;
        
        if (!mCreated) {
            dispatchOnCreate(null);
        }

        onStart();
        mDecor = mWindow.getDecorView();

        if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
            final ApplicationInfo info = mContext.getApplicationInfo();
            mWindow.setDefaultIcon(info.icon);
            mWindow.setDefaultLogo(info.logo);
            mActionBar = new WindowDecorActionBar(this);
        }

        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 {
            mWindowManager.addView(mDecor, l);
            mShowing = true;
    
            sendShowMessage();
        } finally {
        }
    }
    
    /**
     * Hide the dialog, but do not dismiss it.
     */
    public void hide() {
        if (mDecor != null) {
            mDecor.setVisibility(View.GONE);
        }
    }
           

邏輯還是很清晰的,首先判斷根據if (mShowing) {

if (mDecor != null) {

if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {

mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);

}

mDecor.setVisibility(View.VISIBLE);

}

return;

}

判斷是否dialog正在展示,如果展示的話就 判斷dialog是否有actionbar,如果有的話就初始化,然後顯示,你可能要問actionbar是什麼東西,就是最上面有各icon和名字的标題界面,我們的layout檔案初始化的時候就能看到那個東西,dialog也是可以設定的。

然後mCanceled = false;,因為正要展示dialog,當然不能被取消了啊,隻有在展示完了之後才可以消失操作,

mDecor = mWindow.getDecorView();設定dialog的mDecor 屬性,每個視窗都有一個根視圖,是以mDecor 也是dialog的根視圖,dialog上的View都是mDecor 的子視圖,這個概念在Activity中也存在。

if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {

final ApplicationInfo info = mContext.getApplicationInfo();

mWindow.setDefaultIcon(info.icon);

mWindow.setDefaultLogo(info.logo);

mActionBar = new WindowDecorActionBar(this);

}

如果dialog設定了FEATURE_ACTION_BAR,那麼給actionbar設定預設的icon和logo,這裡有ApplicationInfo類,這個類描述了Mainifest.xml的配置資訊,這個類也可以通過context.getPackageManager();進一步獲得,icon,logo就是我們在<application 。。。節點下面配置的資訊啦,

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;
        }
           

從mWindow獲得dialog的布局屬性資訊,然後配置SOFT_INPUT_IS_FORWARD_NAVIGATION,這個Flag就是讓視窗顯示在最前面,這個資訊一般有系統完成設定,l.softInputMode

& WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0

在android系統中有很多的flag都是通過這種方式來存儲和判斷的,一來節省記憶體提高速度,二來友善邏輯操作,文法優美,在上面這斷話是通過按位與操作符,來判SOFT_INPUT_IS_FORWARD_NAVIGATION是否存在,==0則不存在,具體為什麼,稍微想下很好了解

WindowManager.LayoutParams 是描述視窗的位置,水準、垂直權重、視窗類型Type、視窗UI互動等衆多配置資訊的類Flag、例如FLAG_DIM_BEHIND,這個flag控制Window背景是否變暗(dialog就有這個屬性),FLAG_NOT_FOCUSABLE設定window不要擷取焦點,這樣裡面的editText不可以輸入等等,我以後會在其它文章詳細介紹。

try {
            mWindowManager.addView(mDecor, l);
            mShowing = true;
    
            sendShowMessage();
        } finally {
        }
           

終于到了dialog的展示語句了,mWindowManager将mDecor視圖添加如了視窗中,那麼dialog就展示在了我們面前,mWindowManager的實作類是WindowManagerImpl,是以addView()是在WindowManagerImpl中實作的,那麼我們看看是怎麼實作的。

82     @Override
83     public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
84         applyDefaultToken(params);
85         mGlobal.addView(view, params, mDisplay, mParentWindow);
86     }
           

applyDefaultToken(params); 如果IBinder為null則設定預設的IBinder,IBinder是FrameWork和Wms的通信橋梁,具體怎麼實作的我還在繼續學習研究,mGlobal.addView(view, params, mDisplay, mParentWindow);真正的添加動作是

public final class WindowManagerImpl implements WindowManager { 49 private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance(); 50 private final Display mDisplay; 51 private final Window mParentWindow; 52 53 private IBinder mDefaultToken;

WindowManagerGlobal完成的,下面這張圖看以看出WindowManagerGlobal的位置

android源碼分析-Dialog

具體WindowManagerGlobal中addView是怎麼添加的,以後的文章會詳細講解。

sendShowMessage(); 這個方法是通知事件的回調例如展示,取消等等,Message.obtain(mShowMessage).sendToTarget();發送各種消息。

下面我們來分析下dismiss(),dialog如何消失的,

public void dismiss() {
        if (Looper.myLooper() == mHandler.getLooper()) {
            dismissDialog();
        } else {
            mHandler.post(mDismissAction);
        }
    }

    void dismissDialog() {
        if (mDecor == null || !mShowing) {
            return;
        }

        if (mWindow.isDestroyed()) {
            Log.e(TAG, "Tried to dismissDialog() but the Dialog's window was already destroyed!");
            return;
        }

        try {
            mWindowManager.removeViewImmediate(mDecor);
        } finally {
            if (mActionMode != null) {
                mActionMode.finish();
            }
            mDecor = null;
            mWindow.closeAllPanels();
            onStop();
            mShowing = false;

            sendDismissMessage();
        }
    }
           

if (Looper.myLooper() == mHandler.getLooper()) {

dismissDialog();

} else {

mHandler.post(mDismissAction);

}

在主線程和子線程都可以安全關閉對話框

void dismissDialog() {
        if (mDecor == null || !mShowing) {
            return;
        }
           

如果視圖不存在或者根本就沒有展示出來,就不用管了,

if (mWindow.isDestroyed()) {
            Log.e(TAG, "Tried to dismissDialog() but the Dialog's window was already destroyed!");
            return;
        }
           

如果視窗被銷毀了 也就不管了

try {
            mWindowManager.removeViewImmediate(mDecor);
        } finally {
            if (mActionMode != null) {
                mActionMode.finish();
            }
            mDecor = null;
            mWindow.closeAllPanels();
            onStop();
            mShowing = false;

            sendDismissMessage();
        }
           

mWindowManager立刻執行移除視圖操作,finally中最後處理操作,回調onStop(),調用dialog消失回調接口,更改mShowing狀态。

剩下的其它方法有一部分是Window.Callback,

KeyEvent.Callback, OnCreateContextMenuListener, Window.OnWindowDismissedCallback

接口中的方法,

還有一部分是PhoneWindow中的方法

接下來我來看看Dialog的一個實作類AlertDialog,警告對話框。

AlertDialog

AlertDialog是一個典型的建造者模式,通過Builder來構造AlertDialog,在構造過程中是通過AlertController來管理視圖和屬性的.下面是一個典型的展示代碼:

new AlertDialog.Builder(self)
 .setTitle("标題") 
 .setMessage("簡單消息框")
 	.setPositiveButton("确定", null)
 	.show();
           

AlertDialog 可以展示Text文本對話框,帶有EditText對話框,單選對話框,多選對話框,清單對話。

AlertDialog 的構造方法如下

protected AlertDialog(Context context, boolean cancelable, OnCancelListener cancelListener) {
        super(context, resolveDialogTheme(context, 0));
        mWindow.alwaysReadCloseOnTouchAttr();
        setCancelable(cancelable);
        setOnCancelListener(cancelListener);
        mAlert = new AlertController(context, this, getWindow());
    }
           

首先我們看到了resolveDialogTheme(context, 0) 方法,我們看看,

static int resolveDialogTheme(Context context, int resid) {
        if (resid == THEME_TRADITIONAL) {
            return com.android.internal.R.style.Theme_Dialog_Alert;
        } else if (resid == THEME_HOLO_DARK) {
            return com.android.internal.R.style.Theme_Holo_Dialog_Alert;
        } else if (resid == THEME_HOLO_LIGHT) {
            return com.android.internal.R.style.Theme_Holo_Light_Dialog_Alert;
        } else if (resid == THEME_DEVICE_DEFAULT_DARK) {
            return com.android.internal.R.style.Theme_DeviceDefault_Dialog_Alert;
        } else if (resid == THEME_DEVICE_DEFAULT_LIGHT) {
            return com.android.internal.R.style.Theme_DeviceDefault_Light_Dialog_Alert;
        } else if (resid >= 0x01000000) {   // start of real resource IDs.
            return resid;
        } else {
            TypedValue outValue = new TypedValue();
            context.getTheme().resolveAttribute(com.android.internal.R.attr.alertDialogTheme,
                    outValue, true);
            return outValue.resourceId;
        }
    }
           

咋這個方法中主要是按照條件設定主題,在AlertDialog中系統給我們提供了,幾種主題可以選擇,

THEME_TRADITIONAL:透明主題

THEME_HOLO_DARK:全黑色主題

THEME_HOLO_LIGHT:全亮色主題

THEME_DEVICE_DEFAULT_DARK:裝置預設黑色主題

THEME_DEVICE_DEFAULT_LIGHT:裝置預設亮色主題

在這裡面我們看到了HOLO字樣,這個其實是Android 系統在4.0後推出的新主題風格,這種主題分為黑色和亮色系和混合系列,在很多的控件都有這種主題可以選擇,dialog隻是其中一種而已。

else if (resid >= 0x01000000) {   // start of real resource IDs.
            return resid;
        }這裡寫代碼片
           

如果不是系統的選擇,那麼就使用使用者自己定義的主題,

else {
            TypedValue outValue = new TypedValue();
            context.getTheme().resolveAttribute(com.android.internal.R.attr.alertDialogTheme,
                    outValue, true);
            return outValue.resourceId;
        }
           

如果都不是上面的選擇,也不是自定義的,那麼系統将給出預設的主題,com.android.internal.R.attr.alertDialogTheme,這種屬性主題最終通過AssetManager來查找的mAssets.getThemeValue(mTheme, resid, outValue, resolveRefs);然後将查找的内容存到TypedValue中,這個在上面的内容中也詳細的講過了。

接下來我們看到了mWindow.alwaysReadCloseOnTouchAttr();這個我們看名字也大概猜到了什麼作用呢,應該是在點選視窗外面關閉視窗,的确是這樣,這個是在Window中的一個抽象方法,具體的實作是PhoneWindow中的,在PhoneWindow中的實作是很簡單的

public void More ...alwaysReadCloseOnTouchAttr() {
3548        mAlwaysReadCloseOnTouchAttr = true;
3549    }
           

你看就是指派而已,那麼最終這個屬性是在哪裡用呢 我們繼續跟蹤,

if (mAlwaysReadCloseOnTouchAttr || getContext().getApplicationInfo().targetSdkVersion
3368                >= android.os.Build.VERSION_CODES.HONEYCOMB) {
3369            if (a.getBoolean(
3370                    R.styleable.Window_windowCloseOnTouchOutside,
3371                    false)) {
3372                setCloseOnTouchOutsideIfNotSet(true);
3373            }
3374        }
           

我們看到了,如果mAlwaysReadCloseOnTouchAttr ==true或者版本大于等于VERSION_CODES.HONEYCOMB(蜂巢,就是3…0版本啦),然後裡面繼續判斷如果視窗風格設定Window_windowCloseOnTouchOutside了就setCloseOnTouchOutsideIfNotSet(true);而setCloseOnTouchOutsideIfNotSet(true) 方法是在Window中實作的

public void More ...setCloseOnTouchOutsideIfNotSet(boolean close) {
889         if (!mSetCloseOnTouchOutside) {
890             mCloseOnTouchOutside = close;
891             mSetCloseOnTouchOutside = true;
892         }
893     }
           

主要就是設定mCloseOnTouchOutside 和mSetCloseOnTouchOutside 為true。

構造方法接下來就是設定回調,和執行個體化AlertController了。

然後我們縱觀整個AlertDialog,2點重要發現。

第一、AlertDialog隻是個殼,真正的實作和管理控制都是通過AlertController來實作的,AlertController中的還有個AlertParams 類,AlertController主要是管理了相關的View和屬性變量,很多方法都是對視圖的初始化和設定,AlertParams 負責初始化工作例如裡面有apply(AlertController dialog) 這個是初始化AlertDialog的方法,在AlertController的create中主要是它來實作的,

public void More ...apply(AlertController dialog) {
949             if (mCustomTitleView != null) {
950                 dialog.setCustomTitle(mCustomTitleView);
951             } else {
952                 if (mTitle != null) {
953                     dialog.setTitle(mTitle);
954                 }
955                 if (mIcon != null) {
956                     dialog.setIcon(mIcon);
957                 }
958                 if (mIconId != 0) {
959                     dialog.setIcon(mIconId);
960                 }
961                 if (mIconAttrId != 0) {
962                     dialog.setIcon(dialog.getIconAttributeResId(mIconAttrId));
963                 }
964             }
965             if (mMessage != null) {
966                 dialog.setMessage(mMessage);
967             }
968             if (mPositiveButtonText != null) {
969                 dialog.setButton(DialogInterface.BUTTON_POSITIVE, mPositiveButtonText,
970                         mPositiveButtonListener, null);
971             }
972             if (mNegativeButtonText != null) {
973                 dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mNegativeButtonText,
974                         mNegativeButtonListener, null);
975             }
976             if (mNeutralButtonText != null) {
977                 dialog.setButton(DialogInterface.BUTTON_NEUTRAL, mNeutralButtonText,
978                         mNeutralButtonListener, null);
979             }
980             if (mForceInverseBackground) {
981                 dialog.setInverseBackgroundForced(true);
982             }
983             // For a list, the client can either supply an array of items or an
984             // adapter or a cursor
985             if ((mItems != null) || (mCursor != null) || (mAdapter != null)) {
986                 createListView(dialog);
987             }
988             if (mView != null) {
989                 if (mViewSpacingSpecified) {
990                     dialog.setView(mView, mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight,
991                             mViewSpacingBottom);
992                 } else {
993                     dialog.setView(mView);
994                 }
995             } else if (mViewLayoutResId != 0) {
996                 dialog.setView(mViewLayoutResId);
997             }
998 
999             /*
1000            dialog.setCancelable(mCancelable);
1001            dialog.setOnCancelListener(mOnCancelListener);
1002            if (mOnKeyListener != null) {
1003                dialog.setOnKeyListener(mOnKeyListener);
1004            }
1005            */
1006        }
           

例如還有,createListView(final AlertController dialog) 建立ListView,就是AlertDialog中的多選,單選,清單視圖的初始化,是以AlertController結構還是很簡單的。

第二、點就是AlertDialog一般都是通過内部類Builder來執行個體化的,通過點文法風格友善的寫,AlertDialog的建造着模式是Android中很經典的。Builder 有create和show 和一大堆配置方法,寫法結構很值得我們學習,建造者模式在Android其他地方也可以發現,例如BitmapFactory 圖檔工廠,ViewPropertyAnimator View的屬性動畫,

AlertDialog的介紹就是這麼多了,AlertDialog很方面的實作警告對話框,也就是說僅僅是警告對話框,很多時候他的布局是死的,遠遠不能滿足我們需求,我們都會繼承Dialog來自定義自己的dialog,還可以對設定的View設定動畫,提高使用者體驗,那就發揮大家的想象吧!