So verhindern Sie, dass ein Dialogfeld geschlossen wird, wenn auf eine Schaltfläche geklickt wird

732

Ich habe einen Dialog mit EditTextzur Eingabe. Wenn ich im Dialogfeld auf die Schaltfläche "Ja" klicke, wird die Eingabe überprüft und das Dialogfeld geschlossen. Wenn die Eingabe jedoch falsch ist, möchte ich im selben Dialog bleiben. Unabhängig von der Eingabe sollte der Dialog jedes Mal automatisch geschlossen werden, wenn ich auf die Schaltfläche "Nein" klicke. Wie kann ich das deaktivieren? Übrigens habe ich PositiveButton und NegativeButton für die Schaltfläche im Dialogfeld verwendet.

user304881
quelle

Antworten:

916

BEARBEITEN: Dies funktioniert nur mit API 8+, wie in einigen Kommentaren angegeben.

Dies ist eine späte Antwort, aber Sie können dem AlertDialog einen onShowListener hinzufügen, in dem Sie den onClickListener der Schaltfläche überschreiben können.

final AlertDialog dialog = new AlertDialog.Builder(context)
        .setView(v)
        .setTitle(R.string.my_title)
        .setPositiveButton(android.R.string.ok, null) //Set to null. We override the onclick
        .setNegativeButton(android.R.string.cancel, null)
        .create();

dialog.setOnShowListener(new DialogInterface.OnShowListener() {

    @Override
    public void onShow(DialogInterface dialogInterface) {

        Button button = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE);
        button.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View view) {
                // TODO Do something

                //Dismiss once everything is OK.
                dialog.dismiss();
            }
        });
    }
});
dialog.show();
Tom Bollwitt
quelle
7
Hey, besser spät als nie, ich habe genau danach gesucht, danke, +1 :) Dies ist eine elegante Möglichkeit, Ihrem Dialog eine Validierung hinzuzufügen, insbesondere wenn Sie bereits eine Helfer-Wrapping-Klasse haben, die sich mit Warnungen befasst
Guillaume,
11
Funktioniert nicht AlertDialog.Builder.setOnShowListener existiert nicht. developer.android.com/reference/android/app/…
Leandros
4
Mit API vor 8 können Sie d.getButton (AlertDialog.BUTTON_POSITIVE) aufrufen. wie es öffentliche Methode ist, aber Sie müssen es show () nennen; wurde ausgestellt, sonst bekommst
du
13
Sie können dies sogar ein bisschen sauberer machen, indem Sie einen Null-OnClickListener für den Dialog-Builder festlegen (speichert den leeren Listener "// dies wird überschrieben").
Steve Haley
1
Funktioniert auch gut mit DialogFragments, wenn der AlertDialog in der Methode onCreateDialog (Bundle savedInstanceState) erstellt wird.
Christian Lischnig
655

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.

Im Folgenden finden Sie Codebeispiele, die zeigen, wie Sie den Standardhandler für allgemeine Schaltflächen überschreiben und verhindern, dass der Dialog für diese verschiedenen Formen von Dialogen geschlossen wird. Alle Beispiele zeigen, wie verhindert wird, dass die positive Schaltfläche den Dialog schließt.

Hinweis: Eine Beschreibung, wie das Schließen des Dialogfelds unter der Haube für die Basis-Android-Klassen funktioniert und warum die folgenden Ansätze ausgewählt werden, folgt nach den Beispielen für diejenigen, die weitere Details wünschen


AlertDialog.Builder - Ändert den Standard-Button-Handler unmittelbar 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 - überschreibe 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 - überschreibe 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.
                }
            });
}

Erklärung der Ansätze:

Beim Durchsuchen des Android-Quellcodes registriert die Standardimplementierung von AlertDialog einen gemeinsamen Schaltflächenhandler für alle tatsächlichen Schaltflächen in OnCreate (). Wenn auf eine Schaltfläche geklickt wird, leitet der allgemeine Schaltflächenhandler das Klickereignis an den Handler weiter, den Sie in setButton () übergeben haben, und ruft dann den Dialog ab.

Wenn Sie verhindern möchten, dass ein Dialogfeld geschlossen wird, wenn eine dieser Schaltflächen gedrückt wird, müssen Sie den allgemeinen Schaltflächenhandler für die tatsächliche Ansicht der Schaltfläche ersetzen. Da es in OnCreate () zugewiesen ist, müssen Sie es ersetzen, nachdem die Standardimplementierung von OnCreate () aufgerufen wurde. OnCreate wird im Verlauf der show () -Methode aufgerufen. Sie können eine benutzerdefinierte Dialogklasse erstellen und OnCreate () überschreiben, um super.OnCreate () aufzurufen, und dann die Schaltflächenhandler überschreiben. Wenn Sie jedoch ein benutzerdefiniertes Dialogfeld erstellen, erhalten Sie den Builder nicht kostenlos. In diesem Fall geht es darum ?

Wenn Sie ein Dialogfeld so verwenden, wie es entworfen wurde, aber steuern, wann es geschlossen wird, besteht ein Ansatz darin, zuerst dialog.Show () aufzurufen und dann mithilfe von dialog.getButton () einen Verweis auf die Schaltfläche abzurufen, um den Klick-Handler zu überschreiben. Ein anderer Ansatz besteht darin, setOnShowListener () zu verwenden und das Suchen der Schaltflächenansicht und das Ersetzen des Handlers im OnShowListener zu implementieren. Der funktionale Unterschied zwischen den beiden ist "fast" gleich Null, je nachdem, welcher Thread ursprünglich die Dialoginstanz erstellt. Beim Durchsuchen des Quellcodes wird der onShowListener von einer Nachricht aufgerufen, die an einen Handler gesendet wird, der in dem Thread ausgeführt wird, der diesen Dialog erstellt hat. Da Ihr OnShowListener von einer in der Nachrichtenwarteschlange geposteten Nachricht aufgerufen wird, ist es technisch möglich, dass sich der Anruf Ihres Listeners einige Zeit nach Abschluss der Show verzögert.

Daher glaube ich, dass der sicherste Ansatz der erste ist: show.Dialog () aufrufen und dann sofort im selben Ausführungspfad die Schaltflächenhandler ersetzen. Da Ihr Code, der show () aufruft, auf dem Haupt-GUI-Thread ausgeführt wird, bedeutet dies, dass der Code, dem Sie show () folgen, vor jedem anderen Code in diesem Thread ausgeführt wird, während das Timing der OnShowListener-Methode von abhängig ist die Nachrichtenwarteschlange.

Sogger
quelle
12
Dies ist bei weitem die einfachste Implementierung und funktioniert perfekt. Ich habe AlertDialog.Builder verwendet - Ändere den Standard-Button-Handler unmittelbar nach show () und es funktioniert wie ein Zauber.
Reinherd
1
@sogger Alter, ich habe deine erstaunliche Antwort total kühn bearbeitet, weil du in Abschnitt 1 entlassen hast (); anstatt ich glaube dialog.dismiss (); Vielen Dank für die tolle Antwort!
Fattie
Gibt es eine Möglichkeit, das Schließen eines zu verhindern, ProgressDialogwenn eine Schaltfläche darauf geklickt wird?
Joshua Pinter
1
Heilige Kuh, je mehr ich über Android weiß, desto mehr bin ich angewidert ... all dies nur, damit ein einfacher Dialog richtig funktioniert. Es dauert Stunden, um herauszufinden, wie ein Dialog
angezeigt wird
1
@harsh_v hat die Antwort aktualisiert, um onResume () für die nächste Person zu verwenden, danke!
Sogger
37

Eine alternative Lösung

Ich möchte eine alternative Antwort aus UX-Sicht präsentieren.

Warum möchten Sie verhindern, dass ein Dialogfeld geschlossen wird, wenn auf eine Schaltfläche geklickt wird? Vermutlich liegt es daran, dass Sie einen benutzerdefinierten Dialog haben, in dem der Benutzer noch keine Auswahl getroffen oder noch nicht alles vollständig ausgefüllt hat. Und wenn sie noch nicht fertig sind, sollten Sie ihnen nicht erlauben, überhaupt auf die positive Schaltfläche zu klicken. Deaktivieren Sie es einfach, bis alles fertig ist.

Die anderen Antworten hier geben viele Tricks, um den positiven Klick auf die Schaltfläche zu überschreiben. Wenn das wichtig wäre, hätte Android dann nicht eine bequeme Methode dafür entwickelt? Sie haben es nicht getan.

Stattdessen zeigt der Designleitfaden für Dialoge ein Beispiel für eine solche Situation. Die Schaltfläche OK ist deaktiviert, bis der Benutzer eine Auswahl trifft. Es sind überhaupt keine übergeordneten Tricks erforderlich. Für den Benutzer ist es offensichtlich, dass noch etwas getan werden muss, bevor er fortfährt.

Geben Sie hier die Bildbeschreibung ein

So deaktivieren Sie die positive Taste

Informationen zum Erstellen eines benutzerdefinierten Dialoglayouts finden Sie in der Android-Dokumentation . Es wird empfohlen, dass Sie Ihre AlertDialogin einem platzieren DialogFragment. Anschließend müssen Sie nur noch Listener für die Layoutelemente festlegen, um zu wissen, wann die positive Schaltfläche aktiviert oder deaktiviert werden muss.

Die positive Taste kann folgendermaßen deaktiviert werden:

AlertDialog dialog = (AlertDialog) getDialog();
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);

Hier ist eine vollständige Arbeit DialogFragmentmit einer deaktivierten positiven Schaltfläche, wie sie im obigen Bild verwendet werden kann.

import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;

public class MyDialogFragment extends DialogFragment {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {

        // inflate the custom dialog layout
        LayoutInflater inflater = getActivity().getLayoutInflater();
        View view = inflater.inflate(R.layout.my_dialog_layout, null);

        // add a listener to the radio buttons
        RadioGroup radioGroup = (RadioGroup) view.findViewById(R.id.radio_group);
        radioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup radioGroup, int i) {
                // enable the positive button after a choice has been made
                AlertDialog dialog = (AlertDialog) getDialog();
                dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(true);
            }
        });

        // build the alert dialog
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setView(view)
                .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int id) {
                        // TODO: use an interface to pass the user choice back to the activity
                    }
                })
                .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        MyDialogFragment.this.getDialog().cancel();
                    }
                });
        return builder.create();
    }

    @Override
    public void onResume() {
        super.onResume();

        // disable positive button by default
        AlertDialog dialog = (AlertDialog) getDialog();
        dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
    }
}

Der benutzerdefinierte Dialog kann über eine Aktivität wie die folgende ausgeführt werden:

MyDialogFragment dialog = new MyDialogFragment();
dialog.show(getFragmentManager(), "MyTag");

Anmerkungen

  • Der Kürze halber habe ich die Kommunikationsschnittstelle weggelassen, um die Benutzerauswahlinformationen an die Aktivität zurückzugeben. Die Dokumentation zeigt jedoch, wie dies gemacht wird.
  • Der Button ist immer noch nullin onCreateDialogso dass ich es in deaktiviert onResume. Dies hat den unerwünschten Effekt, dass es erneut deaktiviert wird, wenn der Benutzer zu einer anderen App wechselt und dann zurückkehrt, ohne den Dialog zu schließen. Dies könnte gelöst werden, indem auch die Auswahl der Benutzer aufgehoben wird oder indem ein Runnablevon onCreateDialogaufgerufen wird, um die Schaltfläche in der nächsten Ausführungsschleife zu deaktivieren.

    view.post(new Runnable() {
        @Override
        public void run() {
            AlertDialog dialog = (AlertDialog) getDialog();
            dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
        }
    });

verbunden

Suragch
quelle
33

Ich habe eine einfache Klasse (einen AlertDialogBuilder) geschrieben, mit der Sie die automatische Entlassungsfunktion deaktivieren können, wenn Sie auf die Schaltflächen des Dialogfelds klicken.

Es ist auch mit Android 1.6 kompatibel, verwendet also nicht den OnShowListener (der nur als API> = 8 verfügbar ist).

Anstatt AlertDialog.Builder zu verwenden, können Sie diesen CustomAlertDialogBuilder verwenden. Der wichtigste Teil ist, dass Sie nicht create () aufrufen sollten , sondern nur die show () -Methode. Ich habe Methoden wie setCanceledOnTouchOutside () und setOnDismissListener hinzugefügt damit Sie sie weiterhin direkt im Builder festlegen können.

Ich habe es auf Android 1.6, 2.x, 3.x und 4.x getestet, damit es ziemlich gut funktioniert. Wenn Sie Probleme haben, kommentieren Sie diese bitte hier.

package com.droidahead.lib.utils;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.view.View;
import android.view.View.OnClickListener;

public class CustomAlertDialogBuilder extends AlertDialog.Builder {
    /**
     * Click listeners
     */
    private DialogInterface.OnClickListener mPositiveButtonListener = null;
    private DialogInterface.OnClickListener mNegativeButtonListener = null;
    private DialogInterface.OnClickListener mNeutralButtonListener = null;

    /**
     * Buttons text
     */
    private CharSequence mPositiveButtonText = null;
    private CharSequence mNegativeButtonText = null;
    private CharSequence mNeutralButtonText = null;

    private DialogInterface.OnDismissListener mOnDismissListener = null;

    private Boolean mCancelOnTouchOutside = null;

    public CustomAlertDialogBuilder(Context context) {
        super(context);
    }

    public CustomAlertDialogBuilder setOnDismissListener (DialogInterface.OnDismissListener listener) {
        mOnDismissListener = listener;
        return this;
    }

    @Override
    public CustomAlertDialogBuilder setNegativeButton(CharSequence text, DialogInterface.OnClickListener listener) {
        mNegativeButtonListener = listener;
        mNegativeButtonText = text;
        return this;
    }

    @Override
    public CustomAlertDialogBuilder setNeutralButton(CharSequence text, DialogInterface.OnClickListener listener) {
        mNeutralButtonListener = listener;
        mNeutralButtonText = text;
        return this;
    }

    @Override
    public CustomAlertDialogBuilder setPositiveButton(CharSequence text, DialogInterface.OnClickListener listener) {
        mPositiveButtonListener = listener;
        mPositiveButtonText = text;
        return this;
    }

    @Override
    public CustomAlertDialogBuilder setNegativeButton(int textId, DialogInterface.OnClickListener listener) {
        setNegativeButton(getContext().getString(textId), listener);
        return this;
    }

    @Override
    public CustomAlertDialogBuilder setNeutralButton(int textId, DialogInterface.OnClickListener listener) {
        setNeutralButton(getContext().getString(textId), listener);
        return this;
    }

    @Override
    public CustomAlertDialogBuilder setPositiveButton(int textId, DialogInterface.OnClickListener listener) {
        setPositiveButton(getContext().getString(textId), listener);
        return this;
    }

    public CustomAlertDialogBuilder setCanceledOnTouchOutside (boolean cancelOnTouchOutside) {
        mCancelOnTouchOutside = cancelOnTouchOutside;
        return this;
    }



    @Override
    public AlertDialog create() {
        throw new UnsupportedOperationException("CustomAlertDialogBuilder.create(): use show() instead..");
    }

    @Override
    public AlertDialog show() {
        final AlertDialog alertDialog = super.create();

        DialogInterface.OnClickListener emptyOnClickListener = new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) { }
        };


        // Enable buttons (needed for Android 1.6) - otherwise later getButton() returns null
        if (mPositiveButtonText != null) {
            alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, mPositiveButtonText, emptyOnClickListener);
        }

        if (mNegativeButtonText != null) {
            alertDialog.setButton(AlertDialog.BUTTON_NEGATIVE, mNegativeButtonText, emptyOnClickListener);
        }

        if (mNeutralButtonText != null) {
            alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, mNeutralButtonText, emptyOnClickListener);
        }

        // Set OnDismissListener if available
        if (mOnDismissListener != null) {
            alertDialog.setOnDismissListener(mOnDismissListener);
        }

        if (mCancelOnTouchOutside != null) {
            alertDialog.setCanceledOnTouchOutside(mCancelOnTouchOutside);
        }

        alertDialog.show();

        // Set the OnClickListener directly on the Button object, avoiding the auto-dismiss feature
        // IMPORTANT: this must be after alert.show(), otherwise the button doesn't exist..
        // If the listeners are null don't do anything so that they will still dismiss the dialog when clicked
        if (mPositiveButtonListener != null) {
            alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    mPositiveButtonListener.onClick(alertDialog, AlertDialog.BUTTON_POSITIVE);
                }
            });
        }

        if (mNegativeButtonListener != null) {
            alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE).setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    mNegativeButtonListener.onClick(alertDialog, AlertDialog.BUTTON_NEGATIVE);
                }
            });
        }

        if (mNeutralButtonListener != null) {
            alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    mNeutralButtonListener.onClick(alertDialog, AlertDialog.BUTTON_NEUTRAL);
                }
            });
        }

        return alertDialog;
    }   
}

BEARBEITEN Hier ist ein kleines Beispiel für die Verwendung des CustomAlertDialogBuilder:

// Create the CustomAlertDialogBuilder
CustomAlertDialogBuilder dialogBuilder = new CustomAlertDialogBuilder(context);

// Set the usual data, as you would do with AlertDialog.Builder
dialogBuilder.setIcon(R.drawable.icon);
dialogBuilder.setTitle("Dialog title");
dialogBuilder.setMessage("Some text..");

// Set your buttons OnClickListeners
dialogBuilder.setPositiveButton ("Button 1", new DialogInterface.OnClickListener() {
    public void onClick (DialogInterface dialog, int which) {
        // Do something...

        // Dialog will not dismiss when the button is clicked
        // call dialog.dismiss() to actually dismiss it.
    }
});

// By passing null as the OnClickListener the dialog will dismiss when the button is clicked.               
dialogBuilder.setNegativeButton ("Close", null);

// Set the OnDismissListener (if you need it)       
dialogBuilder.setOnDismissListener(new DialogInterface.OnDismissListener() {
    public void onDismiss(DialogInterface dialog) {
        // dialog was just dismissed..
    }
});

// (optional) set whether to dismiss dialog when touching outside
dialogBuilder.setCanceledOnTouchOutside(false);

// Show the dialog
dialogBuilder.show();

Prost,

Yuvi

YuviDroid
quelle
Nett. Hat aber bei mir nicht funktioniert. Der Dialog wird trotzdem entlassen.
Leandros
Mmm das klingt komisch. Ich verwende das in meiner App und nur Schaltflächen, bei denen ich explizit dialog.dismiss () aufrufe, schließen den Dialog. Auf welcher Android-Version testen Sie? Können Sie Ihren Code anzeigen, wo Sie den CustomAlertDialogBuilder verwendet haben?
YuviDroid
Ich denke , es ist aus diesem Grund verursacht: (Call dialog.show () nach OnClickListener) pastebin.com/uLnSu5v7 Wenn ich positiveButton klicken sie entlassen erhalten , wenn boolean wahr ist ...
Leandros
Ich habe es nicht mit Activity.onCreateDialog () getestet. Wahrscheinlich kann es so nicht funktionieren. Ich werde die 'Antwort' bearbeiten, um ein kleines Beispiel für meine Verwendung aufzunehmen.
YuviDroid
4
Das funktioniert bei mir mit der aktuellen Bearbeitung! Allerdings: Noch eine Einschränkung. Builder.getContext () ist nur für API 11+ verfügbar. Fügen Sie ein Feld hinzu Context mContextund legen Sie es stattdessen im Konstruktor fest.
Oleg Vaskevich
28

Hier ist etwas, wenn Sie verwenden DialogFragment- dies ist die empfohlene Methode, um mit Dialogen umzugehen.

Was mit der setButton()Methode von AlertDialog passiert (und ich stelle mir das auch mit AlertDialogBuilder's setPositiveButton()und vor setNegativeButton()), ist, dass die Schaltfläche, die Sie (z. B. AlertDialog.BUTTON_POSITIVE) damit setzen, OnClickListenerbeim Drücken tatsächlich ZWEI verschiedene Objekte auslöst .

Wobei der erste DialogInterface.OnClickListener , die ein Parameter ist setButton(), setPositiveButton()und setNegativeButton().

Der andere ist View.OnClickListener , der so eingestellt wird, dass er Sie automatisch schließt , AlertDialogwenn eine seiner Tasten gedrückt wird - und von AlertDialogselbst festgelegt wird.

Was Sie tun können, ist, setButton()mit nullals DialogInterface.OnClickListenerzu verwenden, die Schaltfläche zu erstellen und dann Ihre benutzerdefinierte Aktionsmethode darin aufzurufen View.OnClickListener. Zum Beispiel,

@Override
public Dialog onCreateDialog(Bundle savedInstanceState)
{
    AlertDialog alertDialog = new AlertDialog(getActivity());
    // set more items...
    alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, "OK", null);

    return alertDialog;
}

Dann können Sie die Standardeinstellung überschreiben AlertDialog‚s Tasten‘ View.OnClickListener(was sonst den Dialog entlassen würde) in der DialogFragment‚s - onResume()Methode:

@Override
public void onResume()
{
    super.onResume();
    AlertDialog alertDialog = (AlertDialog) getDialog();
    Button okButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
    okButton.setOnClickListener(new View.OnClickListener() { 
        @Override
        public void onClick(View v)
        {
            performOkButtonAction();
        }
    });
}

private void performOkButtonAction() {
    // Do your stuff here
}

Sie werden dies in der festlegen müssen onResume()Methode , weil getButton()zurückkehren wird , nullbis der Dialog angezeigt wurde!

Dies sollte dazu führen, dass Ihre benutzerdefinierte Aktionsmethode nur einmal aufgerufen wird und der Dialog standardmäßig nicht geschlossen wird.

Zhuiguang Liu
quelle
21

Inspiriert von Toms Antwort glaube ich, dass die Idee hier ist:

  • Stellen Sie die onClickListenerwährend der Erstellung des Dialogs auf einnull
  • Stellen Sie dann ein ein, onClickListenernachdem der Dialog angezeigt wird.

Sie können den onShowListenergleichen Tom überschreiben . Alternativ können Sie

  1. Holen Sie sich die Schaltfläche nach dem Aufruf von AlertDialog's show()
  2. Stellen Sie die Tasten onClickListenerwie folgt ein (etwas besser lesbar, denke ich).

Code:

AlertDialog.Builder builder = new AlertDialog.Builder(context);
// ...
final AlertDialog dialog = builder.create();
dialog.show();
// now you can override the default onClickListener
Button b = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
b.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Log.i(TAG, "ok button is clicked");
        handleClick(dialog);
    }
});
ericn
quelle
8

Für Pre-API 8 habe ich das Problem mit einem Booleschen Flag, einem Entlassungs-Listener und einem erneuten Aufruf von dialog.show gelöst, falls der Inhalt des editText nicht korrekt war. So was:

case ADD_CLIENT:
        LayoutInflater factoryClient = LayoutInflater.from(this);
        final View EntryViewClient = factoryClient.inflate(
                R.layout.alert_dialog_add_client, null);

        EditText ClientText = (EditText) EntryViewClient
                .findViewById(R.id.client_edit);

        AlertDialog.Builder builderClient = new AlertDialog.Builder(this);
        builderClient
                .setTitle(R.string.alert_dialog_client)
                .setCancelable(false)
                .setView(EntryViewClient)
                .setPositiveButton("Save",
                        new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog,
                                    int whichButton) {
                                EditText newClient = (EditText) EntryViewClient
                                        .findViewById(R.id.client_edit);
                                String newClientString = newClient
                                        .getText().toString();
                                if (checkForEmptyFields(newClientString)) {
                                    //If field is empty show toast and set error flag to true;
                                    Toast.makeText(getApplicationContext(),
                                            "Fields cant be empty",
                                            Toast.LENGTH_SHORT).show();
                                    add_client_error = true;
                                } else {
                                    //Here save the info and set the error flag to false
                                    add_client_error = false;
                                }
                            }
                        })
                .setNegativeButton("Cancel",
                        new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog,
                                    int id) {
                                add_client_error = false;
                                dialog.cancel();
                            }
                        });
        final AlertDialog alertClient = builderClient.create();
        alertClient.show();

        alertClient
                .setOnDismissListener(new DialogInterface.OnDismissListener() {

                    @Override
                    public void onDismiss(DialogInterface dialog) {
                        //If the error flag was set to true then show the dialog again
                        if (add_client_error == true) {
                            alertClient.show();
                        } else {
                            return;
                        }

                    }
                });
        return true;
Steve
quelle
seltsam onDismiss nicht angerufen werden, meine ist API Level 21
duckduckgo
7

Die Antwort unter diesem Link ist eine einfache Lösung, die direkt mit API 3 kompatibel ist. Sie ähnelt der Lösung von Tom Bollwitt sehr, verwendet jedoch nicht den weniger kompatiblen OnShowListener.

Ja, du kannst. Sie müssen grundsätzlich:

  1. Erstellen Sie den Dialog mit DialogBuilder
  2. zeige () den Dialog
  3. Suchen Sie die Schaltflächen im angezeigten Dialogfeld und überschreiben Sie deren onClickListener

Ich habe kleinere Anpassungen an Kamens Code vorgenommen, seit ich eine EditTextPreference erweitert habe.

@Override
protected void showDialog(Bundle state) {
  super.showDialog(state);

  class mocl implements OnClickListener{
    private final AlertDialog dialog;
    public mocl(AlertDialog dialog) {
          this.dialog = dialog;
      }
    @Override
    public void onClick(View v) {

        //checks if EditText is empty, and if so tells the user via Toast
        //otherwise it closes dialog and calls the EditTextPreference's onClick
        //method to let it know that the button has been pressed

        if (!IntPreference.this.getEditText().getText().toString().equals("")){
        dialog.dismiss();
        IntPreference.this.onClick(dialog,DialogInterface.BUTTON_POSITIVE);
        }
        else {
            Toast t = Toast.makeText(getContext(), "Enter a number!", Toast.LENGTH_SHORT);
            t.show();
        }

    }
  }

  AlertDialog d = (AlertDialog) getDialog();
  Button b = d.getButton(DialogInterface.BUTTON_POSITIVE);
  b.setOnClickListener(new mocl((d)));
}

So ein Spaß!

lukeuser
quelle
4

Dieser Code wird für Sie funktionieren, da ich ein ähnliches Problem hatte und dies für mich funktioniert hat. :) :)

1- Überschreiben Sie die Onstart () -Methode in Ihrer Fragment-Dialog-Klasse.

@Override
public void onStart() {
    super.onStart();
    final AlertDialog D = (AlertDialog) getDialog();
    if (D != null) {
        Button positive = (Button) D.getButton(Dialog.BUTTON_POSITIVE);
        positive.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View arg0) {
                if (edittext.equals("")) {
   Toast.makeText(getActivity(), "EditText empty",Toast.LENGTH_SHORT).show();
                } else {
                D.dismiss(); //dissmiss dialog
                }
            }
        });
    }
}
Luis Nuñez
quelle
3

Für ProgressDialogs

Um zu verhindern, dass der Dialog automatisch geschlossen wird, müssen Sie Folgendes einstellen, OnClickListenernachdem das ProgressDialogangezeigt wird:

connectingDialog = new ProgressDialog(this);

connectingDialog.setCancelable(false);
connectingDialog.setCanceledOnTouchOutside(false);

// Create the button but set the listener to a null object.
connectingDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", 
        (DialogInterface.OnClickListener) null )

// Show the dialog so we can then get the button from the view.
connectingDialog.show();

// Get the button from the view.
Button dialogButton = connectingDialog.getButton( DialogInterface.BUTTON_NEGATIVE);

// Set the onClickListener here, in the view.
dialogButton.setOnClickListener( new View.OnClickListener() {

    @Override
    public void onClick ( View v ) {

        // Dialog will not get dismissed until you call dismiss() explicitly.

    }

});
Joshua Pinter
quelle
3
public class ComentarDialog extends DialogFragment{
private EditText comentario;

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {

    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

    LayoutInflater inflater = LayoutInflater.from(getActivity());
    View v = inflater.inflate(R.layout.dialog_comentar, null);
    comentario = (EditText)v.findViewById(R.id.etxt_comentar_dialog);

    builder.setTitle("Comentar")
           .setView(v)
           .setPositiveButton("OK", null)
           .setNegativeButton("CANCELAR", new DialogInterface.OnClickListener() {
               public void onClick(DialogInterface dialog, int id) {

               }
           });

    return builder.create();
}

@Override
public void onStart() {
    super.onStart();

    //Obtenemos el AlertDialog
    AlertDialog dialog = (AlertDialog)getDialog();

    dialog.setCanceledOnTouchOutside(false);
    dialog.setCancelable(false);//Al presionar atras no desaparece

    //Implementamos el listener del boton OK para mostrar el toast
    dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if(TextUtils.isEmpty(comentario.getText())){
               Toast.makeText(getActivity(), "Ingrese un comentario", Toast.LENGTH_SHORT).show();
               return;
            }
            else{
                ((AlertDialog)getDialog()).dismiss();
            }
        }
    });

    //Personalizamos
    Resources res = getResources();

    //Buttons
    Button positive_button = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
    positive_button.setBackground(res.getDrawable(R.drawable.btn_selector_dialog));

    Button negative_button =  dialog.getButton(DialogInterface.BUTTON_NEGATIVE);
    negative_button.setBackground(res.getDrawable(R.drawable.btn_selector_dialog));

    int color = Color.parseColor("#304f5a");

    //Title
    int titleId = res.getIdentifier("alertTitle", "id", "android");
    View title = dialog.findViewById(titleId);
    if (title != null) {
        ((TextView) title).setTextColor(color);
    }

    //Title divider
    int titleDividerId = res.getIdentifier("titleDivider", "id", "android");
    View titleDivider = dialog.findViewById(titleDividerId);
    if (titleDivider != null) {
        titleDivider.setBackgroundColor(res.getColor(R.color.list_menu_divider));
    }
}
}
Eragonz91
quelle
3

Sie können builder.show () hinzufügen; nach der Validierungsnachricht vor der Rückkehr;

so was

    public void login()
{
    final AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setView(R.layout.login_layout);
    builder.setTitle("Login");



    builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener()
    {
        @Override
        public void onClick(DialogInterface dialog, int id)
        {
            dialog.cancel();
        }
    });// put the negative button before the positive button, so it will appear

    builder.setPositiveButton("Ok", new DialogInterface.OnClickListener()
    {
        @Override
        public void onClick(DialogInterface dialog, int id)
        {
            Dialog d = (Dialog) dialog;
            final EditText etUserName = (EditText) d.findViewById(R.id.etLoginName);
            final EditText etPassword = (EditText) d.findViewById(R.id.etLoginPassword);
            String userName = etUserName.getText().toString().trim();
            String password = etPassword.getText().toString().trim();

            if (userName.isEmpty() || password.isEmpty())
            {

                Toast.makeText(getApplicationContext(),
                        "Please Fill all fields", Toast.LENGTH_SHORT).show();
                builder.show();// here after validation message before retrun
                               //  it will reopen the dialog
                              // till the user enter the right condition
                return;
            }

            user = Manager.get(getApplicationContext()).getUserByName(userName);

            if (user == null)
            {
                Toast.makeText(getApplicationContext(),
                        "Error ethier username or password are wrong", Toast.LENGTH_SHORT).show();
                builder.show();
                return;
            }
            if (password.equals(user.getPassword()))
            {
                etPassword.setText("");
                etUserName.setText("");
                setLogged(1);
                setLoggedId(user.getUserId());
                Toast.makeText(getApplicationContext(),
                        "Successfully logged in", Toast.LENGTH_SHORT).show();
               dialog.dismiss();// if every thing is ok then dismiss the dialog
            }
            else
            {
                Toast.makeText(getApplicationContext(),
                        "Error ethier username or password are wrong", Toast.LENGTH_SHORT).show();
                builder.show();
                return;
            }

        }
    });

    builder.show();

}
Person x Person212
quelle
3

So verhindern Sie, dass das Dialogfeld beim Klicken geschlossen wird und nur geschlossen wird, wenn das Internet verfügbar ist

Ich versuche das Gleiche zu tun, da ich nicht möchte, dass das Dialogfeld geschlossen wird, bis das Internet verbunden ist.

Hier ist mein Code:

AlertDialog.Builder builder=new AlertDialog.Builder(MainActivity.this); builder.setTitle("Internet Not Connected");
    if(ifConnected()){

        Toast.makeText(this, "Connected or not", Toast.LENGTH_LONG).show();
    }
    else{
        builder.setPositiveButton("Retry", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
               if(!ifConnected())
               {
                   builder.show();
               }
            }
        }).setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                finish();
            }
        });
        builder.show();

    }

Und hier ist mein Connectivity Manager-Code:

 private boolean ifConnected()
{
    ConnectivityManager connectivityManager= (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo=connectivityManager.getActiveNetworkInfo();
   return networkInfo!=null && networkInfo.isConnected();
}
karan1.singh
quelle
Das ist klug, aber ich bekomme folgende Fehlermeldung:The specified child already has a parent. You must call removeView() on the child's parent first
Dan Chaltiel
2

Wenn Sie verwenden, material designwürde ich vorschlagen, Material-Dialoge zu überprüfen . Es hat einige Probleme für mich behoben, die mit aktuell geöffneten Android-Fehlern zusammenhängen (siehe 78088 ), aber am wichtigsten für dieses Ticket ist, autoDismissdass es ein Flag hat, das bei Verwendung von gesetzt werden kann Builder.

theblang
quelle
1

Verwenden Sie ein benutzerdefiniertes Layout für Ihre DialogFragmentund fügen Sie LinearLayoutunter Ihren Inhalten ein Layout hinzu , das als randlos gestaltet werden kann, um mit Google Material Design übereinzustimmen. Suchen Sie dann die neu erstellten Schaltflächen und überschreiben Sie deren OnClickListener.

Beispiel:

public class AddTopicFragment extends DialogFragment {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        // Get the layout inflater
        LayoutInflater inflater = getActivity().getLayoutInflater();
        final View dialogView = inflater.inflate(R.layout.dialog_add_topic, null);

        Button saveTopicDialogButton = (Button) dialogView.findViewById(R.id.saveTopicDialogButton);
        Button cancelSaveTopicDialogButton = (Button) dialogView.findViewById(R.id.cancelSaveTopicDialogButton);

        final AppCompatEditText addTopicNameET = (AppCompatEditText) dialogView.findViewById(R.id.addTopicNameET);
        final AppCompatEditText addTopicCreatedByET = (AppCompatEditText) dialogView.findViewById(R.id.addTopicCreatedByET);

        saveTopicDialogButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // validate inputs
                if(addTopicNameET.getText().toString().trim().isEmpty()){
                    addTopicNameET.setError("Topic name can't be empty");
                    addTopicNameET.requestFocus();
                }else if(addTopicCreatedByET.getText().toString().trim().isEmpty()){
                    addTopicCreatedByET.setError("Topic created by can't be empty");
                    addTopicCreatedByET.requestFocus();
                }else {
                    // save topic to database
                    Topic topic = new Topic();
                    topic.name = addTopicNameET.getText().toString().trim();
                    topic.createdBy = addTopicCreatedByET.getText().toString().trim();
                    topic.createdDate = new Date().getTime();
                    topic.save();
                    AddTopicFragment.this.dismiss();
                }
            }
        });

        cancelSaveTopicDialogButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                AddTopicFragment.this.dismiss();
            }
        });

        // Inflate and set the layout for the dialog
        // Pass null as the parent view because its going in the dialog layout
        builder.setView(dialogView)
               .setMessage(getString(R.string.add_topic_message));

        return builder.create();
    }

}

dialog_add_topic.xml :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:padding="@dimen/activity_horizontal_margin"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.design.widget.TextInputLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:errorEnabled="true">

        <android.support.v7.widget.AppCompatEditText
            android:id="@+id/addTopicNameET"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Topic Name"
            android:inputType="textPersonName"
            android:maxLines="1" />

    </android.support.design.widget.TextInputLayout>

    <android.support.design.widget.TextInputLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:errorEnabled="true">

        <android.support.v7.widget.AppCompatEditText
            android:id="@+id/addTopicCreatedByET"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Created By"
            android:inputType="textPersonName"
            android:maxLines="1" />

    </android.support.design.widget.TextInputLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <Button
            android:text="@string/cancel"
            android:layout_weight="1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/cancelSaveTopicDialogButton"
            style="@style/Widget.AppCompat.Button.ButtonBar.AlertDialog" />

        <Button
            android:text="@string/save"
            android:layout_weight="1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/saveTopicDialogButton"
            style="@style/Widget.AppCompat.Button.ButtonBar.AlertDialog" />

    </LinearLayout>


</LinearLayout>

Dies ist das Endergebnis.

Ibrahim Hassan
quelle
0

Es könnte auf einfachste Weise gebaut werden:

Warndialog mit benutzerdefinierter Ansicht und mit zwei Schaltflächen (positiv und negativ).

AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()).setTitle(getString(R.string.select_period));
builder.setPositiveButton(getString(R.string.ok), null);

 builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {

    // Click of Cancel Button

   }
 });

  LayoutInflater li = LayoutInflater.from(getActivity());
  View promptsView = li.inflate(R.layout.dialog_date_picker, null, false);
  builder.setView(promptsView);

  DatePicker startDatePicker = (DatePicker)promptsView.findViewById(R.id.startDatePicker);
  DatePicker endDatePicker = (DatePicker)promptsView.findViewById(R.id.endDatePicker);

  final AlertDialog alertDialog = builder.create();
  alertDialog.show();

  Button theButton = alertDialog.getButton(DialogInterface.BUTTON_POSITIVE);
  theButton.setOnClickListener(new CustomListener(alertDialog, startDatePicker, endDatePicker));

CustomClickLister of Positive Button von Alert Dailog :

private class CustomListener implements View.OnClickListener {
        private final Dialog dialog;
        private DatePicker mStartDp, mEndDp;
    public CustomListener(Dialog dialog, DatePicker dS, DatePicker dE) {
        this.dialog = dialog;
        mStartDp = dS;
        mEndDp = dE;
    }

    @Override
    public void onClick(View v) {

        int day1  = mStartDp.getDayOfMonth();
        int month1= mStartDp.getMonth();
        int year1 = mStartDp.getYear();
        Calendar cal1 = Calendar.getInstance();
        cal1.set(Calendar.YEAR, year1);
        cal1.set(Calendar.MONTH, month1);
        cal1.set(Calendar.DAY_OF_MONTH, day1);


        int day2  = mEndDp.getDayOfMonth();
        int month2= mEndDp.getMonth();
        int year2 = mEndDp.getYear();
        Calendar cal2 = Calendar.getInstance();
        cal2.set(Calendar.YEAR, year2);
        cal2.set(Calendar.MONTH, month2);
        cal2.set(Calendar.DAY_OF_MONTH, day2);

        if(cal2.getTimeInMillis()>=cal1.getTimeInMillis()){
            dialog.dismiss();
            Log.i("Dialog", "Dismiss");
            // Condition is satisfied so do dialog dismiss
            }else {
            Log.i("Dialog", "Do not Dismiss");
            // Condition is not satisfied so do not dialog dismiss
        }

    }
}

Erledigt

Hiren Patel
quelle
-1

Dies ist wahrscheinlich eine sehr späte Antwort, aber die Verwendung von setCancelable reicht aus.

alertDial.setCancelable(false);
Navneeth T.
quelle
10
Aus den Dokumenten: "Legt fest, ob dieser Dialog mit der Taste ZURÜCK abgebrochen werden kann." Dies hat nichts mit dem positiven Knopf zu tun, der den Dialog
schließt
3
Funktioniert nicht für mich, immer noch entlassen, wenn Sie auf den positiven Button klicken
Hugo
1
Dies hat nichts mit dem OP
MatPag
1
Adressiert keine Frage
Kevin Crain