Voici quelques solutions pour tous les types de boîtes de dialogue, y compris une solution pour AlertDialog.Builder qui fonctionnera sur tous les niveaux d'API (fonctionne en dessous de l'API 8, ce que l'autre réponse ici ne fait pas). Il existe des solutions pour les dialogues AlertDialog utilisant AlertDialog.Builder, DialogFragment et DialogPreference.
Les exemples de code ci-dessous montrent comment remplacer le gestionnaire de bouton commun par défaut et empêcher la fermeture de la boîte de dialogue pour ces différentes formes de boîtes de dialogue. Tous les exemples montrent comment empêcher le bouton positif de fermer la boîte de dialogue.
Remarque : Une description du fonctionnement de la fermeture de la boîte de dialogue pour les classes Android de base et des raisons pour lesquelles les approches suivantes ont été choisies suit les exemples, pour ceux qui veulent plus de détails.
AlertDialog.Builder - Modifier le gestionnaire de bouton par défaut immédiatement après 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 - surcharge 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.
}
});
}
}
Préférence de dialogue - surcharge 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.
}
});
}
Explication des approches :
En examinant le code source d'Android, l'implémentation par défaut d'AlertDialog fonctionne en enregistrant un gestionnaire de bouton commun à tous les boutons actuels dans OnCreate(). Lorsqu'un bouton est cliqué, le gestionnaire de bouton commun transmet l'événement de clic au gestionnaire que vous avez passé dans setButton(), puis appelle la boîte de dialogue.
Si vous souhaitez empêcher la fermeture d'une boîte de dialogue lorsque vous appuyez sur l'un de ces boutons, vous devez remplacer le gestionnaire de bouton commun par la vue réelle du bouton. Comme il est affecté dans OnCreate(), vous devez le remplacer après l'appel de l'implémentation OnCreate() par défaut. OnCreate est appelé dans le processus de la méthode show(). Vous pourriez créer une classe de dialogue personnalisée et surcharger OnCreate() pour appeler super.OnCreate() puis surcharger les gestionnaires de bouton, mais si vous créez un dialogue personnalisé, vous n'obtenez pas le Builder gratuitement, auquel cas quel est l'intérêt ?
Ainsi, pour utiliser une boîte de dialogue de la manière dont elle a été conçue, mais en contrôlant le moment où elle est rejetée, une approche consiste à appeler d'abord dialog.Show(), puis à obtenir une référence au bouton en utilisant dialog.getButton() pour remplacer le gestionnaire de clic. Une autre approche consiste à utiliser setOnShowListener() et à implémenter la recherche de la vue du bouton et le remplacement du gestionnaire dans le OnShowListener. La différence fonctionnelle entre les deux est "presque" nulle, selon le thread qui crée initialement l'instance de dialogue. En regardant le code source, le OnShowListener est appelé par un message posté à un handler s'exécutant sur le thread qui a créé ce dialogue. Ainsi, puisque votre OnShowListener est appelé par un message posté dans la file d'attente des messages, il est techniquement possible que l'appel de votre écouteur soit retardé quelque temps après la fin de l'affichage.
Par conséquent, je pense que l'approche la plus sûre est la première : appeler show.Dialog(), puis immédiatement dans le même chemin d'exécution remplacer les gestionnaires de boutons. Puisque le code qui appelle show() fonctionnera sur le thread principal de l'interface graphique, cela signifie que le code qui suit show() sera exécuté avant tout autre code sur ce thread, alors que le timing de la méthode OnShowListener est à la merci de la file d'attente des messages.