AlertDialog点击确定必定会dismiss问题

场景:

对于业务处理,需要在点击AlertDialog的确定按钮之后做判断,如果满足要求,弹窗消失,反之,在弹窗上做特殊的错误处理,弹窗不消失。

首先,弹窗部分使用原生的AlertDialog.Builder来构建的。因此,最开始我觉得这个功能只需要设置对应确定按钮即可–setPositiveButton(text, positiveClickListener);

     positiveClickListener = new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {// 逻辑判断........if (条件不满足) {return;}dialog.dismiss();}}

最开始的理解,是AlertDialog需要在点击确定的时候手动dismiss掉。所以在调用dialog.dismiss()之前进行逻辑判断,不满足条件在dismiss之前返回即可。但是后续实际测试时发现,不论何时return,只要点击了确定按钮,dialog都会dismiss。

解决方案:

先说明下解决方案:

网络上有很多说用放射的方式,实际上没有必要,只要拿到对应的弹窗确定按钮,然后手动设置点击响应即可(注意,需要在Dialog show之后进行设置);

// dialog 未对应AlertDialog实例对象;
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> {// 逻辑确认........if (不满足条件) {return;}dialog.dismiss();
}

原理解析:

从现象上看:我们对于DialogInterface.OnClickListener这个监听的设置不生效,所以得从源码看下:

AlertDialog.Builder builder = new AlertDialog.Builder(context).setView(dialogLayout).setCancelable(cancelable).setPositiveButton(positiveText, onPositiveClickListener).setNegativeButton(negativeText, onNegativeClickListener);

通常来说,对应AlertDialog的创建如上所示,使用Builder进行构建时,会先设置参数:

        public Builder(@NonNull Context context, @StyleRes int themeResId) {this.P = new AlertParams(new ContextThemeWrapper(context, AlertDialog.resolveDialogTheme(context, themeResId)));this.mTheme = themeResId;}public AlertDialog.Builder setView(View view) {this.P.mView = view;this.P.mViewLayoutResId = 0;this.P.mViewSpacingSpecified = false;return this;}public AlertDialog.Builder setPositiveButton(CharSequence text, OnClickListener listener) {this.P.mPositiveButtonText = text;this.P.mPositiveButtonListener = listener;return this;}

通过Builder进行构造,实际上只是将数据存储在其AlertParams中,后面实际起作用是在Builder.create()或Builder.show():

        public AlertDialog show() {AlertDialog dialog = this.create();dialog.show();return dialog;}public AlertDialog create() {AlertDialog dialog = new AlertDialog(this.P.mContext, this.mTheme);this.P.apply(dialog.mAlert);dialog.setCancelable(this.P.mCancelable);if (this.P.mCancelable) {dialog.setCanceledOnTouchOutside(true);}dialog.setOnCancelListener(this.P.mOnCancelListener);dialog.setOnDismissListener(this.P.mOnDismissListener);if (this.P.mOnKeyListener != null) {dialog.setOnKeyListener(this.P.mOnKeyListener);}return dialog;}

实际上对于Builder.show会在内部调用create,先创建一个AlertDialog对象,然后调用Dialog的show方法进行展示。

在create()方法中,首先创建一个AlertDialog对象,然后调用AlertParams.apply(),后面都是对Dialog监听的设置,这里没有涉及到确定/取消按钮的,所以先不深入;

在apply()中会传递一个AlertControl对象,它是在AlertDialog构造函数中被创建的:

    protected AlertDialog(@NonNull Context context, @StyleRes int themeResId) {super(context, resolveDialogTheme(context, themeResId));this.mAlert = new AlertController(this.getContext(), this, this.getWindow());}public void apply(AlertController dialog) {........if (this.mMessage != null) {dialog.setMessage(this.mMessage);}if (this.mPositiveButtonText != null || this.mPositiveButtonIcon != null) {dialog.setButton(-1, this.mPositiveButtonText, this.mPositiveButtonListener, (Message)null, this.mPositiveButtonIcon);}if (this.mNegativeButtonText != null || this.mNegativeButtonIcon != null) {dialog.setButton(-2, this.mNegativeButtonText, this.mNegativeButtonListener, (Message)null, this.mNegativeButtonIcon);}if (this.mNeutralButtonText != null || this.mNeutralButtonIcon != null) {dialog.setButton(-3, this.mNeutralButtonText, this.mNeutralButtonListener, (Message)null, this.mNeutralButtonIcon);}if (this.mItems != null || this.mCursor != null || this.mAdapter != null) {this.createListView(dialog);}........}

只看相关部分,也就是dialog.setButton(-1, this.mPositiveBu…);部分

    public void setButton(int whichButton, CharSequence text, android.content.DialogInterface.OnClickListener listener, Message msg, Drawable icon) {if (msg == null && listener != null) {// 这里的mHandler实际上是在AlertController构造中创建的// this.mHandler = new AlertController.ButtonHandler(di);// 传递了一个DialogInterface对象进去,msg = this.mHandler.obtainMessage(whichButton, listener);}switch(whichButton) {case -3:this.mButtonNeutralText = text;this.mButtonNeutralMessage = msg;this.mButtonNeutralIcon = icon;break;case -2:this.mButtonNegativeText = text;this.mButtonNegativeMessage = msg;this.mButtonNegativeIcon = icon;break;case -1:// 将前面构建的Message赋值给AlertController的mButtonPositiveMessage字段// 对应的确定按钮监听放在message中。this.mButtonPositiveText = text;this.mButtonPositiveMessage = msg;this.mButtonPositiveIcon = icon;break;default:throw new IllegalArgumentException("Button does not exist");}}

到这,实际上AlertDialog的创建就完成了,我们还没明白为什么弹窗一定会消失,只能说这个确定按钮的监听在Message中,后面肯定是通过handler机制进行消息传递的。

接着看,Dialog.show()方法:

   // Dialog.java中代码public void show() {........if (!mCreated) {dispatchOnCreate(null);} else {........}void dispatchOnCreate(Bundle savedInstanceState) {if (!mCreated) {onCreate(savedInstanceState);mCreated = true;}}protected void onCreate(Bundle savedInstanceState) {}

在show()中,第一次创建的时候会调用dispatchOnCreate()->onCreate()但是,在Dialog中onCreate的实现为空,所以它应该在子类中有Override:

    // AlertDialog extends Dialog@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// mAlert是一个AlertController对象mAlert.installContent();}// AlertController.javapublic void installContent() {int contentView = this.selectContentView();this.mDialog.setContentView(contentView);this.setupView(); // 相关按钮代码在setupView中}private void setupButtons(ViewGroup buttonPanel) {int BIT_BUTTON_POSITIVE = true;int BIT_BUTTON_NEGATIVE = true;int BIT_BUTTON_NEUTRAL = true;int whichButtons = false;this.mButtonPositive = (Button)buttonPanel.findViewById(16908313);this.mButtonPositive.setOnClickListener(this.mButtonHandler);........}

setupView()->setupButtons();在设置按钮方法张,先从ViewGroup中拿到对应的确认按钮,然后设置按钮监听,这里传递进去的是一个OnClickListener对象,在AlertController的全局字段中:

Handler mHandler;private final OnClickListener mButtonHandler = new OnClickListener() {public void onClick(View v) {Message m;if (v == AlertController.this.mButtonPositive && AlertController.this.mButtonPositiveMessage != null) {m = Message.obtain(AlertController.this.mButtonPositiveMessage);} else if (v == AlertController.this.mButtonNegative && AlertController.this.mButtonNegativeMessage != null) {m = Message.obtain(AlertController.this.mButtonNegativeMessage);} else if (v == AlertController.this.mButtonNeutral && AlertController.this.mButtonNeutralMessage != null) {m = Message.obtain(AlertController.this.mButtonNeutralMessage);} else {m = null;}if (m != null) {m.sendToTarget();}AlertController.this.mHandler.obtainMessage(1, AlertController.this.mDialog).sendToTarget();}};

所以,当我们点击确认按钮时,会调用mButtonHandler这个监听,判断时mButtonPositive这个View时,从AlertController中拿mButtonPositiveMessage这个变量,它是在setButton中被赋值的,其中的msg.obj就是我们在AlertDialog.Builder.setPositiveButton(listener)中传递进来的一个DialogInterface.OnClickListener对象。

拿到消息后,先调用Message.sendToTarget,根据Handler-Message的消息机制,发送消息后,对应处理消息回调在Handler中,而这里的Handler就是mHandler:this.mHandler = new AlertController.ButtonHandler(di);

    private static final class ButtonHandler extends Handler {private static final int MSG_DISMISS_DIALOG = 1;private WeakReference<DialogInterface> mDialog;public ButtonHandler(DialogInterface dialog) {this.mDialog = new WeakReference(dialog);}public void handleMessage(Message msg) {switch(msg.what) {case -3:case -2:case -1:((android.content.DialogInterface.OnClickListener)msg.obj).onClick((DialogInterface)this.mDialog.get(), msg.what);case 0:default:break;case 1:((DialogInterface)msg.obj).dismiss();}}}

所以在处理消息逻辑中,先是从msg中拿出之前保存的DialogInterface.OnClickListener对象,然后调用其onClick,也就是执行我们自己写的确认回调;

到此位置,点击确认按钮源码流程就理解通了,但是,为什么会消失呢?

我们回到之前的按钮的监听流程中:

         if (m != null) {m.sendToTarget();}AlertController.this.mHandler.obtainMessage(1, AlertController.this.mDialog).sendToTarget();

在调用m.sendToTarget()执行自定义监听之后,还会发送一个消息,其中msgWhat = 1;msg.obj = AppCompatDialog;

所以就算我们之前自定义监听中,判断不满足条件,直接return之后,也会执行到ButtonHandler.handleMessage中的case 1,所以必定会走Dialog.dismiss();

而我们做的,就是在AlertDialog显示之后,通过getButton拿到确认按钮实例,然后自行设置OnClickListener,覆盖掉之前在Dialog::show()->Dialog::dispatchOnCreate()->AlertDialog::onCreate()->AlertController::installContent()->AlertController::setupView()->AlertController::setupButtons()->mButtonPositive.setOnClickListener();中设置的mButtonHandler。将这个监听覆盖掉之后,就不会走后续DialogInterface.OnClickListener监听的流程了,而是直接走的View.OnClickListener流程;

所以,上面提到的“必须在弹窗show()之后进行监听设置”,也是因为只有调用show之后,才能获取到对应的窗口中Buttom实例(此时View被从xml中解析,并实例化),并且在此之后,我们设置的监听才能覆盖掉原有show流程中设置的监听。

AlertDialog点击确定必定会dismiss问题相关推荐

  1. android dialog 点击确定不消失,AlertDialog点击按钮不消失的实现方法

    我有一个文本输入对话框,当我点击对话框上的"是"按钮,它会验证输入,然后关闭对话框. 但是,如果输入错误,我想停留在同一个对话框中.. 每一次,无论输入是什么,对话框应该自动关闭, ...

  2. AlertDialog 点击空白处、返回按钮 行为

    为什么80%的码农都做不了架构师?>>>    弹出AlertDialog之后,点击空白处,默认Dialog消失: 点击回退键,默认消失: 那么如何点击空白处,不隐藏对话框,点击An ...

  3. android之AlertDialog 点击其它区域自己主动消失

    遇到一个问题记录下来,在开发中使用了AlertDialog,想点击屏幕其它区域的时候让这个dialog消失,一開始不做不论什么设置,在小米手机能够正常显示,可是在三星中却有问题.后来发现少了一个属性: ...

  4. android安卓之常用对话框设计AlertDialog

    android安卓之常用对话框设计AlertDialog 学习目的:掌握常用对话框中提示,单选,复选,自定义对话框的设计和使用. 布局文件 activity_main.xml <?xml ver ...

  5. 自定义AlertDialog的创建和使用

    填充视图 View v = myActivity.this.getLayoutInflater().inflate(R.layout.dialog_nickname, null); 其中R.layou ...

  6. 弹出页(指定高度,自由拖动,点击空白包括状态栏触发)

    iOS指定高度弹出页.可拖动,点击空白包括状态栏收回 1.首先需要指定弹出页面的高度: /**页面高度*/func groundHeight() -> CGFloat {return Slide ...

  7. popwindow 加个边框_利用popupwindow生成带有列表的对话框,并设置对话框列表的点击事件...

    点击某个View弹出popupwindow列表: 代码:private ArrayAdapter adapter_huoMing; private PopupWindow popupWindow; / ...

  8. 优秀的设计UI界面按钮素材,让点击率飙升

    快节奏的网络数码时代人们越来越离不开智能设备,面对屏幕上无数的按钮,点还是不点是个问题.不知道大家有没有发现,按钮越多,我们点击得越慢.这是因为只要有按钮,我们的下意识就会"检查" ...

  9. 仿微信朋友圈点击评论自动定位到对应位置

    参考文章:仿微信朋友圈点击评论自动定位到相关行 一.思路: 1.在点击[评论]控件的时候弹出一个 dialog,dialog 上面为 ScrollView,下面为输入框,实现键盘弹出时把输入框顶上去的 ...

  10. JS实现搜索按钮的点击事件

    JS实现搜索按钮的点击事件 开发工具与关键技术:DW JS 作者:易金亮 撰写时间:2019.02.02 在各个网页或者软件中,往往离不开搜索框以及搜索按钮,下面我们来用JS实现简单的搜索按钮点击事件 ...

最新文章

  1. Druid连接池一个设置引发的血案
  2. Tomcat启动过程中找不到JAVA_HOME解决方法
  3. python 基本数据类型常用方法总结
  4. hwt字体转换ttf_五分钟教你弄懂了字体反爬是个啥
  5. 简单的php代理 Simple PHP Proxy
  6. iOS 3DTouch
  7. python模拟访问js_python模拟http请求,返回“浏览器不支持javascript,请设置您的浏览器开启javascript支持”...
  8. 怎么看台式计算机是几位的,怎么看电脑是32位还是64位?一目了然
  9. 软考软件设计师下午真题-面向对象的程序设计与实现-装饰设计模式(2012年上半年试题六))Java代码讲解
  10. python中浮点型占几个字节_python的浮点数占多少个字节
  11. win8 完全禁用uac的方法
  12. 浅谈 光学字符识别(OCR识别)技术的前世今生...
  13. 如何解决Unity高版本使用EasyTouch5的问题
  14. linux 网络速度非常慢,解决Ubuntu 10.04上网速度慢的问题
  15. elementUi tabs刷新后,选中的tab下划线不显示
  16. hive to hbase
  17. python|面向对象(一)
  18. GPIO端口初始化设置,STM32F103点亮LED流水灯过程
  19. 2018年最新黑马前端视频教程视频与源码全集
  20. train_test_split()函数用法

热门文章

  1. 关于读书学习与思考力。 文/江湖一剑客
  2. canvas实现星星闪烁特效
  3. windows下使用endless报错:undefined: syscall.SIGUSR1
  4. DM8 Linux服务注册
  5. 加油中国,雄起汶川-快乐工作,快乐生活(多图)
  6. 吴恩达反向传播算法推导,吴恩达卷积神经网络ppt
  7. phyton做彩色电子管
  8. 深度学习分类常见评价指标:accuracy recall precision specificity sensitivity AUC ROC 曲线
  9. 把一个godaddy的域名转回国内的注册商
  10. iphone,ipad,android图片尺寸