今天給大家介紹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):取消對話框
這裡很簡單,大家可以自行閱讀源碼
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的位置
具體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設定動畫,提高使用者體驗,那就發揮大家的想象吧!