Как предотвратить закрытие диалога при нажатии кнопки

732

У меня есть диалог с EditTextдля ввода. Когда я нажимаю кнопку «Да» в диалоговом окне, он проверяет ввод, а затем закрывает диалоговое окно. Однако, если ввод неправильный, я хочу остаться в том же диалоге. Каждый раз, независимо от того, что ввод, диалог должен автоматически закрываться, когда я нажимаю кнопку «нет». Как я могу отключить это? Кстати, я использовал PositiveButton и NegativeButton для кнопки в диалоге.

user304881
источник

Ответы:

916

РЕДАКТИРОВАТЬ: Это работает только на 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();
Том Боллвитт
источник
7
Эй, лучше поздно, чем никогда, я искал именно это, спасибо, +1 :) Это элегантный способ добавить проверку в ваш диалог, особенно если у вас уже есть класс-помощник для работы с оповещениями
Гийом
11
Не работает AlertDialog.Builder.setOnShowListener не существует. developer.android.com/reference/android/app/...
Leandros
4
С API pre 8 вы можете вызвать d.getButton (AlertDialog.BUTTON_POSITIVE); так как это публичный метод, но вы должны вызывать его show (); был выдан, иначе вы просто получите
ноль
13
Вы можете сделать это даже немного чище, установив null OnClickListener для построителя диалогов (сохраняет пустой «// это будет переопределено» слушатель).
Стив Хейли
1
Также отлично работает с DialogFragments, когда AlertDialog создается в методе onCreateDialog (Bundle saveInstanceState).
Кристиан Лишниг
655

Вот некоторые решения для всех типов диалогов, включая решение для AlertDialog.Builder, которое будет работать на всех уровнях API (работает ниже API 8, а другой ответ здесь нет). Существуют решения для AlertDialogs, использующие AlertDialog.Builder, DialogFragment и DialogPreference.

Ниже приведены примеры кода, показывающие, как переопределить стандартный обработчик общих кнопок и предотвратить закрытие диалога для этих различных форм диалогов. Все примеры показывают, как предотвратить закрытие диалогового окна положительной кнопкой.

Примечание: описание того, как закрытие диалога работает под капотом для базовых классов 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 (). При нажатии кнопки общий обработчик кнопки перенаправляет событие click любому обработчику, который вы передали в setButton (), а затем вызывает, закрывает диалоговое окно.

Если вы хотите предотвратить закрытие диалогового окна при нажатии одной из этих кнопок, вы должны заменить общий обработчик кнопки для фактического вида кнопки. Поскольку он назначается в OnCreate (), его необходимо заменить после вызова реализации OnCreate () по умолчанию. OnCreate вызывается в процессе метода show (). Вы можете создать собственный класс Dialog и переопределить OnCreate () для вызова super.OnCreate (), а затем переопределить обработчики кнопок, но если вы создадите пользовательский диалог, вы не получите Builder бесплатно, в этом случае какой смысл ?

Таким образом, при использовании диалога в том виде, в котором он разработан, но с контролем, когда он отклоняется, одним из подходов является сначала вызвать dialog.Show (), а затем получить ссылку на кнопку с помощью dialog.getButton () для переопределения обработчика щелчка. Другой подход заключается в использовании setOnShowListener () и реализации поиска кнопки и замены обработчика в OnShowListener. Функциональное различие между ними почти равно нулю, в зависимости от того, какой поток изначально создает экземпляр диалога. Просматривая исходный код, onShowListener вызывается сообщением, отправленным обработчику, запущенному в потоке, который создал это диалоговое окно. Итак, поскольку ваш OnShowListener вызывается сообщением, размещенным в очереди сообщений, технически возможно, что вызов вашего слушателя задерживается через некоторое время после завершения шоу.

Поэтому я считаю, что самый безопасный подход - первый: вызвать show.Dialog (), а затем немедленно в том же пути выполнения заменить обработчики кнопок. Так как ваш код, вызывающий show (), будет работать в основном потоке графического интерфейса, это означает, что любой код, которым вы следуете с show (), будет выполняться перед любым другим кодом в этом потоке, в то время как синхронизация метода OnShowListener зависит от очередь сообщений.

Sogger
источник
12
Это, безусловно, самая простая реализация и работает отлично. Я использовал AlertDialog.Builder - измените обработчик кнопки по умолчанию сразу после show (), и он работает как шарм.
Райнхард
1
@ sogger чувак, я смело отредактировал твой изумительный ответ, потому что в разделе 1 ты уволил (); вместо того, чтобы верить dialog.dismiss (); Большое спасибо за отличный ответ!
Толстяк
Есть ли способ предотвратить закрытие ProgressDialogпри нажатии на кнопку?
Джошуа Пинтер
1
Святая корова, чем больше я знаю об Android, тем больше чувствую отвращение ... все это только для того, чтобы простой диалог работал правильно. Чтобы понять, как отобразить диалоговое окно,
нужны часы
1
@harsh_v обновил ответ, чтобы использовать onResume () для следующего человека, спасибо!
Sogger
37

Альтернативное решение

Я хотел бы представить альтернативный ответ с точки зрения UX.

Почему вы хотите запретить закрытие диалогового окна при нажатии кнопки? Предположительно, это потому, что у вас есть пользовательский диалог, в котором пользователь не сделал выбор или еще не полностью заполнил все. И если они не закончены, то вы не должны позволять им нажимать на положительную кнопку вообще. Просто отключите его, пока все не будет готово.

Другие ответы здесь дают много приемов для переопределения положительного нажатия кнопки. Если бы это было важно сделать, разве Android не сделал бы удобный способ сделать это? Они не

Вместо этого в руководстве по разработке Dialogs приведен пример такой ситуации. Кнопка ОК отключена, пока пользователь не сделает выбор. Никаких главных трюков не требуется вообще. Для пользователя очевидно, что что-то еще нужно сделать, прежде чем продолжать.

введите описание изображения здесь

Как отключить положительную кнопку

См. Документацию по Android для создания пользовательского макета диалога . Он рекомендует вам разместить 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");

Ноты

  • Для краткости я пропустил интерфейс связи, чтобы передать информацию о выборе пользователя обратно в действие. В документации показано , как это делается, хотя.
  • Кнопка все еще nullвключена, onCreateDialogпоэтому я отключил ее onResume. Это приводит к нежелательному эффекту его повторного отключения, если пользователь переключается на другое приложение и затем возвращается, не закрывая диалоговое окно. Эту проблему можно решить, отменив выбор любого пользователя или вызвав команду Runnablefrom, onCreateDialogчтобы отключить кнопку в следующем цикле выполнения.

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

связанные с

Suragch
источник
33

Я написал простой класс (AlertDialogBuilder), который можно использовать, чтобы отключить функцию автоматического закрытия при нажатии кнопок диалогового окна.

Он также совместим с Android 1.6, поэтому он не использует OnShowListener (который доступен только API> = 8).

Таким образом, вместо использования AlertDialog.Builder вы можете использовать этот CustomAlertDialogBuilder. Наиболее важной частью является то, что вы не должны вызывать 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();

Ура,

Yuvi

YuviDroid
источник
Ницца. Но у меня не сработало. Диалог все же распускают.
Леандрос
Ммм, это звучит странно. Я использую это в своем приложении, и только кнопки, где я явно вызываю dialog.dismiss (), закрывают диалог. На какой версии Android вы тестируете? Можете ли вы показать свой код, где вы использовали CustomAlertDialogBuilder?
YuviDroid
Я думаю, что это вызвано из-за этого: (вызову dialog.show () после onClickListener) pastebin.com/uLnSu5v7 Если я нажимаю positiveButton, они отклоняются, если логическое значение истинно ...
Леандрос
Я не проверял это с помощью Activity.onCreateDialog (). Вероятно, это не может работать таким образом. Я отредактирую «ответ», чтобы включить небольшой пример того, как я его использую.
YuviDroid
4
Это работает для меня с текущим редактированием! Однако: еще одна оговорка. Builder.getContext () доступен только для API 11+. Добавьте поле Context mContextи установите его в конструкторе.
Олег Васкевич
28

Вот кое-что, если вы используете DialogFragment- который в любом случае является рекомендуемым способом обработки диалогов.

Что происходит с setButton()методом AlertDialog (и я представляю то же самое с AlertDialogBuilders setPositiveButton()и 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;
}

Затем вы можете переопределить AlertDialogкнопки по умолчанию View.OnClickListener(которые в противном случае закрыли бы диалог) в методе DialogFragments onResume():

@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до тех пор, пока не будет показано диалоговое окно!

Это должно привести к тому, что ваш метод пользовательского действия будет вызываться только один раз, и диалоговое окно по умолчанию не будет закрыто.

Чжуйгуан Лю
источник
21

Вдохновленный ответом Тома, я думаю, что идея здесь такова:

  • Установите onClickListenerво время создания диалогаnull
  • Затем установите onClickListenerпосле того, как диалоговое окно отображается.

Вы можете переопределить 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);
    }
});
ericn
источник
8

Для предварительного API 8 я решил проблему, используя логический флаг, прослушиватель dismiss и снова вызвал dialog.show, если в случае содержимое editText было неправильным. Нравится:

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;
Стив
источник
странно onDisiss не получить вызов, мой API-уровень 21
duckduckgo
7

Ответ по этой ссылке - простое решение, которое совместимо прямо с API 3. Оно очень похоже на решение Тома Боллвитта, но без использования менее совместимого OnShowListener.

Да, ты можешь. Вам в основном нужно:

  1. Создать диалог с DialogBuilder
  2. показать () диалог
  3. Найдите кнопки в показанном диалоге и переопределите их onClickListener

Я сделал небольшие изменения в коде Kamen, так как я расширял EditTextPreference.

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

Так весело!

lukeuser
источник
4

Этот код будет работать для вас, потому что у меня была схожая проблема, и это сработало для меня. :)

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
                }
            }
        });
    }
}
Луис Нуньес
источник
3

Для ProgressDialogs

Чтобы предотвратить автоматическое закрытие диалога, вы должны установить OnClickListenerпосле того ProgressDialog, как отображается, например:

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.

    }

});
Джошуа Пинтер
источник
3
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));
    }
}
}
Eragonz91
источник
3

Вы можете добавить 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();

}
Человек х Человек212
источник
3

Чтобы диалоговое окно не закрывалось при нажатии, оно должно закрываться только при наличии Интернета

Я пытаюсь сделать то же самое, поскольку я не хочу, чтобы диалоговое окно было закрыто до тех пор, пока интернет не подключен.

Вот мой код:

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();
}
karan1.singh
источник
Это умно, но я получаю это сообщение об ошибке:The specified child already has a parent. You must call removeView() on the child's parent first
Дэн Чалтиэль
2

Если вы используете, material designя бы предложил проверить диалоги материалов . Он исправил несколько проблем, связанных с открытыми в данный момент ошибками Android (см. 78088 ), но самое главное для этого тикета - autoDismissфлаг, который можно установить при использовании Builder.

theblang
источник
1

Используйте пользовательский макет для вашего 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>

Это конечный результат.

Ибрагим Хасан
источник
0

Это может быть построено самым простым способом:

Диалог оповещений с пользовательским видом и двумя кнопками (положительный и отрицательный).

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

CustomClickLister из Positive Баттона из оповещения Dailog :

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
        }

    }
}

Выполнено

Хирен Патель
источник
-1

Это, вероятно, очень поздний ответ, но использование setCancelable поможет.

alertDial.setCancelable(false);
Навнет Т
источник
10
Из документов: «Устанавливает, можно ли отменить это диалоговое окно клавишей BACK». Это не имеет ничего общего с положительной кнопкой,
закрывающей
3
Не работает для меня, все еще отклоняя при нажатии положительной кнопки
Хьюго
1
Это не имеет ничего общего с ОП
MatPag
1
Не отвечает на вопрос
Кевин Крейн