大家都知道监听 Dialog 消失事件常常是要重写 onDismiss()
或者 onCancle()
方法,有时候为了让 Dialog 主动消失,我们会调用 Dialog 的 dismiss()
和 cancle()
方法。而一个 Dialog 的消失有三种方式可以设置:
builder.setNegativeButton(android.R.string.cancel, null);
builder.create().setCanceledOnTouchOutside(true);
但是这三种方法是怎样让 Dialog 消失的?这个问题的答案通过查看相关源码来一探究竟。
查看 Dialog 的 Click 的响应事件可以看到:
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();
}
// Post a message so we dismiss after the above handlers are executed
mHandler.obtainMessage(ButtonHandler.MSG_DISMISS_DIALOG, mDialogInterface)
.sendToTarget();
}
};
由于默认的方式没有设置 mButtonNegativeMessage,所以默认的 Cancel 按键执行的是
mHandler.obtainMessage(ButtonHandler.MSG_DISMISS_DIALOG, mDialogInterface)
.sendToTarget();
从对应的 Handler 可以看到,这个 case 下,所执行的是 dismiss() 方法。
case MSG_DISMISS_DIALOG:
((DialogInterface) msg.obj).dismiss();
当设置为 true 的时候可以看到这个 Dialog 就被设置为 cancelable 的。
public void setCanceledOnTouchOutside(boolean cancel) {
if (cancel && !mCancelable) {
mCancelable = true;
}
mWindow.setCloseOnTouchOutside(cancel);
}
可以看到 Dialog 范围外的 Touch 事件是由 Window 判断的。
public void setCloseOnTouchOutside(boolean close) {
mCloseOnTouchOutside = close;
mSetCloseOnTouchOutside = true;
}
通过查看 mCloseOnTouchOutside 的引用可以找到:
public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
if (mCloseOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN
&& isOutOfBounds(context, event) && peekDecorView() != null) {
return true;
}
return false;
}
可以看到确实是由 Window 类响应了这个点击事件。查看该函数的引用则找到 Dialog 里的方法:
public boolean onTouchEvent(MotionEvent event) {
if (mCancelable && mShowing && mWindow.shouldCloseOnTouch(mContext, event)) {
cancel();
return true;
}
return false;
}
很明显,cancel()
方法就是要找的答案。
public void cancel() {
if (!mCanceled && mCancelMessage != null) {
mCanceled = true;
// Obtain a new message so this dialog can be re-used
Message.obtain(mCancelMessage).sendToTarget();
}
dismiss();
}
可以看到这和直接调用 dismiss()
方法是类似的,但是是先发送了一个 cancle 消息再调用 dismiss()
。
点击 Back 键就很简单,一目了然。
public void onBackPressed() {
if (mCancelable) {
cancel();
}
}
同样的调用了 cancel 方法。
cancel()
方法先发送 cancel 消息,再 dismiss()
,而 dismiss()
就是发送消息通知 WindowManager 立即移除 View, 接着发送 dismiss 消息。
简单跳转之后可以看到,每一个 AlertDialog 都注册了 cencel()
和 dismiss()
的 Listener,这也就是我们常常重写 onCencel()
和 onDismiss()
这两个方法的作用。
通过查看以上三种方式的源码,可以发现:
因此要注意你需要复写的是哪个方法,如果使用默认的 Cancle 按键,复写 onCancle()
可是一点作用都没有呀。