Aquí hay algunas soluciones para todo tipo de cuadros de diálogo, incluida una solución para AlertDialog.Builder que funcionará en todos los niveles de API (funciona por debajo de API 8, que la otra respuesta aquí no). Hay soluciones para AlertDialogs usando AlertDialog.Builder, DialogFragment y DialogPreference.
A continuación se muestran los ejemplos de código que muestran cómo anular el controlador de botón común predeterminado y evitar que el cuadro de diálogo se cierre para estas diferentes formas de cuadros de diálogo. Todos los ejemplos muestran cómo evitar que el botón positivo cierre el cuadro de diálogo.
Nota: Una descripción de cómo funciona el cierre del diálogo debajo del capó para las clases base de Android y por qué se eligen los siguientes enfoques a continuación de los ejemplos, para aquellos que desean más detalles
AlertDialog.Builder - Cambiar el controlador de botón predeterminado inmediatamente después de mostrar ()
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 - anular 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 - anular 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.
}
});
}
Explicación de enfoques:
Mirando a través del código fuente de Android, la implementación predeterminada de AlertDialog funciona al registrar un controlador de botones común para todos los botones reales en OnCreate (). Cuando se hace clic en un botón, el controlador de botón común reenvía el evento de clic a cualquier controlador que haya pasado en setButton () y luego llama para cerrar el diálogo.
Si desea evitar que se cierre un cuadro de diálogo cuando se presiona uno de estos botones, debe reemplazar el controlador de botón común para la vista real del botón. Debido a que está asignado en OnCreate (), debe reemplazarlo después de que se llame a la implementación predeterminada de OnCreate (). Se llama a OnCreate en el proceso del método show (). Puede crear una clase de diálogo personalizada y anular OnCreate () para llamar al super.OnCreate () y luego anular los controladores de botones, pero si crea un diálogo personalizado, no obtendrá el generador de forma gratuita, en cuyo caso cuál es el punto ?
Por lo tanto, al usar un cuadro de diálogo de la forma en que está diseñado pero con controlar cuándo se descarta, un enfoque es llamar al cuadro de diálogo.Show () primero, luego obtener una referencia al botón usando dialog.getButton () para anular el controlador de clic. Otro enfoque es usar setOnShowListener () e implementar la búsqueda de la vista del botón y reemplazar el controlador en OnShowListener. La diferencia funcional entre los dos es 'casi' nula, dependiendo de qué hilo cree originalmente la instancia de diálogo. Mirando a través del código fuente, onShowListener es llamado por un mensaje publicado en un controlador que se ejecuta en el hilo que creó ese diálogo. Por lo tanto, dado que su OnShowListener es llamado por un mensaje publicado en la cola de mensajes, es técnicamente posible que la llamada a su escucha se demore algún tiempo después de que se complete la presentación.
Por lo tanto, creo que el enfoque más seguro es el primero: llamar a show.Dialog (), luego, inmediatamente en la misma ruta de ejecución, reemplace los controladores de botones. Dado que su código que llama a show () estará operando en el hilo principal de la GUI, significa que cualquier código que siga show () se ejecutará antes que cualquier otro código en ese hilo, mientras que el tiempo del método OnShowListener está a merced de La cola de mensajes.