Hier sind einige Lösungen für alle Arten von Dialogen, einschließlich einer Lösung für AlertDialog.Builder, die auf allen API-Ebenen funktioniert (funktioniert unter API 8, was die andere Antwort hier nicht tut). Es gibt Lösungen für AlertDialogs mit AlertDialog.Builder, DialogFragment, und DialogPreference.
Nachfolgend finden Sie Code-Beispiele, die zeigen, wie Sie den Standard-Button-Handler überschreiben und das Schließen des Dialogs für diese verschiedenen Formen von Dialogen verhindern können. Alle Beispiele zeigen, wie man verhindert, dass die positive Schaltfläche den Dialog schließt.
Hinweis: Eine Beschreibung, wie das Schließen des Dialogs unter der Haube für die Android-Basisklassen funktioniert und warum die folgenden Ansätze gewählt wurden, folgt nach den Beispielen, für diejenigen, die mehr Details wünschen
AlertDialog.Builder - Ändern Sie den Standard-Button-Handler sofort nach 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 - Überschreibung 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 - Überschreibung 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.
}
});
}
Erläuterung der Ansätze:
Wenn man sich den Android-Quellcode ansieht, funktioniert die AlertDialog-Standardimplementierung, indem ein gemeinsamer Button-Handler für alle aktuellen Buttons in OnCreate() registriert wird. Wenn eine Schaltfläche angeklickt wird, leitet der gemeinsame Button-Handler das Klick-Ereignis an den Handler weiter, den Sie in setButton() übergeben haben, und beendet dann den Dialog.
Wenn Sie verhindern möchten, dass ein Dialogfeld geschlossen wird, wenn eine dieser Schaltflächen gedrückt wird, müssen Sie den allgemeinen Schaltflächen-Handler für die aktuelle Ansicht der Schaltfläche ersetzen. Da er in OnCreate() zugewiesen wird, müssen Sie ihn ersetzen, nachdem die Standardimplementierung von OnCreate() aufgerufen wurde. OnCreate wird während des Prozesses der show()-Methode aufgerufen. Sie könnten eine benutzerdefinierte Dialogklasse erstellen und OnCreate() überschreiben, um super.OnCreate() aufzurufen und dann die Schaltflächenhandler zu überschreiben, aber wenn Sie einen benutzerdefinierten Dialog erstellen, erhalten Sie den Builder nicht kostenlos, was ist dann der Sinn?
Um ein Dialogfeld so zu verwenden, wie es entworfen wurde, aber mit der Kontrolle, wann es beendet wird, besteht ein Ansatz darin, zuerst dialog.Show() aufzurufen und dann einen Verweis auf die Schaltfläche mit dialog.getButton() zu erhalten, um den Click-Handler zu überschreiben. Ein anderer Ansatz besteht darin, setOnShowListener() zu verwenden und das Auffinden der Schaltflächenansicht und das Ersetzen des Handlers im OnShowListener zu implementieren. Der funktionale Unterschied zwischen den beiden ist "fast" gleich, je nachdem, welcher Thread die Dialoginstanz ursprünglich erstellt. Wenn man sich den Quellcode ansieht, wird der OnShowListener durch eine Nachricht aufgerufen, die an einen Handler gesendet wird, der in dem Thread läuft, der den Dialog erstellt hat. Da Ihr OnShowListener also durch eine Nachricht in der Nachrichtenwarteschlange aufgerufen wird, ist es technisch möglich, dass der Aufruf Ihres Listeners einige Zeit nach Beendigung der Show verzögert wird.
Daher glaube ich, dass der sicherste Ansatz der erste ist: show.Dialog() aufzurufen und dann sofort im gleichen Ausführungspfad die Button-Handler zu ersetzen. Da Ihr Code, der show() aufruft, auf dem Haupt-GUI-Thread ausgeführt wird, bedeutet dies, dass der Code, der auf show() folgt, vor jedem anderen Code auf diesem Thread ausgeführt wird, während das Timing der OnShowListener-Methode von der Nachrichtenwarteschlange abhängt.