Ось декілька рішень для всіх типів діалогів, включаючи рішення для 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 (). Після натискання кнопки звичайний обробник кнопок пересилає подію натискання на той обробник, який ви передали в setButton (), а потім виклики відхиляє діалогове вікно.
Якщо ви хочете не допустити закриття діалогового вікна при натисканні однієї з цих кнопок, вам слід замінити звичайний обробник кнопок на фактичний вигляд кнопки. Оскільки він призначений в OnCreate (), ви повинні замінити його після виклику реалізації за замовчуванням OnCreate (). OnCreate викликається в процесі методу show (). Ви можете створити спеціальний клас діалогового вікна та замінити OnCreate () для виклику super.OnCreate (), а потім замінить обробники кнопок, але якщо ви зробите спеціальний діалог, ви не отримаєте Builder безкоштовно, і в цьому випадку в чому справа ?
Отже, використовуючи діалоговий вигляд, як він розроблений, але контролюючи його відхилення, одним із підходів є спочатку викликати dialog.Show (), потім отримати посилання на кнопку, використовуючи dialog.getButton (), щоб змінити обробник кліків. Іншим підходом є використання setOnShowListener () та реалізація пошуку перегляду кнопок та заміни обробника в OnShowListener. Функціональна різниця між цими двома - «майже» нульова, залежно від того, який потік створює екземпляр діалога. Переглядаючи вихідний код, onShowListener викликається повідомленням, розміщеним на обробнику, що працює на потоці, який створив це діалогове вікно. Отже, оскільки ваш OnShowListener викликається повідомленням, розміщеним у черзі повідомлень, технічно можливо, що виклик слухача затримується на деякий час після завершення шоу.
Тому я вважаю, що найбезпечнішим підходом є перший: викликати show.Dialog (), потім негайно в тому ж шляху виконання замінити обробники кнопок. Оскільки ваш код, який викликає show (), буде працювати в головному потоці графічного інтерфейсу, це означає, що будь-який код, за яким ви слідуєте show (), буде виконуватися перед будь-яким іншим кодом у цьому потоці, тоді як час використання методу OnShowListener знаходиться на милі черга повідомлень.