ChatGPT解决这个技术问题 Extra ChatGPT

单击按钮时如何防止对话框关闭

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


D
Daniel Nugent

编辑:正如一些评论所指出的,这只适用于 API 8+。

这是一个较晚的答案,但您可以将 onShowListener 添加到 AlertDialog ,然后您可以在其中覆盖按钮的 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”侦听器)来使其更加简洁。
不阻止使用 AlertDialog Android API 级别 26 关闭对话框
C
Community

以下是所有类型对话框的一些解决方案,包括适用于所有 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() 的答案,谢谢!
S
Suragch

替代解决方案

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

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

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

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

https://i.stack.imgur.com/np1RE.png

如何禁用积极按钮

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

如果您的自定义对话框有单选按钮,则使用 RadioGroup.OnCheckedChangeListener。

如果您的自定义对话框有复选框,则使用 CompoundButton.OnCheckedChangeListener。

如果您的自定义对话框具有 EditText,则使用 TextWatcher。

可以像这样禁用积极按钮:

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");

笔记

为简洁起见,我省略了将用户选择信息传递回活动的通信接口。但是,文档显示了这是如何完成的。

onCreateDialog 中的按钮仍然为空,所以我在 onResume 中禁用了它。如果用户切换到另一个应用程序然后返回而不关闭对话框,这会产生不希望的效果,即再次禁用它。这也可以通过取消选择任何用户选择或通过从 onCreateDialog 调用 Runnable 来禁用下一个运行循环上的按钮来解决。 view.post(new Runnable() { @Override public void run() { AlertDialog dialog = (AlertDialog) getDialog(); dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false); } });

有关的

带有一个、两个和三个按钮的 Android 警报对话框

如何在 Android 警报对话框中显示列表视图?


这对于非常简单的对话框来说很好,但是如果输入验证比您显示的更复杂,或者如果它包含多个字段,您需要一种方法来告诉用户为什么他们不能继续。
@mhsmith,是的,尽管对于具有多个字段的复杂事物,我可能不会使用弹出对话框。
Y
YuviDroid

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

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

因此,您可以使用此 CustomAlertDialogBuilder,而不是使用 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;
    }   
}

编辑这是一个关于如何使用 CustomAlertDialogBuilder 的小例子:

// 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 版本?您可以在使用 CustomAlertDialogBuilder 的地方显示您的代码吗?
我认为这是由于以下原因引起的:(在 onClickListener 之后调用 dialog.show())pastebin.com/uLnSu5v7 如果我单击 positiveButton,如果布尔值为 true,它们将被关闭...
我没有使用 Activity.onCreateDialog() 对其进行测试。可能它不能以这种方式工作。我将编辑“答案”以包含一个关于我如何使用它的小例子。
这适用于我当前的编辑!但是:还有一个警告。 Builder.getContext() 仅在 API 11+ 上可用。添加一个字段 Context mContext 并将其设置在构造函数中。
Z
Zhuiguang Liu

如果您使用的是 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

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


e
ericn

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

将创建对话框期间的onClickListener设置为null

然后在显示对话框后设置一个 onClickListener。

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

在调用 AlertDialog 的 show() 后获取按钮将按钮的 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);
    }
});

S
Steve

对于 pre 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
C
Community

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

是的你可以。您基本上需要: 使用 DialogBuilder show() 对话框创建对话框 在显示的对话框中找到按钮并覆盖它们的 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)));
}

好好玩!


S
Sai

超级简单的 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()

L
Luis Nuñez

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

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
                }
            }
        });
    }
}

A
Arsen Khachaturyan

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

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

这是我的代码:

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
J
Joshua Pinter

对于进度对话框

要防止对话框自动关闭,您必须在显示 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.

    }

});

E
Eragonz91
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));
    }
}
}

P
Person x Person212

您可以添加 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();

}

S
Serhat Türkman

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

    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();
    })

t
theblang

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


r
ricardopereira

为您的 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.


G
Gastón Saillén

科特林

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,然后根据需要设置肯定按钮


J
Jaisuriya R

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

第 1 步:将对话框打开代码放入方法 (或 C 中的函数)
第 2 步:在 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();

}

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


H
Hiren Patel

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

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

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 Dailog 正面按钮的 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
        }

    }
}

完毕


N
Navneeth T

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

alertDial.setCancelable(false);

来自文档:“设置此对话框是否可以使用 BACK 键取消。”这与关闭对话框的肯定按钮无关..
对我不起作用,单击肯定按钮时仍然关闭
这与OP无关
不解决问题