1, 概述
Dialog(对话框)不仅可以显示信息,还可以和Activity界面进行交互,这种交互是阻塞式的. 继承Dialog的类有好几种,主要以AlertDialog为例来分析一下具体的原理。
2 实现
Dialog依附于Activity来实现,一般在acitivty中显示,因为Dialog的交互是阻塞式的,所以最好另开一线程,
1,首先调用showDialog方法
showDialog(R.id.dialog_export_confirmation);
// 因为一个activity中可以显示多个dialog,所以利用id对dialog进行标记。
2,在onCreateDialog方法中new一个dialog对象。
protected Dialog onCreateDialog(int id, Bundle bundle) {
switch (id) {
case R.id.dialog_export_confirmation: {
return new AlertDialog.Builder(this)
.setTitle(R.string.confirm_export_title)
.setMessage(getString(R.string.confirm_export_message, mTargetFileName))
.setPositiveButton(android.R.string.ok,this)
.setNegativeButton(android.R.string.cancel, this)
.setOnCancelListener(this)
.create();
}
~~~
return super.onCreateDialog(id, bundle);
}
3,选择性的实现onPrepareDialog方法,
protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {
if (id == R.id.dialog_export_confirmation) {
((AlertDialog)dialog).setMessage(
getString(R.string.confirm_export_message, mTargetFileName));
} else {
super.onPrepareDialog(id, dialog, args);
}
}
4,实现2个接口,当然也可以在创建dialog时实现。
class ~~~Activity extends Activity implements
DialogInterface.OnClickListener, DialogInterface.OnCancelListener{
public void onClick(DialogInterface dialog, int which) {
~~~
}
@Override
public void onCancel(DialogInterface dialog) {
~~~
}
3 源码解析
3.1 对象创建
创建流程如下,
首先看Activity中的showDialog方法,
public final boolean showDialog(int id, Bundle args) {
if (mManagedDialogs == null) {
mManagedDialogs = new SparseArray<ManagedDialog>(); // 为了便于管理
}
ManagedDialog md = mManagedDialogs.get(id);
if (md == null) {
md = new ManagedDialog();
md.mDialog = createDialog(id, null, args);
if (md.mDialog == null) {
return false;
}
mManagedDialogs.put(id, md);
}
md.mArgs = args;
onPrepareDialog(id, md.mDialog, args);
md.mDialog.show(); // 显示
return true;
}
ManagedDialog数组是为了便于管理各个dialog,不用每次显示的时候就创建,这样可以节约资源,提高效率。
调用AlertDialog内部静态Builder的oncreate方法创建AlertDialog对象,
public AlertDialog create() {
// Context has already been wrapped with the appropriate theme.
final AlertDialog dialog = new AlertDialog(P.mContext, 0, false);
P.apply(dialog.mAlert);
dialog.setCancelable(P.mCancelable);
if (P.mCancelable) {
dialog.setCanceledOnTouchOutside(true);
}
dialog.setOnCancelListener(P.mOnCancelListener);
dialog.setOnDismissListener(P.mOnDismissListener);
if (P.mOnKeyListener != null) {
dialog.setOnKeyListener(P.mOnKeyListener);
}
return dialog;
}
AlertDialog的构造方法如下,
AlertDialog(Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
super(context, createContextThemeWrapper ? resolveDialogTheme(context, themeResId) : 0,
createContextThemeWrapper);
mWindow.alwaysReadCloseOnTouchAttr();
mAlert = new AlertController(getContext(), this, getWindow()); // 构造AlertController
}
父类Dialog的构造方法如下,
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
if (createContextThemeWrapper) {
if (themeResId == 0) {
final TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);
themeResId = outValue.resourceId;
}
mContext = new ContextThemeWrapper(context, themeResId);
} else {
mContext = context;
}
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
final Window w = new PhoneWindow(mContext); // 新建一个PhoneWindow对象。
mWindow = w;
w.setCallback(this);
w.setOnWindowDismissedCallback(this);
w.setWindowManager(mWindowManager, null, null);
w.setGravity(Gravity.CENTER);
mListenersHandler = new ListenersHandler(this);
}
3.2 显示
调用Dialog的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) { // 第一次为false
dispatchOnCreate(null); // 加载view
}
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();// type和activity完全一样
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); // 直接调用WindowManagerImpl的addView方法显示
// 测量,确定位置,绘制View
mShowing = true;
sendShowMessage();
} finally {
}
}
void dispatchOnCreate(Bundle savedInstanceState) {
if (!mCreated) {
onCreate(savedInstanceState);
mCreated = true; // 这才将mCreated置为true,加载一次就不再加载了。
}
}
public void installContent() {
/* We use a custom title so never request a window title */
mWindow.requestFeature(Window.FEATURE_NO_TITLE);
int contentView = selectContentView(); // 获取资源 mAlertDialogLayout
mWindow.setContentView(contentView); // 加载资源
setupView();
setupDecor();
}
在AlertController的构造方法中为mAlertDialogLayout赋值,
public AlertController(Context context, DialogInterface di, Window window) {
mContext = context;
mDialogInterface = di;
mWindow = window;
mHandler = new ButtonHandler(di);
TypedArray a = context.obtainStyledAttributes(null,
com.android.internal.R.styleable.AlertDialog,
com.android.internal.R.attr.alertDialogStyle, 0);
mAlertDialogLayout = a.getResourceId(com.android.internal.R.styleable.AlertDialog_layout,
com.android.internal.R.layout.alert_dialog);
mButtonPanelSideLayout = a.getResourceId(
com.android.internal.R.styleable.AlertDialog_buttonPanelSideLayout, 0);
mListLayout = a.getResourceId(
com.android.internal.R.styleable.AlertDialog_listLayout,
com.android.internal.R.layout.select_dialog);
mMultiChoiceItemLayout = a.getResourceId(
com.android.internal.R.styleable.AlertDialog_multiChoiceItemLayout,
com.android.internal.R.layout.select_dialog_multichoice);
mSingleChoiceItemLayout = a.getResourceId(
com.android.internal.R.styleable.AlertDialog_singleChoiceItemLayout,
com.android.internal.R.layout.select_dialog_singlechoice);
mListItemLayout = a.getResourceId(
com.android.internal.R.styleable.AlertDialog_listItemLayout,
com.android.internal.R.layout.select_dialog_item);
a.recycle();
}
Dialog几乎和activity一样,都是调用WindowManagerImpl的setContentView方法来加载解析资源,利用addView方法测量,确定位置,绘制View,最后将Dialog显示出来。
3.3 单击事件
1,调用AlertDialog 内部类Builder 的setPositiveButton/ setNegativeButton方法设置监听事件,
public Builder setPositiveButton(@StringRes int textId, final OnClickListener listener) {
P.mPositiveButtonText = P.mContext.getText(textId);
P.mPositiveButtonListener = listener; // 为mPositiveButtonListener赋值
return this;
}
P的定义如下,
private final AlertController.AlertParams P;
2
,
创建
AlertDialog
时
,
public void apply(AlertController dialog) {
•••
if (mPositiveButtonText != null) {
dialog.setButton(DialogInterface.BUTTON_POSITIVE, mPositiveButtonText,
mPositiveButtonListener, null);
}
if (mNegativeButtonText != null) {
dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mNegativeButtonText,
mNegativeButtonListener, null);
}
••••
}
setButton方法构造单击事件的Message对象,
public void setButton(int whichButton, CharSequence text,
DialogInterface.OnClickListener listener, Message msg) {
if (msg == null && listener != null) {
msg = mHandler.obtainMessage(whichButton, listener);
}
switch (whichButton) {
case DialogInterface.BUTTON_POSITIVE:
mButtonPositiveText = text;
mButtonPositiveMessage = msg;
break;
case DialogInterface.BUTTON_NEGATIVE:
mButtonNegativeText = text;
mButtonNegativeMessage = msg;
break;
case DialogInterface.BUTTON_NEUTRAL:
mButtonNeutralText = text;
mButtonNeutralMessage = msg;
break;
default:
throw new IllegalArgumentException("Button does not exist");
}
}
3,分发单击事件
AlertController的View.OnClickListener匿名内部类实现的onClick方法如下,
private final View.OnClickListener mButtonHandler = new View.OnClickListener() {
@Override
public void onClick(View v) {
final Message m;
if (v == mButtonPositive && mButtonPositiveMessage != null) {
m = Message.obtain(mButtonPositiveMessage);
} else if (v == mButtonNegative && mButtonNegativeMessage != null) {
m = Message.obtain(mButtonNegativeMessage);
} else if (v == mButtonNeutral && mButtonNeutralMessage != null) {
m = Message.obtain(mButtonNeutralMessage);
} else {
m = null;
}
if (m != null) {
m.sendToTarget(); // 发送Message消息
}
// Post a message so we dismiss after the above handlers are executed
mHandler.obtainMessage(ButtonHandler.MSG_DISMISS_DIALOG, mDialogInterface)
.sendToTarget();
}
};
处理消息如下,
private static final class ButtonHandler extends Handler {
// Button clicks have Message.what as the BUTTON{1,2,3} constant
private static final int MSG_DISMISS_DIALOG = 1;
private WeakReference<DialogInterface> mDialog;
public ButtonHandler(DialogInterface dialog) {
mDialog = new WeakReference<DialogInterface>(dialog);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case DialogInterface.BUTTON_POSITIVE:
case DialogInterface.BUTTON_NEGATIVE:
case DialogInterface.BUTTON_NEUTRAL:
((DialogInterface.OnClickListener) msg.obj).onClick(mDialog.get(), msg.what);
break;
case MSG_DISMISS_DIALOG:
((DialogInterface) msg.obj).dismiss(); // 关闭Dialog
}
}
}
调用DialogInterface.OnClickListener的onClick方法。
3.4 关闭
Acitivity中有个dismissDialog方法,来关闭dialog,
public final void dismissDialog(int id) {
if (mManagedDialogs == null) {
throw missingDialog(id);
}
final ManagedDialog md = mManagedDialogs.get(id);
if (md == null) {
throw missingDialog(id);
}
md.mDialog.dismiss();
}
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); // 直接移除view
} finally {
if (mActionMode != null) {
mActionMode.finish();
}
mDecor = null;
mWindow.closeAllPanels();
onStop();
mShowing = false;
sendDismissMessage();
}
}
当然,可以直接调用dialog的cancel方法来关闭。
4 Dialog
当然,除了系统的Dialog风格之外,也可以自己定义dialog,可以通过set~~~方法来实现,
setContentView | 设置自定义View |
setTitle | 设置标题 |
~~~ |
|
show | 显示 |
cancel | 关闭 |