Вот некоторые решения для всех типов диалогов, включая решение для 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 зависит от очередь сообщений.