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

How to prevent a dialog from closing when a button is clicked

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


编辑:这仅适用于API 8+,如某些评论所述。

这是一个迟到的答案,但您可以将一个onShowListener添加到AlertDialog,然后您可以覆盖该按钮的onClickListener。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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();


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

下面是代码示例,演示如何覆盖默认的公共按钮处理程序,并防止对话框关闭这些不同形式的对话框。所有示例都显示了如何防止正面按钮关闭对话框。

注意:对于那些想要了解更多细节的人来说,对于基本的android类以及如何选择以下方法,对话框关闭的工作方式的说明如下:

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@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()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@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.
                }
            });
}

方法说明:

Looking through Android source code the AlertDialog default implementation works by registering a common button handler to all the actual buttons in OnCreate(). When a button is clicked the common button handler forwards the click event to whatever handler you passed in setButton() then calls dismisses the dialog.

If you wish to prevent a dialog box from closing when one of these buttons is pressed you must replace the common button handler for the actual view of the button. Because it is assigned in OnCreate(), you must replace it after the default OnCreate() implementation is called. OnCreate is called in the process of the show() method. You could create a custom Dialog class and override OnCreate() to call the super.OnCreate() then override the button handlers, but if you make a custom dialog you don't get the Builder for free, in which case what is the point?

So, in using a dialog the way it is designed but with controlling when it is dismissed, one approach is to call dialog.Show() first, then obtain a reference to the button using dialog.getButton() to override the click handler. Another approach is to use setOnShowListener() and implement finding the button view and replacing the handler in the OnShowListener. The functional difference between the two is 'almost' nill, depending on what thread originally creates the dialog instance. Looking through the source code, the onShowListener gets called by a message posted to a handler running on the thread that created that dialog. So, since your OnShowListener is called by a message posted on the message queue it is technically possible that calling your listener is delayed some time after show completes.

Therefore, I believe the safest approach is the first: to call show.Dialog(), then immediately in the same execution path replace the button handlers. Since your code that calls show() will be operating on the main GUI thread, it means whatever code you follow show() with will be executed before any other code on that thread, whereas the timing of the OnShowListener method is at the mercy of the message queue.


我编写了一个简单的类(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上进行了测试,所以它应该可以正常工作。
如果您发现一些问题,请在此处评论。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
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的小例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 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();

干杯,

Yuvi


另一种解决方案

我想从UX的角度提出另一个答案。

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

这里的其他答案提供了许多技巧来覆盖正面按钮点击。如果这很重要,Android会不会做出方便的方法呢?他们没有。

相反,Dialogs设计指南显示了这种情况的一个例子。在用户做出选择之前,"确定"按钮被禁用。根本不需要压倒一切的技巧。对用户来说显而易见的是,在继续之前还需要做一些事情。

enter image description here

如何禁用正面按钮

请参阅Android文档以创建自定义对话框布局。它建议您将AlertDialog放在DialogFragment内。然后,您需要做的就是在布局元素上设置侦听器,以了解何时启用或禁用肯定按钮。

  • 如果您的自定义对话框有单选按钮,则使用RadioGroup.OnCheckedChangeListener。
  • 如果您的自定义对话框有复选框,则使用CompoundButton.OnCheckedChangeListener。
  • 如果您的自定义对话框具有EditText,则使用TextWatcher。

正面按钮可以像这样禁用:

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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);
    }
}

可以从以下活动运行自定义对话框:

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

笔记

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

    1
    2
    3
    4
    5
    6
    7
    view.post(new Runnable() {
        @Override
        public void run() {
            AlertDialog dialog = (AlertDialog) getDialog();
            dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
        }
    });

有关

  • 带有一个,两个和三个按钮的Android Alert对话框
  • 如何在Android警报对话框中显示列表视图?

如果你使用的是DialogFragment - 这是处理Dialogs的推荐方法。

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

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

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

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

1
2
3
4
5
6
7
8
9
@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(否则会忽略对话框):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@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,直到显示对话框为止!

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


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

  • 在创建对话框期间将OnClickListener设置为null
  • 然后在显示对话框后设置OnClickListener

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

  • 调用AlertDialog的show()后获取按钮
  • 设置按钮'OnClickListener如下(我认为稍微可读)。
  • 码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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);
        }
    });


    对于API 8之前,我使用布尔标志,解除侦听器和再次调用dialog.show来解决问题,如果editText的内容不正确的话。像这样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    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;


    这个链接的答案是一个简单的解决方案,它可以直接与API 3兼容。它与Tom Bollwitt的解决方案非常相似,但不使用兼容性较差的OnShowListener。

    Yes, you can. You basically need to:

  • Create the dialog with DialogBuilder
  • show() the dialog
  • Find the buttons in the dialog shown and override their onClickListener
  • 自从我扩展EditTextPreference以来,我对Kamen的代码做了一些小修改。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    @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)));
    }

    好好玩!


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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    @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
                    }
                }
            });
        }
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    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));
        }
    }
    }

    你可以添加builder.show();在返回之前验证消息之后;

    像这样

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
        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();

    }


    对于ProgressDialogs

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    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.

        }

    });

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

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

    这是我的代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    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();

        }

    这是我的连接管理器代码

    1
    2
    3
    4
    5
    6
     private boolean ifConnected()
    {
        ConnectivityManager connectivityManager= (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo=connectivityManager.getActiveNetworkInfo();
       return networkInfo!=null && networkInfo.isConnected();
    }


    如果您使用的是material design,我建议您查看材料对话框。它为我解决了与当前打开的Android错误相关的几个问题(参见78088),但最重要的是,对于此票证,它有一个autoDismiss标志,可以在使用Builder时设置。


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

    1
    alertDial.setCancelable(false);


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

    例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    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:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    <?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>

    这是最终结果。


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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    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:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    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
            }

        }
    }

    完成