【问题标题】:How to prevent a dialog from closing when a button is clicked单击按钮时如何防止对话框关闭
【发布时间】:2022-01-06 07:03:12
【问题描述】:

我与EditText 有一个对话框供输入。当我单击对话框上的“是”按钮时,它将验证输入,然后关闭对话框。但是,如果输入错误,我想留在同一个对话框中。每次无论输入什么,当我单击“否”按钮时,对话框都应该自动关闭。我怎样才能禁用它?顺便说一句,我使用 PositiveButton 和 NegativeButton 作为对话框上的按钮。

【问题讨论】:

    标签: android dialog android-alertdialog android-dialog android-dialogfragment


    【解决方案1】:

    编辑:这仅适用于 API 8+,正如一些 cmets 所指出的那样。

    这是一个较晚的答案,但您可以在 AlertDialog 中添加一个 onShowListener,然后您可以覆盖按钮的 onClickListener。

    final AlertDialog dialog = new AlertDialog.Builder(context)
            .setView(v)
            .setTitle(R.string.my_title)
            .setPositiveButton(android.R.string.ok, null) //Set to null. We override the onclick
            .setNegativeButton(android.R.string.cancel, null)
            .create();
    
    dialog.setOnShowListener(new DialogInterface.OnShowListener() {
    
        @Override
        public void onShow(DialogInterface dialogInterface) {
    
            Button button = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE);
            button.setOnClickListener(new View.OnClickListener() {
    
                @Override
                public void onClick(View view) {
                    // TODO Do something
    
                    //Dismiss once everything is OK.
                    dialog.dismiss();
                }
            });
        }
    });
    dialog.show();
    

    【讨论】:

    • 嘿,迟到总比没有好,我一直在寻找那个,谢谢,+1 :) 这是向对话框添加验证的一种优雅方式,尤其是当您已经有一个辅助包装类时处理警报
    • 不起作用。 AlertDialog.Builder.setOnShowListener 不存在。 developer.android.com/reference/android/app/…
    • 使用 API pre 8,您可以调用 d.getButton(AlertDialog.BUTTON_POSITIVE);因为它是公共方法,但你必须调用它 show();已发布,否则 zou 从中获取 null
    • 您可以通过将 null OnClickListener 设置为对话框构建器(保存空的“//this will be overridden”侦听器)来使其更加简洁。
    • 在 onCreateDialog(Bundle savedInstanceState) 方法中创建 AlertDialog 时,也可以与 DialogFragments 一起使用。
    【解决方案2】:

    这里有一些适用于所有类型对话框的解决方案,包括适用于所有 API 级别的 AlertDialog.Builder 解决方案(适用于 API 8 以下,此处的其他答案不适用)。有使用 AlertDialog.Builder、DialogFragment 和 DialogPreference 的 AlertDialogs 解决方案。

    下面的代码示例展示了如何覆盖默认的常用按钮处理程序并防止对话框关闭这些不同形式的对话框。所有的例子都展示了如何防止正按钮关闭对话框。

    注意:关于基本 android 类的对话框关闭工作原理以及为什么选择以下方法的说明在示例之后,供需要更多详细信息的人参考


    AlertDialog.Builder - 在 show() 之后立即更改默认按钮处理程序

    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    builder.setMessage("Test for preventing dialog close");
    builder.setPositiveButton("Test", 
            new DialogInterface.OnClickListener()
            {
                @Override
                public void onClick(DialogInterface dialog, int which)
                {
                    //Do nothing here because we override this button later to change the close behaviour. 
                    //However, we still need this because on older versions of Android unless we 
                    //pass a handler the button doesn't get instantiated
                }
            });
    final AlertDialog dialog = builder.create();
    dialog.show();
    //Overriding the handler immediately after show is probably a better approach than OnShowListener as described below
    dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener()
          {            
              @Override
              public void onClick(View v)
              {
                  Boolean wantToCloseDialog = false;
                  //Do stuff, possibly set wantToCloseDialog to true then...
                  if(wantToCloseDialog)
                      dialog.dismiss();
                  //else dialog stays open. Make sure you have an obvious way to close the dialog especially if you set cancellable to false.
              }
          });
          
    

    DialogFragment - 覆盖 onResume()

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState)
    {
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setMessage("Test for preventing dialog close");
        builder.setPositiveButton("Test", 
            new DialogInterface.OnClickListener()
            {
                @Override
                public void onClick(DialogInterface dialog, int which)
                {
                    //Do nothing here because we override this button later to change the close behaviour. 
                    //However, we still need this because on older versions of Android unless we 
                    //pass a handler the button doesn't get instantiated
                }
            });
        return builder.create();
    }
    
    //onStart() is where dialog.show() is actually called on 
    //the underlying dialog, so we have to do it there or 
    //later in the lifecycle.
    //Doing it in onResume() makes sure that even if there is a config change 
    //environment that skips onStart then the dialog will still be functioning
    //properly after a rotation.
    @Override
    public void onResume()
    {
        super.onResume();    
        final AlertDialog d = (AlertDialog)getDialog();
        if(d != null)
        {
            Button positiveButton = (Button) d.getButton(Dialog.BUTTON_POSITIVE);
            positiveButton.setOnClickListener(new View.OnClickListener()
                    {
                        @Override
                        public void onClick(View v)
                        {
                            Boolean wantToCloseDialog = false;
                            //Do stuff, possibly set wantToCloseDialog to true then...
                            if(wantToCloseDialog)
                                d.dismiss();
                            //else dialog stays open. Make sure you have an obvious way to close the dialog especially if you set cancellable to false.
                        }
                    });
        }
    }
    

    DialogPreference - 覆盖 showDialog()

    @Override
    protected void onPrepareDialogBuilder(Builder builder)
    {
        super.onPrepareDialogBuilder(builder);
        builder.setPositiveButton("Test", this);   //Set the button here so it gets created
    }
    
    @Override
    protected void showDialog(Bundle state)
    {       
        super.showDialog(state);    //Call show on default first so we can override the handlers
    
        final AlertDialog d = (AlertDialog) getDialog();
        d.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener()
                {            
                    @Override
                    public void onClick(View v)
                    {
                        Boolean wantToCloseDialog = false;
                        //Do stuff, possibly set wantToCloseDialog to true then...
                        if(wantToCloseDialog)
                            d.dismiss();
                        //else dialog stays open. Make sure you have an obvious way to close the dialog especially if you set cancellable to false.
                    }
                });
    }
    

    方法说明:

    查看 Android 源代码,AlertDialog 默认实现通过向 OnCreate() 中的所有实际按钮注册一个通用按钮处理程序来工作。单击按钮时,通用按钮处理程序会将单击事件转发给您在 setButton() 中传递的任何处理程序,然后调用会关闭对话框。

    如果您希望在按下这些按钮之一时阻止对话框关闭,您必须将通用按钮处理程序替换为按钮的实际视图。因为它是在 OnCreate() 中分配的,所以必须在调用默认 OnCreate() 实现后替换它。在 show() 方法的过程中调用 OnCreate。您可以创建一个自定义 Dialog 类并覆盖 OnCreate() 以调用 super.OnCreate() 然后覆盖按钮处理程序,但是如果您制作自定义对话框,您不会免费获得 Builder,在这种情况下有什么意义?

    因此,在按照设计方式使用对话框但控制何时关闭时,一种方法是先调用 dialog.Show(),然后使用 dialog.getButton() 获取对按钮的引用以覆盖单击处理程序。另一种方法是使用 setOnShowListener() 并实现查找按钮视图并替换 OnShowListener 中的处理程序。两者之间的功能差异“几乎”为零,具体取决于最初创建对话框实例的线程。查看源代码,onShowListener 被一条消息调用,该消息发布到在创建该对话框的线程上运行的处理程序。因此,由于您的 OnShowListener 是由消息队列上发布的消息调用的,因此从技术上讲,调用您的侦听器可能会在显示完成后延迟一段时间。

    因此,我认为最安全的方法是第一种:调用 show.Dialog(),然后立即在同一执行路径中替换按钮处理程序。由于您调用 show() 的代码将在主 GUI 线程上运行,这意味着您遵循 show() 的任何代码都将在该线程上的任何其他代码之前执行,而 OnShowListener 方法的时间由消息队列。

    【讨论】:

    • 这是迄今为止最简单的实现并且完美运行。我使用了 AlertDialog.Builder - 在 show() 之后立即更改默认按钮处理程序,它的工作原理就像魅力一样。
    • @sogger 伙计,我完全大胆地编辑了你的惊人答案,因为在第 1 节中你有dismiss();而不是我相信 dialog.dismiss();非常感谢您的精彩回答!
    • 有什么方法可以防止在单击按钮时关闭ProgressDialog
    • 天哪,我对Android了解得越多,我就越觉得恶心……这一切只是为了让一个简单的对话框正常工作。弄清楚如何显示对话框需要几个小时
    • @harsh_v 更新了下一个人使用 onResume() 的答案,谢谢!
    【解决方案3】:

    另一种解决方案

    我想从用户体验的角度提出另一个答案。

    为什么要在单击按钮时阻止对话框关闭?大概是因为您有一个自定义对话框,其中用户尚未做出选择或尚未完全填写所有内容。如果他们没有完成,那么你根本不应该让他们点击肯定按钮。只需禁用它,直到一切准备就绪。

    这里的其他答案提供了很多技巧来覆盖积极的按钮点击。如果这很重要,那么 Android 不会有一种方便的方法来做到这一点吗?他们没有。

    相反,Dialogs design guide 显示了这种情况的一个示例。在用户做出选择之前,确定按钮被禁用。根本不需要压倒一切的技巧。对用户来说很明显,在继续之前还需要做一些事情。

    如何禁用正面按钮

    请参阅Android documentation for creating a custom dialog layout。它建议您将AlertDialog 放在DialogFragment 中。然后您需要做的就是在布局元素上设置监听器,以了解何时启用或禁用正按钮。

    积极按钮可以这样禁用:

    AlertDialog dialog = (AlertDialog) getDialog();
    dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
    

    这是一个完整的工作 DialogFragment,带有一个禁用的正按钮,例如上图中可能使用的按钮。

    import android.support.v4.app.DialogFragment;
    import android.support.v7.app.AlertDialog;
    
    public class MyDialogFragment extends DialogFragment {
    
        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
    
            // inflate the custom dialog layout
            LayoutInflater inflater = getActivity().getLayoutInflater();
            View view = inflater.inflate(R.layout.my_dialog_layout, null);
    
            // add a listener to the radio buttons
            RadioGroup radioGroup = (RadioGroup) view.findViewById(R.id.radio_group);
            radioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(RadioGroup radioGroup, int i) {
                    // enable the positive button after a choice has been made
                    AlertDialog dialog = (AlertDialog) getDialog();
                    dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(true);
                }
            });
    
            // build the alert dialog
            AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
            builder.setView(view)
                    .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int id) {
                            // TODO: use an interface to pass the user choice back to the activity
                        }
                    })
                    .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                            MyDialogFragment.this.getDialog().cancel();
                        }
                    });
            return builder.create();
        }
    
        @Override
        public void onResume() {
            super.onResume();
    
            // disable positive button by default
            AlertDialog dialog = (AlertDialog) getDialog();
            dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
        }
    }
    

    自定义对话框可以从这样的活动中运行:

    MyDialogFragment dialog = new MyDialogFragment();
    dialog.show(getFragmentManager(), "MyTag");
    

    备注

    • 为简洁起见,我省略了将用户选择信息传递回活动的通信接口。不过,documentation 显示了这是如何完成的。
    • 按钮在onCreateDialog 中仍然是null,所以我在onResume 中禁用了它。如果用户切换到另一个应用程序然后返回而不关闭对话框,这会产生不希望的效果,即再次禁用它。这也可以通过取消选择任何用户选项或通过从onCreateDialog 调用Runnable 来禁用下一个运行循环上的按钮来解决。

      view.post(new Runnable() {
          @Override
          public void run() {
              AlertDialog dialog = (AlertDialog) getDialog();
              dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
          }
      });
      

    相关

    【讨论】:

    • 这对于非常简单的对话框来说很好,但是如果输入验证比您显示的更复杂,或者它包含多个字段,您需要一种方法来告诉用户为什么他们可以'继续。
    • @mhsmith,是的,虽然对于包含多个字段的复杂内容,我可能不会使用弹出对话框。
    【解决方案4】:

    我编写了一个简单的类(AlertDialogBu​​ilder),您可以使用它在按下对话框按钮时禁用自动关闭功能。

    它也与 Android 1.6 兼容,因此它不使用 OnShowListener(仅 API >= 8 可用)。

    因此,您可以使用此 CustomAlertDialogBu​​ilder,而不是使用 AlertDialog.Builder。 最重要的部分是你不应该调用create(),而应该只调用show() 方法。我添加了 setCanceledOnTouchOutside()setOnDismissListener 等方法,以便您仍然可以直接在构建器上设置它们。

    我在 Android 1.6、2.x、3.x 和 4.x 上对其进行了测试,因此它应该运行良好。 如果您发现一些问题,请在此处发表评论。

    package com.droidahead.lib.utils;
    
    import android.app.AlertDialog;
    import android.content.Context;
    import android.content.DialogInterface;
    import android.view.View;
    import android.view.View.OnClickListener;
    
    public class CustomAlertDialogBuilder extends AlertDialog.Builder {
        /**
         * Click listeners
         */
        private DialogInterface.OnClickListener mPositiveButtonListener = null;
        private DialogInterface.OnClickListener mNegativeButtonListener = null;
        private DialogInterface.OnClickListener mNeutralButtonListener = null;
    
        /**
         * Buttons text
         */
        private CharSequence mPositiveButtonText = null;
        private CharSequence mNegativeButtonText = null;
        private CharSequence mNeutralButtonText = null;
    
        private DialogInterface.OnDismissListener mOnDismissListener = null;
    
        private Boolean mCancelOnTouchOutside = null;
    
        public CustomAlertDialogBuilder(Context context) {
            super(context);
        }
    
        public CustomAlertDialogBuilder setOnDismissListener (DialogInterface.OnDismissListener listener) {
            mOnDismissListener = listener;
            return this;
        }
    
        @Override
        public CustomAlertDialogBuilder setNegativeButton(CharSequence text, DialogInterface.OnClickListener listener) {
            mNegativeButtonListener = listener;
            mNegativeButtonText = text;
            return this;
        }
    
        @Override
        public CustomAlertDialogBuilder setNeutralButton(CharSequence text, DialogInterface.OnClickListener listener) {
            mNeutralButtonListener = listener;
            mNeutralButtonText = text;
            return this;
        }
    
        @Override
        public CustomAlertDialogBuilder setPositiveButton(CharSequence text, DialogInterface.OnClickListener listener) {
            mPositiveButtonListener = listener;
            mPositiveButtonText = text;
            return this;
        }
    
        @Override
        public CustomAlertDialogBuilder setNegativeButton(int textId, DialogInterface.OnClickListener listener) {
            setNegativeButton(getContext().getString(textId), listener);
            return this;
        }
    
        @Override
        public CustomAlertDialogBuilder setNeutralButton(int textId, DialogInterface.OnClickListener listener) {
            setNeutralButton(getContext().getString(textId), listener);
            return this;
        }
    
        @Override
        public CustomAlertDialogBuilder setPositiveButton(int textId, DialogInterface.OnClickListener listener) {
            setPositiveButton(getContext().getString(textId), listener);
            return this;
        }
    
        public CustomAlertDialogBuilder setCanceledOnTouchOutside (boolean cancelOnTouchOutside) {
            mCancelOnTouchOutside = cancelOnTouchOutside;
            return this;
        }
    
    
    
        @Override
        public AlertDialog create() {
            throw new UnsupportedOperationException("CustomAlertDialogBuilder.create(): use show() instead..");
        }
    
        @Override
        public AlertDialog show() {
            final AlertDialog alertDialog = super.create();
    
            DialogInterface.OnClickListener emptyOnClickListener = new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) { }
            };
    
    
            // Enable buttons (needed for Android 1.6) - otherwise later getButton() returns null
            if (mPositiveButtonText != null) {
                alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, mPositiveButtonText, emptyOnClickListener);
            }
    
            if (mNegativeButtonText != null) {
                alertDialog.setButton(AlertDialog.BUTTON_NEGATIVE, mNegativeButtonText, emptyOnClickListener);
            }
    
            if (mNeutralButtonText != null) {
                alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, mNeutralButtonText, emptyOnClickListener);
            }
    
            // Set OnDismissListener if available
            if (mOnDismissListener != null) {
                alertDialog.setOnDismissListener(mOnDismissListener);
            }
    
            if (mCancelOnTouchOutside != null) {
                alertDialog.setCanceledOnTouchOutside(mCancelOnTouchOutside);
            }
    
            alertDialog.show();
    
            // Set the OnClickListener directly on the Button object, avoiding the auto-dismiss feature
            // IMPORTANT: this must be after alert.show(), otherwise the button doesn't exist..
            // If the listeners are null don't do anything so that they will still dismiss the dialog when clicked
            if (mPositiveButtonListener != null) {
                alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new OnClickListener() {
    
                    @Override
                    public void onClick(View v) {
                        mPositiveButtonListener.onClick(alertDialog, AlertDialog.BUTTON_POSITIVE);
                    }
                });
            }
    
            if (mNegativeButtonListener != null) {
                alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE).setOnClickListener(new OnClickListener() {
    
                    @Override
                    public void onClick(View v) {
                        mNegativeButtonListener.onClick(alertDialog, AlertDialog.BUTTON_NEGATIVE);
                    }
                });
            }
    
            if (mNeutralButtonListener != null) {
                alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener(new OnClickListener() {
    
                    @Override
                    public void onClick(View v) {
                        mNeutralButtonListener.onClick(alertDialog, AlertDialog.BUTTON_NEUTRAL);
                    }
                });
            }
    
            return alertDialog;
        }   
    }
    

    编辑下面是一个关于如何使用 CustomAlertDialogBu​​ilder 的小例子:

    // Create the CustomAlertDialogBuilder
    CustomAlertDialogBuilder dialogBuilder = new CustomAlertDialogBuilder(context);
    
    // Set the usual data, as you would do with AlertDialog.Builder
    dialogBuilder.setIcon(R.drawable.icon);
    dialogBuilder.setTitle("Dialog title");
    dialogBuilder.setMessage("Some text..");
    
    // Set your buttons OnClickListeners
    dialogBuilder.setPositiveButton ("Button 1", new DialogInterface.OnClickListener() {
        public void onClick (DialogInterface dialog, int which) {
            // Do something...
    
            // Dialog will not dismiss when the button is clicked
            // call dialog.dismiss() to actually dismiss it.
        }
    });
    
    // By passing null as the OnClickListener the dialog will dismiss when the button is clicked.               
    dialogBuilder.setNegativeButton ("Close", null);
    
    // Set the OnDismissListener (if you need it)       
    dialogBuilder.setOnDismissListener(new DialogInterface.OnDismissListener() {
        public void onDismiss(DialogInterface dialog) {
            // dialog was just dismissed..
        }
    });
    
    // (optional) set whether to dismiss dialog when touching outside
    dialogBuilder.setCanceledOnTouchOutside(false);
    
    // Show the dialog
    dialogBuilder.show();
    

    干杯,

    尤维

    【讨论】:

    • 不错。但对我不起作用。尽管如此,对话框还是被解雇了。
    • 嗯,这听起来很奇怪。我在我的应用程序中使用它,只有我明确调用 dialog.dismiss() 的按钮才会关闭对话框。您正在测试哪个 Android 版本?你能在你使用 CustomAlertDialogBu​​ilder 的地方展示你的代码吗?
    • 我认为这是因为:(在 onClickListener 之后调用 dialog.show())pastebin.com/uLnSu5v7 如果我单击 positiveButton,如果 boolean 为 true,它们会被关闭...
    • 我没有使用 Activity.onCreateDialog() 对其进行测试。可能它不能以这种方式工作。我将编辑“答案”以包含一个关于我如何使用它的小示例。
    • 这适用于我当前的编辑!但是:还有一个警告。 Builder.getContext() 仅在 API 11+ 上可用。添加一个字段Context mContext 并将其设置在构造函数中。
    【解决方案5】:

    如果您使用的是DialogFragment,这里有一些东西 - 无论如何这是处理对话框的推荐方法。

    AlertDialog 的setButton() 方法会发生什么(我想AlertDialogBuildersetPositiveButton()setNegativeButton() 也一样)是您设置的按钮(例如AlertDialog.BUTTON_POSITIVE)实际上会触发两个不同的OnClickListener 按下时的对象。

    第一个是DialogInterface.OnClickListener,它是setButton()setPositiveButton()setNegativeButton()的参数。

    另一个是View.OnClickListener,它将设置为在按下任何按钮时自动关闭您的AlertDialog - 并由AlertDialog 本身设置。

    您可以做的是使用setButton()null 作为DialogInterface.OnClickListener,创建按钮,然后在View.OnClickListener 中调用您的自定义操作方法。例如,

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState)
    {
        AlertDialog alertDialog = new AlertDialog(getActivity());
        // set more items...
        alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, "OK", null);
    
        return alertDialog;
    }
    

    然后,您可以在DialogFragmentonResume() 方法中覆盖默认的AlertDialog 按钮的View.OnClickListener(否则会关闭对话框):

    @Override
    public void onResume()
    {
        super.onResume();
        AlertDialog alertDialog = (AlertDialog) getDialog();
        Button okButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
        okButton.setOnClickListener(new View.OnClickListener() { 
            @Override
            public void onClick(View v)
            {
                performOkButtonAction();
            }
        });
    }
    
    private void performOkButtonAction() {
        // Do your stuff here
    }
    

    您需要在onResume() 方法中进行设置,因为在显示对话框之前getButton() 将返回null

    这应该会导致您的自定义操作方法只被调用一次,并且默认情况下不会关闭对话框。

    【讨论】:

      【解决方案6】:

      受到汤姆回答的启发,我相信这里的想法是:

      • 在创建对话框时将onClickListener设置为null
      • 然后在显示对话框后设置onClickListener

      您可以像 Tom 一样覆盖 onShowListener。或者,您可以

      1. 调用AlertDialog的show()后获取按钮
      2. 按如下方式设置按钮的onClickListener(我认为更具可读性)。

      代码:

      AlertDialog.Builder builder = new AlertDialog.Builder(context);
      // ...
      final AlertDialog dialog = builder.create();
      dialog.show();
      // now you can override the default onClickListener
      Button b = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
      b.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View view) {
              Log.i(TAG, "ok button is clicked");
              handleClick(dialog);
          }
      });
      

      【讨论】:

        【解决方案7】:

        对于 API 8 之前的版本,如果 editText 的内容不正确,我使用布尔标志、关闭侦听器并再次调用 dialog.show 解决了问题。像这样:

        case ADD_CLIENT:
                LayoutInflater factoryClient = LayoutInflater.from(this);
                final View EntryViewClient = factoryClient.inflate(
                        R.layout.alert_dialog_add_client, null);
        
                EditText ClientText = (EditText) EntryViewClient
                        .findViewById(R.id.client_edit);
        
                AlertDialog.Builder builderClient = new AlertDialog.Builder(this);
                builderClient
                        .setTitle(R.string.alert_dialog_client)
                        .setCancelable(false)
                        .setView(EntryViewClient)
                        .setPositiveButton("Save",
                                new DialogInterface.OnClickListener() {
                                    public void onClick(DialogInterface dialog,
                                            int whichButton) {
                                        EditText newClient = (EditText) EntryViewClient
                                                .findViewById(R.id.client_edit);
                                        String newClientString = newClient
                                                .getText().toString();
                                        if (checkForEmptyFields(newClientString)) {
                                            //If field is empty show toast and set error flag to true;
                                            Toast.makeText(getApplicationContext(),
                                                    "Fields cant be empty",
                                                    Toast.LENGTH_SHORT).show();
                                            add_client_error = true;
                                        } else {
                                            //Here save the info and set the error flag to false
                                            add_client_error = false;
                                        }
                                    }
                                })
                        .setNegativeButton("Cancel",
                                new DialogInterface.OnClickListener() {
                                    public void onClick(DialogInterface dialog,
                                            int id) {
                                        add_client_error = false;
                                        dialog.cancel();
                                    }
                                });
                final AlertDialog alertClient = builderClient.create();
                alertClient.show();
        
                alertClient
                        .setOnDismissListener(new DialogInterface.OnDismissListener() {
        
                            @Override
                            public void onDismiss(DialogInterface dialog) {
                                //If the error flag was set to true then show the dialog again
                                if (add_client_error == true) {
                                    alertClient.show();
                                } else {
                                    return;
                                }
        
                            }
                        });
                return true;
        

        【讨论】:

        • 奇怪的 onDismiss 没有被调用,我的是 api level 21
        【解决方案8】:

        this link 的答案是一个简单的解决方案,它与 API 3 直接兼容。它与 Tom Bollwitt 的解决方案非常相似,但没有使用不太兼容的 OnShowListener。

        是的,你可以。你基本上需要:

        1. 使用 DialogBu​​ilder 创建对话框
        2. show() 对话框
        3. 在显示的对话框中找到按钮并覆盖其 onClickListener

        自从我扩展 EditTextPreference 以来,我对 Kamen 的代码做了一些小的修改。

        @Override
        protected void showDialog(Bundle state) {
          super.showDialog(state);
        
          class mocl implements OnClickListener{
            private final AlertDialog dialog;
            public mocl(AlertDialog dialog) {
                  this.dialog = dialog;
              }
            @Override
            public void onClick(View v) {
        
                //checks if EditText is empty, and if so tells the user via Toast
                //otherwise it closes dialog and calls the EditTextPreference's onClick
                //method to let it know that the button has been pressed
        
                if (!IntPreference.this.getEditText().getText().toString().equals("")){
                dialog.dismiss();
                IntPreference.this.onClick(dialog,DialogInterface.BUTTON_POSITIVE);
                }
                else {
                    Toast t = Toast.makeText(getContext(), "Enter a number!", Toast.LENGTH_SHORT);
                    t.show();
                }
        
            }
          }
        
          AlertDialog d = (AlertDialog) getDialog();
          Button b = d.getButton(DialogInterface.BUTTON_POSITIVE);
          b.setOnClickListener(new mocl((d)));
        }
        

        真有趣!

        【讨论】:

          【解决方案9】:

          这段代码对你有用,因为我有一个类似的问题,这对我有用。 :)

          1- 覆盖片段对话框类中的 Onstart() 方法。

          @Override
          public void onStart() {
              super.onStart();
              final AlertDialog D = (AlertDialog) getDialog();
              if (D != null) {
                  Button positive = (Button) D.getButton(Dialog.BUTTON_POSITIVE);
                  positive.setOnClickListener(new View.OnClickListener() {
                      @Override
                      public void onClick(View arg0) {
                          if (edittext.equals("")) {
             Toast.makeText(getActivity(), "EditText empty",Toast.LENGTH_SHORT).show();
                          } else {
                          D.dismiss(); //dissmiss dialog
                          }
                      }
                  });
              }
          }
          

          【讨论】:

            【解决方案10】:

            为了防止对话框在点击时关闭,它应该只在互联网可用时关闭

            我正在尝试做同样的事情,因为我不希望在连接互联网之前关闭对话框。

            这是我的代码:

            AlertDialog.Builder builder=new AlertDialog.Builder(MainActivity.this); builder.setTitle("Internet Not Connected");
                if(ifConnected()){
            
                    Toast.makeText(this, "Connected or not", Toast.LENGTH_LONG).show();
                }
                else{
                    builder.setPositiveButton("Retry", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                           if(!ifConnected())
                           {
                               builder.show();
                           }
                        }
                    }).setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            finish();
                        }
                    });
                    builder.show();
            
                }
            

            这是我的连接管理器代码:

             private boolean ifConnected()
            {
                ConnectivityManager connectivityManager= (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
                NetworkInfo networkInfo=connectivityManager.getActiveNetworkInfo();
               return networkInfo!=null && networkInfo.isConnected();
            }
            

            【讨论】:

            • 这很聪明,但我收到此错误消息:The specified child already has a parent. You must call removeView() on the child's parent first
            【解决方案11】:

            对于 ProgressDialogs

            为防止对话框自动关闭,您必须在显示ProgressDialog 后设置OnClickListener,如下所示:

            connectingDialog = new ProgressDialog(this);
            
            connectingDialog.setCancelable(false);
            connectingDialog.setCanceledOnTouchOutside(false);
            
            // Create the button but set the listener to a null object.
            connectingDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", 
                    (DialogInterface.OnClickListener) null )
            
            // Show the dialog so we can then get the button from the view.
            connectingDialog.show();
            
            // Get the button from the view.
            Button dialogButton = connectingDialog.getButton( DialogInterface.BUTTON_NEGATIVE);
            
            // Set the onClickListener here, in the view.
            dialogButton.setOnClickListener( new View.OnClickListener() {
            
                @Override
                public void onClick ( View v ) {
            
                    // Dialog will not get dismissed until you call dismiss() explicitly.
            
                }
            
            });
            

            【讨论】:

              【解决方案12】:
              public class ComentarDialog extends DialogFragment{
              private EditText comentario;
              
              @Override
              public Dialog onCreateDialog(Bundle savedInstanceState) {
              
                  AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
              
                  LayoutInflater inflater = LayoutInflater.from(getActivity());
                  View v = inflater.inflate(R.layout.dialog_comentar, null);
                  comentario = (EditText)v.findViewById(R.id.etxt_comentar_dialog);
              
                  builder.setTitle("Comentar")
                         .setView(v)
                         .setPositiveButton("OK", null)
                         .setNegativeButton("CANCELAR", new DialogInterface.OnClickListener() {
                             public void onClick(DialogInterface dialog, int id) {
              
                             }
                         });
              
                  return builder.create();
              }
              
              @Override
              public void onStart() {
                  super.onStart();
              
                  //Obtenemos el AlertDialog
                  AlertDialog dialog = (AlertDialog)getDialog();
              
                  dialog.setCanceledOnTouchOutside(false);
                  dialog.setCancelable(false);//Al presionar atras no desaparece
              
                  //Implementamos el listener del boton OK para mostrar el toast
                  dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener() {
                      @Override
                      public void onClick(View v) {
                          if(TextUtils.isEmpty(comentario.getText())){
                             Toast.makeText(getActivity(), "Ingrese un comentario", Toast.LENGTH_SHORT).show();
                             return;
                          }
                          else{
                              ((AlertDialog)getDialog()).dismiss();
                          }
                      }
                  });
              
                  //Personalizamos
                  Resources res = getResources();
              
                  //Buttons
                  Button positive_button = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
                  positive_button.setBackground(res.getDrawable(R.drawable.btn_selector_dialog));
              
                  Button negative_button =  dialog.getButton(DialogInterface.BUTTON_NEGATIVE);
                  negative_button.setBackground(res.getDrawable(R.drawable.btn_selector_dialog));
              
                  int color = Color.parseColor("#304f5a");
              
                  //Title
                  int titleId = res.getIdentifier("alertTitle", "id", "android");
                  View title = dialog.findViewById(titleId);
                  if (title != null) {
                      ((TextView) title).setTextColor(color);
                  }
              
                  //Title divider
                  int titleDividerId = res.getIdentifier("titleDivider", "id", "android");
                  View titleDivider = dialog.findViewById(titleDividerId);
                  if (titleDivider != null) {
                      titleDivider.setBackgroundColor(res.getColor(R.color.list_menu_divider));
                  }
              }
              }
              

              【讨论】:

                【解决方案13】:

                您可以添加 builder.show();验证信息后返回前;

                喜欢这个

                    public void login()
                {
                    final AlertDialog.Builder builder = new AlertDialog.Builder(this);
                    builder.setView(R.layout.login_layout);
                    builder.setTitle("Login");
                
                
                
                    builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener()
                    {
                        @Override
                        public void onClick(DialogInterface dialog, int id)
                        {
                            dialog.cancel();
                        }
                    });// put the negative button before the positive button, so it will appear
                
                    builder.setPositiveButton("Ok", new DialogInterface.OnClickListener()
                    {
                        @Override
                        public void onClick(DialogInterface dialog, int id)
                        {
                            Dialog d = (Dialog) dialog;
                            final EditText etUserName = (EditText) d.findViewById(R.id.etLoginName);
                            final EditText etPassword = (EditText) d.findViewById(R.id.etLoginPassword);
                            String userName = etUserName.getText().toString().trim();
                            String password = etPassword.getText().toString().trim();
                
                            if (userName.isEmpty() || password.isEmpty())
                            {
                
                                Toast.makeText(getApplicationContext(),
                                        "Please Fill all fields", Toast.LENGTH_SHORT).show();
                                builder.show();// here after validation message before retrun
                                               //  it will reopen the dialog
                                              // till the user enter the right condition
                                return;
                            }
                
                            user = Manager.get(getApplicationContext()).getUserByName(userName);
                
                            if (user == null)
                            {
                                Toast.makeText(getApplicationContext(),
                                        "Error ethier username or password are wrong", Toast.LENGTH_SHORT).show();
                                builder.show();
                                return;
                            }
                            if (password.equals(user.getPassword()))
                            {
                                etPassword.setText("");
                                etUserName.setText("");
                                setLogged(1);
                                setLoggedId(user.getUserId());
                                Toast.makeText(getApplicationContext(),
                                        "Successfully logged in", Toast.LENGTH_SHORT).show();
                               dialog.dismiss();// if every thing is ok then dismiss the dialog
                            }
                            else
                            {
                                Toast.makeText(getApplicationContext(),
                                        "Error ethier username or password are wrong", Toast.LENGTH_SHORT).show();
                                builder.show();
                                return;
                            }
                
                        }
                    });
                
                    builder.show();
                
                }
                

                【讨论】:

                  【解决方案14】:

                  如果您使用的是material design,我建议您查看material-dialogs。它为我修复了与当前打开的 Android 错误相关的几个问题(请参阅 78088),但最重要的是对于这张票它有一个 autoDismiss 标志,可以在使用 Builder 时设置。

                  【讨论】:

                    【解决方案15】:

                    超级简单的 Kotlin 方法

                     with(AlertDialog.Builder(this)) {
                            setTitle("Title")
                            setView(R.layout.dialog_name)
                            setPositiveButton("Ok", null)
                            setNegativeButton("Cancel") { _, _ -> }
                            create().apply {
                                setOnShowListener {
                                    getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
                                        //Validate and dismiss
                                        dismiss()
                                    }
                                }
                            }
                        }.show()
                    

                    【讨论】:

                      【解决方案16】:

                      使用此代码,您可以防止在单击正按钮时关闭对话框。您也可以使用否定按钮实现相同的功能。

                          final AlertDialog alertDialog = alertDialogBuilder
                                  .setCancelable(false)
                                  .setTitle("TITLE");
                                  .setPositiveButton("OK", null)
                                  .setNegativeButton("CANCEL",
                                          (dialog, id) -> {
                                              dialog.cancel();
                                          })
                                  .show();
                          Button positiveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
                          positiveButton.setOnClickListener(v -> {
                               // check whatever you want
                               if(checkMyCondition())
                                   dialog.cancel();
                          })
                      

                      【讨论】:

                        【解决方案17】:

                        为您的DialogFragment 使用自定义布局,并在您的内容下添加一个LinearLayout,可以将其设置为无边框以匹配 Google Material Design。然后找到新创建的按钮并覆盖它们的OnClickListener

                        例子:

                        public class AddTopicFragment extends DialogFragment {
                        
                            @Override
                            public Dialog onCreateDialog(Bundle savedInstanceState) {
                                final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
                                // Get the layout inflater
                                LayoutInflater inflater = getActivity().getLayoutInflater();
                                final View dialogView = inflater.inflate(R.layout.dialog_add_topic, null);
                        
                                Button saveTopicDialogButton = (Button) dialogView.findViewById(R.id.saveTopicDialogButton);
                                Button cancelSaveTopicDialogButton = (Button) dialogView.findViewById(R.id.cancelSaveTopicDialogButton);
                        
                                final AppCompatEditText addTopicNameET = (AppCompatEditText) dialogView.findViewById(R.id.addTopicNameET);
                                final AppCompatEditText addTopicCreatedByET = (AppCompatEditText) dialogView.findViewById(R.id.addTopicCreatedByET);
                        
                                saveTopicDialogButton.setOnClickListener(new View.OnClickListener() {
                                    @Override
                                    public void onClick(View v) {
                                        // validate inputs
                                        if(addTopicNameET.getText().toString().trim().isEmpty()){
                                            addTopicNameET.setError("Topic name can't be empty");
                                            addTopicNameET.requestFocus();
                                        }else if(addTopicCreatedByET.getText().toString().trim().isEmpty()){
                                            addTopicCreatedByET.setError("Topic created by can't be empty");
                                            addTopicCreatedByET.requestFocus();
                                        }else {
                                            // save topic to database
                                            Topic topic = new Topic();
                                            topic.name = addTopicNameET.getText().toString().trim();
                                            topic.createdBy = addTopicCreatedByET.getText().toString().trim();
                                            topic.createdDate = new Date().getTime();
                                            topic.save();
                                            AddTopicFragment.this.dismiss();
                                        }
                                    }
                                });
                        
                                cancelSaveTopicDialogButton.setOnClickListener(new View.OnClickListener() {
                                    @Override
                                    public void onClick(View v) {
                                        AddTopicFragment.this.dismiss();
                                    }
                                });
                        
                                // Inflate and set the layout for the dialog
                                // Pass null as the parent view because its going in the dialog layout
                                builder.setView(dialogView)
                                       .setMessage(getString(R.string.add_topic_message));
                        
                                return builder.create();
                            }
                        
                        }
                        

                        dialog_add_topic.xml

                        <?xml version="1.0" encoding="utf-8"?>
                        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                            xmlns:app="http://schemas.android.com/apk/res-auto"
                            android:orientation="vertical"
                            android:padding="@dimen/activity_horizontal_margin"
                            android:layout_width="match_parent"
                            android:layout_height="match_parent">
                        
                            <android.support.design.widget.TextInputLayout
                                android:layout_width="match_parent"
                                android:layout_height="wrap_content"
                                app:errorEnabled="true">
                        
                                <android.support.v7.widget.AppCompatEditText
                                    android:id="@+id/addTopicNameET"
                                    android:layout_width="match_parent"
                                    android:layout_height="wrap_content"
                                    android:hint="Topic Name"
                                    android:inputType="textPersonName"
                                    android:maxLines="1" />
                        
                            </android.support.design.widget.TextInputLayout>
                        
                            <android.support.design.widget.TextInputLayout
                                android:layout_width="match_parent"
                                android:layout_height="wrap_content"
                                app:errorEnabled="true">
                        
                                <android.support.v7.widget.AppCompatEditText
                                    android:id="@+id/addTopicCreatedByET"
                                    android:layout_width="match_parent"
                                    android:layout_height="wrap_content"
                                    android:hint="Created By"
                                    android:inputType="textPersonName"
                                    android:maxLines="1" />
                        
                            </android.support.design.widget.TextInputLayout>
                        
                            <LinearLayout
                                android:layout_width="match_parent"
                                android:layout_height="wrap_content"
                                android:orientation="horizontal">
                                <Button
                                    android:text="@string/cancel"
                                    android:layout_weight="1"
                                    android:layout_width="match_parent"
                                    android:layout_height="wrap_content"
                                    android:id="@+id/cancelSaveTopicDialogButton"
                                    style="@style/Widget.AppCompat.Button.ButtonBar.AlertDialog" />
                        
                                <Button
                                    android:text="@string/save"
                                    android:layout_weight="1"
                                    android:layout_width="match_parent"
                                    android:layout_height="wrap_content"
                                    android:id="@+id/saveTopicDialogButton"
                                    style="@style/Widget.AppCompat.Button.ButtonBar.AlertDialog" />
                        
                            </LinearLayout>
                        
                        
                        </LinearLayout>
                        

                        This is the final result.

                        【讨论】:

                          【解决方案18】:

                          科特林

                          val dialogView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_userinput, null)
                                  val dialogBuilder = MaterialAlertDialogBuilder(requireContext(), R.style.AlertDialogTheme)
                             
                                  dialogBuilder.setView(dialogView)
                                  dialogBuilder.setCancelable(false)
                                  dialogBuilder.setPositiveButton("send",null)
                                  dialogBuilder.setNegativeButton("cancel") { dialog,_ ->
                                  dialog.dismiss()
                                  }
                          
                          
                                  val alertDialog = dialogBuilder.create()
                                  alertDialog.show()
                          
                                  val positiveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE)
                                  positiveButton.setOnClickListener {
                                      val myInputText = dialogView.etxt_userinput.text.toString().trim()
                                      if(myInputText.isNotEmpty()){
                                       //Do something
                                      }else{
                                          //Prompt error
                                          dialogView.etxt_userinput.error = "Please fill this"
                                      }
                                  }
                          

                          我们只需使用dialogBuilder 创建一个AlertDialog,然后根据需要设置肯定按钮

                          【讨论】:

                            【解决方案19】:

                            它可以用最简单的方法构建:

                            带有自定义视图两个按钮(正面和负面)的警报对话框。

                            AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()).setTitle(getString(R.string.select_period));
                            builder.setPositiveButton(getString(R.string.ok), null);
                            
                             builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                            
                                // Click of Cancel Button
                            
                               }
                             });
                            
                              LayoutInflater li = LayoutInflater.from(getActivity());
                              View promptsView = li.inflate(R.layout.dialog_date_picker, null, false);
                              builder.setView(promptsView);
                            
                              DatePicker startDatePicker = (DatePicker)promptsView.findViewById(R.id.startDatePicker);
                              DatePicker endDatePicker = (DatePicker)promptsView.findViewById(R.id.endDatePicker);
                            
                              final AlertDialog alertDialog = builder.create();
                              alertDialog.show();
                            
                              Button theButton = alertDialog.getButton(DialogInterface.BUTTON_POSITIVE);
                              theButton.setOnClickListener(new CustomListener(alertDialog, startDatePicker, endDatePicker));
                            
                            Alert DailogPositive Button

                            CustomClickLister

                            private class CustomListener implements View.OnClickListener {
                                    private final Dialog dialog;
                                    private DatePicker mStartDp, mEndDp;
                                public CustomListener(Dialog dialog, DatePicker dS, DatePicker dE) {
                                    this.dialog = dialog;
                                    mStartDp = dS;
                                    mEndDp = dE;
                                }
                            
                                @Override
                                public void onClick(View v) {
                            
                                    int day1  = mStartDp.getDayOfMonth();
                                    int month1= mStartDp.getMonth();
                                    int year1 = mStartDp.getYear();
                                    Calendar cal1 = Calendar.getInstance();
                                    cal1.set(Calendar.YEAR, year1);
                                    cal1.set(Calendar.MONTH, month1);
                                    cal1.set(Calendar.DAY_OF_MONTH, day1);
                            
                            
                                    int day2  = mEndDp.getDayOfMonth();
                                    int month2= mEndDp.getMonth();
                                    int year2 = mEndDp.getYear();
                                    Calendar cal2 = Calendar.getInstance();
                                    cal2.set(Calendar.YEAR, year2);
                                    cal2.set(Calendar.MONTH, month2);
                                    cal2.set(Calendar.DAY_OF_MONTH, day2);
                            
                                    if(cal2.getTimeInMillis()>=cal1.getTimeInMillis()){
                                        dialog.dismiss();
                                        Log.i("Dialog", "Dismiss");
                                        // Condition is satisfied so do dialog dismiss
                                        }else {
                                        Log.i("Dialog", "Do not Dismiss");
                                        // Condition is not satisfied so do not dialog dismiss
                                    }
                            
                                }
                            }
                            

                            完成

                            【讨论】:

                              【解决方案20】:

                              我找到了另一种方法来实现这一点...

                              第 1 步:将对话框打开代码放入方法(或 C 中的函数)
                              第二步:在yes(Your positiveButton)的onClick里面,调用这个对话框打开 如果您的条件不满足,则递归方法(通过使用 if...else...)。如下:

                              private void openSave() {
                                 
                                  final AlertDialog.Builder builder=new AlertDialog.Builder(Phase2Activity.this);
                              
                                  builder.setTitle("SAVE")
                                          .setIcon(R.drawable.ic_save_icon)
                                          .setPositiveButton("Save", new DialogInterface.OnClickListener() {
                                              @Override
                                              public void onClick(DialogInterface dialogInterface, int i) {
                                                  
                                                      if((!editText.getText().toString().isEmpty() && !editText1.getText().toString().isEmpty())){
                              
                                                              createPdf(fileName,title,file);
                                                          
                                                      }else {
                                                          openSave();
                                                          Toast.makeText(Phase2Activity.this, "Some fields are empty.", Toast.LENGTH_SHORT).show();
                                                      }
                              
                                                  
                                          })
                                          .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                                          @Override
                                          public void onClick(DialogInterface dialogInterface, int i) {
                                             dialogInterface.dismiss();
                                          }
                                      })
                                          .setCancelable(false)
                                          .create()
                                          .show();
                              
                              }
                              

                              但这会使对话框消失片刻,它会立即再次出现。 :)

                              【讨论】:

                                【解决方案21】:

                                这可能是很晚的回复,但使用 setCancelable 可以解决问题。

                                alertDial.setCancelable(false);
                                

                                【讨论】:

                                • 来自文档:“设置此对话框是否可以使用 BACK 键取消。”这与关闭对话框的肯定按钮无关..
                                • 对我不起作用,单击肯定按钮时仍然关闭
                                • 这与OP无关
                                • 不解决问题
                                猜你喜欢
                                • 1970-01-01
                                • 2012-02-18
                                • 2018-02-20
                                • 2013-04-06
                                • 2012-08-22
                                • 1970-01-01
                                相关资源
                                最近更新 更多