Comment empêcher une boîte de dialogue de se fermer lorsqu'un bouton est cliqué


732

J'ai une boîte de dialogue avec EditTextpour entrée. Lorsque je clique sur le bouton "oui" de la boîte de dialogue, cela validera l'entrée, puis fermera la boîte de dialogue. Cependant, si l'entrée est incorrecte, je veux rester dans la même boîte de dialogue. Chaque fois, quelle que soit l'entrée, la boîte de dialogue doit se fermer automatiquement lorsque je clique sur le bouton "non". Comment puis-je désactiver cela? Par ailleurs, j'ai utilisé PositiveButton et NegativeButton pour le bouton de dialogue.

Réponses:


916

EDIT: Cela ne fonctionne que sur l'API 8+ comme indiqué par certains des commentaires.

Il s'agit d'une réponse tardive, mais vous pouvez ajouter un onShowListener au AlertDialog où vous pouvez ensuite remplacer le onClickListener du bouton.

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();

7
Hé, mieux vaut tard que jamais, je cherchais exactement cela, merci, +1 :) C'est une façon élégante d'ajouter de la validation à votre boîte de dialogue, surtout quand vous avez déjà un cours d'encapsulation d'aide pour gérer les alertes
Guillaume

11
Ça ne marche pas. AlertDialog.Builder.setOnShowListener n'existe pas. developer.android.com/reference/android/app/…
Leandros

4
Avec l'API pre 8, vous pouvez appeler d.getButton (AlertDialog.BUTTON_POSITIVE); car c'est une méthode publique, mais vous devez l'appeler show (); a été émis, sinon zou vient de devenir nul
Hurda

13
Vous pouvez rendre cela encore plus propre en définissant un OnClickListener nul dans le générateur de boîtes de dialogue (enregistre l'écouteur vide "// ce sera remplacé").
Steve Haley

1
Fonctionne également très bien avec DialogFragments lorsque le AlertDialog est créé dans la méthode onCreateDialog (Bundle savedInstanceState).
Christian Lischnig

655

Voici quelques solutions pour tous les types de boîtes de dialogue, y compris une solution pour AlertDialog.Builder qui fonctionnera à 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 AlertDialogs utilisant AlertDialog.Builder, DialogFragment et DialogPreference.

Vous trouverez ci-dessous des exemples de code montrant comment remplacer le gestionnaire de boutons communs par défaut et empêcher la boîte de dialogue de se fermer 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 sous le capot pour les classes Android de base et des raisons pour lesquelles les approches suivantes sont choisies suit les exemples, pour ceux qui souhaitent plus de détails.


AlertDialog.Builder - Modifier le gestionnaire de boutons 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 - remplacer 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 - remplacer 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 parcourant le code source Android, l'implémentation par défaut d'AlertDialog fonctionne en enregistrant un gestionnaire de boutons commun à tous les boutons réels dans OnCreate (). Lorsqu'un bouton est cliqué, le gestionnaire de bouton commun transmet l'événement click au gestionnaire que vous avez passé dans setButton (), puis les appels rejette la boîte de dialogue.

Si vous souhaitez empêcher la fermeture d'une boîte de dialogue lorsque l'un de ces boutons est enfoncé, vous devez remplacer le gestionnaire de bouton commun pour l'affichage réel du bouton. Puisqu'il est attribué 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 pouvez créer une classe Dialog personnalisée et remplacer OnCreate () pour appeler le super.OnCreate () puis remplacer les gestionnaires de boutons, mais si vous créez une boîte de dialogue personnalisée, vous n'obtenez pas le générateur gratuitement, auquel cas quel est le point ?

Ainsi, en utilisant une boîte de dialogue de la manière dont elle est conçue mais en contrôlant quand elle est fermée, une approche consiste à appeler dialog.Show () d'abord, puis à obtenir une référence au bouton à l'aide de dialog.getButton () pour remplacer le gestionnaire de clics. Une autre approche consiste à utiliser setOnShowListener () et à implémenter la recherche de la vue de bouton et le remplacement du gestionnaire dans OnShowListener. La différence fonctionnelle entre les deux est «presque» nulle, selon le thread qui crée à l'origine l'instance de dialogue. En parcourant le code source, onShowListener est appelé par un message publié sur un gestionnaire s'exécutant sur le thread qui a créé cette boîte de dialogue. Ainsi, puisque votre OnShowListener est appelé par un message publié dans la file d'attente de messages, il est techniquement possible que l'appel de votre écouteur soit retardé quelque temps après la fin de l'émission.

Par conséquent, je crois 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. Étant donné que votre code qui appelle show () fonctionnera sur le thread principal de l'interface graphique, cela signifie que le code que vous suivez show () sera exécuté avant tout autre code sur ce thread, tandis que le timing de la méthode OnShowListener est à la merci de la file d'attente de messages.


12
C'est de loin la mise en œuvre la plus simple et fonctionne parfaitement. J'ai utilisé AlertDialog.Builder - Changer le gestionnaire de boutons par défaut immédiatement après show () et cela fonctionne comme un charme.
Reinherd

1
@sogger mec, j'ai totalement audacieusement modifié votre réponse étonnante parce que dans la section 1, vous avez ignoré (); au lieu de je crois dialog.dismiss (); merci beaucoup pour la réponse géniale!
Fattie

Existe-t-il un moyen d'empêcher la fermeture d'un ProgressDialoglorsqu'un bouton est cliqué dessus?
Joshua Pinter

1
Sainte vache, plus j'en connais sur Android, plus je me sens dégoûté ... tout cela pour que le dialogue simple fonctionne correctement. cela prend des heures juste pour comprendre comment afficher une boîte de dialogue
SpaceMonkey

1
@harsh_v a mis à jour la réponse pour utiliser onResume () pour la prochaine personne, merci!
Sogger

37

Une solution alternative

Je voudrais présenter une autre réponse du point de vue UX.

Pourquoi voudriez-vous empêcher une boîte de dialogue de se fermer lorsqu'un bouton est cliqué? Vraisemblablement, c'est parce que vous avez une boîte de dialogue personnalisée dans laquelle l'utilisateur n'a pas fait de choix ou n'a pas encore tout rempli. Et s'ils ne sont pas terminés, vous ne devez pas du tout leur permettre de cliquer sur le bouton positif. Désactivez-le jusqu'à ce que tout soit prêt.

Les autres réponses ici donnent de nombreuses astuces pour remplacer le clic positif du bouton. Si cela était important à faire, Android n'aurait-il pas fait une méthode pratique pour le faire? Ils ne l'ont pas fait.

Au lieu de cela, le guide de conception des boîtes de dialogue montre un exemple d'une telle situation. Le bouton OK est désactivé jusqu'à ce que l'utilisateur fasse un choix. Aucune astuce prioritaire n'est nécessaire du tout. Il est évident pour l'utilisateur que quelque chose doit encore être fait avant de continuer.

entrez la description de l'image ici

Comment désactiver le bouton positif

Consultez la documentation Android pour créer une disposition de boîte de dialogue personnalisée . Il vous recommande de placer votre AlertDialogintérieur a DialogFragment. Ensuite, tout ce que vous devez faire est de définir des écouteurs sur les éléments de mise en page pour savoir quand activer ou désactiver le bouton positif.

Le bouton positif peut être désactivé comme ceci:

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

Voici un travail complet DialogFragmentavec un bouton positif désactivé tel que celui utilisé dans l'image ci-dessus.

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);
    }
}

La boîte de dialogue personnalisée peut être exécutée à partir d'une activité comme celle-ci:

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

Remarques

  • Par souci de concision, j'ai omis l'interface de communication pour transmettre les informations de choix de l'utilisateur à l'activité. La documentation montre cependant comment procéder.
  • Le bouton est toujours nullen place onCreateDialog, je l'ai donc désactivé onResume. Cela a pour effet indésirable de le désactiver à nouveau si l'utilisateur passe à une autre application, puis revient sans fermer la boîte de dialogue. Cela pourrait être résolu en décochant aussi des choix de l' utilisateur ou en appelant un Runnablede onCreateDialogpour désactiver le bouton de la boucle suivante de l' exécution.

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

en relation


33

J'ai écrit une classe simple (AlertDialogBuilder) que vous pouvez utiliser pour désactiver la fonction de suppression automatique lorsque vous appuyez sur les boutons de la boîte de dialogue.

Il est également compatible avec Android 1.6, il n'utilise donc pas OnShowListener (qui n'est disponible que pour l'API> = 8).

Ainsi, au lieu d'utiliser AlertDialog.Builder, vous pouvez utiliser ce CustomAlertDialogBuilder. La partie la plus importante est que vous ne devez pas appeler create () , mais uniquement la méthode show () . J'ai ajouté des méthodes comme setCanceledOnTouchOutside () et setOnDismissListener afin que vous puissiez toujours les définir directement sur le générateur.

Je l'ai testé sur Android 1.6, 2.x, 3.x et 4.x, cela devrait donc fonctionner assez bien. Si vous rencontrez des problèmes, veuillez commenter ici.

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;
    }   
}

EDIT Voici un petit exemple sur la façon d'utiliser le 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();

À votre santé,

Yuvi


Agréable. Mais ça n'a pas marché pour moi. La boîte de dialogue est néanmoins rejetée.
Leandros

Mmmm, cela semble étrange. J'utilise cela dans mon application et seuls les boutons où j'appelle explicitement dialog.dismiss () fermeront la boîte de dialogue. Sur quelle version d'Android vous testez? Pouvez-vous montrer votre code là où vous avez utilisé CustomAlertDialogBuilder?
YuviDroid

Je pense que cela est dû à cela: (appelez dialog.show () après onClickListener) pastebin.com/uLnSu5v7 Si je clique sur positiveButton, ils sont rejetés si booléen est vrai ...
Leandros

Je ne l'ai pas testé en utilisant Activity.onCreateDialog (). Cela ne peut probablement pas fonctionner de cette façon. Je vais modifier la «réponse» pour inclure un petit exemple sur la façon dont je l'utilise.
YuviDroid

4
Cela fonctionne pour moi avec l'édition actuelle! Cependant: une mise en garde de plus. Builder.getContext () n'est disponible que sur l'API 11+. Ajoutez un champ Context mContextet définissez-le à la place dans le constructeur.
Oleg Vaskevich

28

Voici quelque chose si vous utilisez DialogFragment- qui est de toute façon la méthode recommandée pour gérer les dialogues.

Qu'est - ce qui se passe avec de AlertDialog setButton()méthode (et je pense la même chose avec AlertDialogBuilderl » setPositiveButton()et setNegativeButton()) est que le bouton vous définissez (par exemple AlertDialog.BUTTON_POSITIVE) avec elle déclenche réellement deux différents OnClickListenerobjets lorsqu'ils sont pressés.

La première étant DialogInterface.OnClickListener , qui est un paramètre de setButton(), setPositiveButton(), et setNegativeButton().

L'autre est View.OnClickListener , qui sera configuré pour fermer automatiquement votre AlertDialoglorsque l'un de ses boutons est enfoncé - et est défini par AlertDialoglui-même.

Ce que vous pouvez faire est d'utiliser setButton()avec nullcomme DialogInterface.OnClickListener, de créer le bouton, puis d'appeler votre méthode d'action personnalisée à l'intérieur View.OnClickListener. Par exemple,

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

    return alertDialog;
}

Ensuite, vous pouvez remplacer la valeur par défaut AlertDialog« boutons » s View.OnClickListener(qui autrement rejeter la boîte de dialogue) dans le DialogFragmentde onResume()méthode:

@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
}

Vous devrez le définir dans la onResume()méthode, car getButton()il reviendra nulljusqu'à ce que la boîte de dialogue soit affichée!

Cela devrait entraîner que votre méthode d'action personnalisée ne soit appelée qu'une seule fois et la boîte de dialogue ne sera pas fermée par défaut.


21

Inspiré par la réponse de Tom, je crois que l'idée ici est:

  • Réglez le onClickListenerpendant la création de la boîte de dialogue surnull
  • Définissez ensuite un une onClickListenerfois que la boîte de dialogue est affichée.

Vous pouvez remplacer le onShowListenerTom comme. Vous pouvez également

  1. obtenir le bouton après avoir appelé AlertDialog show()
  2. réglez les boutons onClickListenercomme suit (un peu plus lisible je pense).

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);
    }
});

8

Pour la pré API 8, j'ai résolu le problème en utilisant un drapeau booléen, un écouteur de rejet et en appelant dialog.show à nouveau si au cas où le contenu du editText n'était pas correct. Comme ça:

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;

étrange onNe pas se faire appeler, le mien est api niveau 21
duckduckgo

7

La réponse à ce lien est une solution simple, et qui est compatible directement avec l'API 3. Elle est très similaire à la solution de Tom Bollwitt, mais sans utiliser le OnShowListener moins compatible.

Oui, vous pouvez. Vous devez essentiellement:

  1. Créez la boîte de dialogue avec DialogBuilder
  2. afficher () la boîte de dialogue
  3. Recherchez les boutons dans la boîte de dialogue affichée et remplacez leur onClickListener

J'ai apporté des adaptations mineures au code de Kamen depuis que j'étendais un EditTextPreference.

@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)));
}

Si amusant!


4

Ce code fonctionnera pour vous, car j'ai eu un problème similaire et cela a fonctionné pour moi. :)

1- Remplacez la méthode Onstart () dans votre classe de dialogue de fragments.

@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
                }
            }
        });
    }
}

3

Pour ProgressDialogs

Pour éviter que la boîte de dialogue ne soit automatiquement fermée, vous devez définir le OnClickListeneraprès que le ProgressDialogs'affiche, comme ceci:

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.

    }

});

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));
    }
}
}

3

vous pouvez ajouter builder.show (); après message de validation avant retour;

comme ça

    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();

}

3

Pour empêcher la boîte de dialogue de se fermer lorsque vous cliquez dessus et elle ne doit se fermer que lorsque Internet est disponible

J'essaie de faire la même chose, car je ne veux pas que la boîte de dialogue soit fermée tant que Internet n'est pas connecté.

Voici mon 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();

    }

Et voici mon code de gestionnaire de connectivité:

 private boolean ifConnected()
{
    ConnectivityManager connectivityManager= (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo=connectivityManager.getActiveNetworkInfo();
   return networkInfo!=null && networkInfo.isConnected();
}

C'est intelligent, mais je reçois ce message d'erreur:The specified child already has a parent. You must call removeView() on the child's parent first
Dan Chaltiel

2

Si vous utilisez, material designje vous suggère de vérifier les dialogues de matériaux . Il a corrigé plusieurs problèmes liés aux bogues Android actuellement ouverts (voir 78088 ), mais surtout pour ce ticket, il a un autoDismissindicateur qui peut être défini lors de l'utilisation de Builder.


1

Utilisez une mise en page personnalisée pour votre DialogFragmentet ajoutez un LinearLayoutsous votre contenu qui peut être stylé comme sans bordure pour correspondre à Google Material Design. Recherchez ensuite les boutons nouvellement créés et remplacez-les OnClickListener.

Exemple:

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>

Ceci est le résultat final.


0

Il pourrait être construit de la manière la plus simple:

Boîte de dialogue d'alerte avec vue personnalisée et avec deux boutons (positif et négatif).

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 du bouton positif du journal des alertes :

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
        }

    }
}

Terminé


-1

C'est probablement une réponse très tardive, mais l'utilisation de setCancelable fera l'affaire.

alertDial.setCancelable(false);

10
De la documentation: "Définit si cette boîte de dialogue peut être annulée avec la touche RETOUR." Cela n'a rien à voir avec le bouton positif qui
ferme

3
Ça ne marche pas pour moi, j'écarte quand je clique sur le bouton positif
Hugo

1
Cela n'a rien à voir avec l'OP
MatPag

1
Ne répond pas à la question
Kevin Crain
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.