Ecco alcune soluzioni per tutti i tipi di finestre di dialogo, inclusa una soluzione per AlertDialog.Builder che funzionerà a tutti i livelli di API (funziona al di sotto dell'API 8, a cui l'altra risposta non corrisponde). Esistono soluzioni per AlertDialogs che utilizzano AlertDialog.Builder, DialogFragment e DialogPreference.
Di seguito sono riportati degli esempi di codice che mostrano come sovrascrivere il gestore dei pulsanti comuni predefinito e impedire la chiusura della finestra di dialogo per queste diverse forme di finestre di dialogo. Tutti gli esempi mostrano come impedire al pulsante positivo di chiudere la finestra di dialogo.
Nota: una descrizione di come funziona la chiusura della finestra di dialogo sotto il cofano per le classi Android di base e perché vengono scelti i seguenti approcci dopo gli esempi, per coloro che desiderano maggiori dettagli
AlertDialog.Builder: modifica il gestore pulsanti predefinito immediatamente dopo 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 - override 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 - override 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.
}
});
}
Spiegazione degli approcci:
Esaminando il codice sorgente Android, l'implementazione predefinita di AlertDialog funziona registrando un gestore di pulsanti comune su tutti i pulsanti effettivi in OnCreate (). Quando si fa clic su un pulsante, il gestore pulsanti comune inoltra l'evento click a qualsiasi gestore passato in setButton (), quindi le chiamate chiudono la finestra di dialogo.
Se si desidera impedire la chiusura di una finestra di dialogo quando si preme uno di questi pulsanti, è necessario sostituire il gestore dei pulsanti comune per la visualizzazione effettiva del pulsante. Poiché è assegnato in OnCreate (), è necessario sostituirlo dopo aver chiamato l'implementazione predefinita di OnCreate (). OnCreate viene chiamato nel processo del metodo show (). È possibile creare una classe Dialog personalizzata e sovrascrivere OnCreate () per chiamare super.OnCreate () quindi sovrascrivere i gestori dei pulsanti, ma se si crea una finestra di dialogo personalizzata non si ottiene il Builder gratuitamente, nel qual caso qual è il punto ?
Quindi, usando una finestra di dialogo nel modo in cui è progettata, ma controllando quando viene respinta, un approccio consiste nel chiamare dialog.Show () prima, quindi ottenere un riferimento al pulsante usando dialog.getButton () per sovrascrivere il gestore di clic. Un altro approccio è utilizzare setOnShowListener () e implementare la ricerca della vista pulsanti e la sostituzione del gestore in OnShowListener. La differenza funzionale tra i due è "quasi" nulla, a seconda di quale thread crea originariamente l'istanza di dialogo. Esaminando il codice sorgente, onShowListener viene chiamato da un messaggio inviato a un gestore in esecuzione sul thread che ha creato quella finestra di dialogo. Pertanto, poiché OnShowListener viene chiamato da un messaggio pubblicato nella coda dei messaggi, è tecnicamente possibile che la chiamata del proprio ascoltatore venga ritardata un po 'di tempo dopo il completamento dello spettacolo.
Pertanto, credo che l'approccio più sicuro sia il primo: chiamare show.Dialog (), quindi immediatamente nello stesso percorso di esecuzione sostituire i gestori di pulsanti. Dato che il tuo codice che chiama show () funzionerà sul thread della GUI principale, significa che qualunque codice tu segua show () verrà eseguito prima di qualsiasi altro codice su quel thread, mentre i tempi del metodo OnShowListener sono in balia di la coda dei messaggi.