54 votes

Android "Best Practice" renvoyer des valeurs à partir d'une boîte de dialogue

Quelle est la méthode "correcte" pour renvoyer les valeurs à l'activité appelante depuis une boîte de dialogue personnalisée complexe - par exemple, des champs de texte, un sélecteur de date ou d'heure, une série de boutons radio, etc., plus un bouton "Enregistrer" et un bouton "Annuler"?

Certaines des techniques que j'ai vues sur le web comprennent :

  • membres de données publics dans la classe dérivée de la boîte de dialogue qui peuvent être lus par l'activité

  • accesseurs "get" publics . . . " . . " . . "

  • Lancement de la boîte de dialogue avec un Intent (par opposition à show()) plus des gestionnaires dans la classe de la boîte de dialogue qui récupèrent les entrées des différents contrôles et les regroupent pour les renvoyer à l'activité, ainsi lorsque l'utilisateur appuie sur "Enregistrer", le groupe est renvoyé en utilisant ReturnIntent()

  • Écouteurs dans l'activité qui traitent les entrées des contrôles qui se trouvent dans la boîte de dialogue, par exemple, les écouteurs du TimePicker ou du DatePicker sont vraiment dans l'activité. Dans ce schéma, pratiquement tout le travail est effectué dans l'activité

  • Un écouteur dans l'activité pour le bouton "Enregistrer" et ensuite l'activité interroge directement les contrôles dans la boîte de dialogue ; l'activité ferme la boîte de dialogue.

...plus d'autres que j'ai déjà oubliées.

Y a-t-il une technique particulière considérée comme étant correcte ou "meilleure pratique"?

20voto

Computerish Points 5219

Peut-être que je ne comprends pas bien votre question, mais pourquoi ne pas simplement utiliser le système d'écoute intégré :

builder.setPositiveButton("Oui", new DialogInterface.OnClickListener() {
    public void onClick(DialogInterface dialog, int id) {
        // exécuter le code que vous souhaitez exécuter ici
        // si vous avez besoin de renvoyer des données, appelez simplement une fonction dans votre
        // activité et transmettez-lui certains paramètres
    }
})

C'est ainsi que j'ai toujours géré les données provenant des boîtes de dialogue.

ÉDITION : Permettez-moi de vous donner un exemple plus concret qui répondra mieux à votre question. Je vais emprunter un peu de code d'exemple à cette page, que vous devriez lire :

http://developer.android.com/guide/topics/ui/dialogs.html

// Code de l'AlertDialog (principalement copié de la documentation Android)
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Choisissez une couleur");
builder.setItems(items, new DialogInterface.OnClickListener() {
    public void onClick(DialogInterface dialog, int item) {
        myFunction(item);
    }
});
AlertDialog alert = builder.create();

...

// Maintenant, ailleurs dans votre classe Activity, vous auriez cette fonction
private void myFunction(int result){
    // Maintenant, les données ont été "renvoyées" (comme indiqué, ce n'est pas
    // le bon terme)
}

7voto

Brian Bailey Points 41

Pour mon application MIDI, j'avais besoin de dialogues de confirmation oui/non/annuler, donc j'ai d'abord créé une classe StandardDialog générale :

public class StandardDialog {

    import android.app.Activity;
    import android.app.AlertDialog;
    import android.content.DialogInterface;
    import android.os.Handler;

    public class StandardDialog {
    public static final int dlgResultOk         = 0;
    public static final int dlgResultYes        = 1;
    public static final int dlgResultNo         = 2;
    public static final int dlgResultCancel     = 3;

    public static final int dlgTypeOk           = 10;
    public static final int dlgTypeYesNo        = 11;
    public static final int dlgTypeYesNoCancel  = 12;

    private Handler mResponseHandler;
    private AlertDialog.Builder mDialogBuilder;
    private int mDialogId;

    public StandardDialog(Activity parent, 
                          Handler reponseHandler, 
                          String title, 
                          String message, 
                          int dialogType, 
                          int dialogId) {

        mResponseHandler = reponseHandler;
        mDialogId = dialogId;
        mDialogBuilder = new AlertDialog.Builder(parent);
        mDialogBuilder.setCancelable(false);
        mDialogBuilder.setTitle(title);
        mDialogBuilder.setIcon(android.R.drawable.ic_dialog_alert);
        mDialogBuilder.setMessage(message);
        switch (dialogType) {
        case dlgTypeOk:
            mDialogBuilder.setNeutralButton("Ok", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    mResponseHandler.sendEmptyMessage(mDialogId + dlgResultOk);
                }
            });         
            break;

        case dlgTypeYesNo:
        case dlgTypeYesNoCancel:
            mDialogBuilder.setPositiveButton("Oui", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    mResponseHandler.sendEmptyMessage(mDialogId + dlgResultYes);
                }
            });         
            mDialogBuilder.setNegativeButton("Non", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    mResponseHandler.sendEmptyMessage(mDialogId + dlgResultNo);
                }
            });         
            if (dialogType == dlgTypeYesNoCancel) {
                mDialogBuilder.setNeutralButton("Annuler", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        mResponseHandler.sendEmptyMessage(mDialogId + dlgResultCancel);
                    }
                });         
            }
            break;
        }
        mDialogBuilder.show();
    }
}

Ensuite, dans mon activité principale, j'avais déjà un gestionnaire de message pour les mises à jour de l'interface utilisateur à partir d'autres threads, donc j'ai simplement ajouté du code pour traiter les messages des dialogues. En utilisant un paramètre dialogId différent lorsque j'instancie le StandardDialog pour diverses fonctions du programme, je peux exécuter le code approprié pour gérer les réponses oui/non/annuler à différentes questions. Cette idée peut être étendue pour les dialogues personnalisés complexes en envoyant un Bundle de données, mais c'est beaucoup plus lent qu'un simple message entier.

private Handler uiMsgHandler = new Handler() {

    @Override
    public void handleMessage(Message msg) {
        if (msg != null) {

            // {Code pour vérifier d'autres messages d'interface utilisateur ici}

            // Vérifier les réponses des boîtes de dialogue
            if (msg.what == (clearDlgId + StandardDialog.dlgResultYes)) {
                doClearDlgYesClicked();
            }
            else if (msg.what == (recordDlgId + StandardDialog.dlgResultYes)) {
                doRecordDlgYesClicked();
            }
            else if (msg.what == (recordDlgId + StandardDialog.dlgResultNo)) {
                doRecordDlgNoClicked();
            }
        }
    }
};

Ensuite, tout ce que j'ai besoin de faire est de définir les méthodes do{Quelquechose}() dans l'activité. Pour afficher une boîte de dialogue, par exemple j'ai une méthode répondant à un bouton "effacer les événements MIDI enregistrés" et la confirmer comme suit :

public void onClearBtnClicked(View view) {
    new StandardDialog(this, uiMsgHandler, 
        getResources().getString(R.string.dlgTitleClear),
        getResources().getString(R.string.dlgMsgClear), 
        StandardDialog.dlgTypeYesNo, clearDlgId);
}

clearDlgId est défini comme un entier unique ailleurs. Cette méthode fait apparaître une boîte de dialogue Oui/Non devant l'activité, qui perd le focus jusqu'à ce que la boîte de dialogue se ferme, à ce moment-là l'activité reçoit un message avec le résultat de la boîte de dialogue. Ensuite, le gestionnaire de messages appelle la méthode doClearDlgYesClicked() si le bouton "Oui" a été cliqué. (Je n'avais pas besoin d'un message pour le bouton "Non" car aucune action n'était nécessaire dans ce cas).

Quoi qu'il en soit, cette méthode fonctionne pour moi, et rend facile de transmettre les résultats d'une boîte de dialogue.

4voto

barmaley Points 7307

Je suis en train d'utiliser la méthode suivante :

  1. Toutes mes activités ont une activité parente commune (disons ControlActivity). ControlActivity a private volatile Bundle controlBundle; avec les getters/setters appropriés
  2. Quand je lance une boîte de dialogue, j'avais l'habitude d'appeler la boîte de dialogue via ma propre méthode :

    public void showMyDialog(int id, Bundle bundle)
    {
        this.controlBundle=bundle;
        this.showDialog(id, bundle);
    }

Donc, à chaque fois, je connais les paramètres envoyés à la boîte de dialogue

  1. Quand la boîte de dialogue est sur le point de se terminer, je forme dans la boîte de dialogue un autre Bundle avec les valeurs nécessaires et ensuite les mets à travers mon setter de bundle de Activity :

    ((ControlActivity )this.getOwnerActivity).setControlBundle(bundle);

Donc, à la fin, quand la boîte de dialogue se termine, je connais la valeur "retournée" par la boîte de dialogue. Je sais que ce n'est pas comme int retCode=this.showMyDialog(); c'est un peu plus complexe, mais ça fonctionne.

2voto

JJD Points 7539

Je vais expliquer une solution avec l'exemple de deux fragments. Imaginez qu'il y a un SimpleFragment qui a juste un champ de texte pour afficher une date. Puis il y a un DatePickerFragment qui permet de choisir une date spécifique. Ce que je veux, c'est que le DatePickerFragment transmette la valeur de la date au SimpleFragment appelant chaque fois que l'utilisateur confirme sa sélection.

SimpleFragment

Alors, pour commencer, nous lançons le DatePickerFragment depuis le SimpleFragment :

private DateTime mFavoriteDate; // Joda-Time date

private void launchDatePicker() {
    DatePickerFragment datePickerFragment = new DatePickerFragment();
    Bundle extras = new Bundle();
    // Transmettre une valeur initiale ou la dernière valeur pour le sélecteur de date
    long dateEnMillisecondes = mFavoriteDate.getMillis();
    extras.putLong(BundleKeys.LAST_KNOWN_DATE, dateEnMilliSeconds);
    datePickerFragment.setArguments(extras);
    datePickerFragment.setTargetFragment(this, SIMPLE_FRAGMENT_REQUEST_CODE);
    datePickerFragment.show(getActivity().getSupportFragmentManager(),
            DatePickerFragment.FRAGMENT_TAG);
}

DatePickerFragment

Dans le fragment de dialogue, nous nous préparons à transmettre la date sélectionnée lorsque l'utilisateur appuie sur le bouton positif :

public static final String DATE_PICKED_INTENT_KEY = "DATE_PICKED_INTENT_KEY";
public static final int DATE_PICKED_RESULT_CODE = 123;

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    // ...
    Long dateEnMillisecondes = getArguments().getLong(BundleKeys.LAST_KNOWN_DATE);
    DateTime date = new DateTime(dateInMilliSeconds);
    initializePickerUiControl(date);

    AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(activity);
    dialogBuilder
        .setPositiveButton(R.string.date_picker_positive, (dialog, which) -> {
            // Transmettre la date à l'appelant
            passBackDate();
        })
        .setNegativeButton(R.string.date_picker_negative, (dialog, which) -> {
            // Rien à faire ici
        });
    return dialogBuilder.create();
}

private void passBackDate() {
    DateTime dateTime = getDateTimeFromPickerControl();
    Intent intent = new Intent();
    intent.putExtra(DATE_PICKED_INTENT_KEY, dateTime.getMillis());
    getTargetFragment().onActivityResult(
            getTargetRequestCode(), DATE_PICKED_RESULT_CODE, intent);
}

SimpleFragment

De retour dans le fragment demandeur, nous consommons ce qui a été transmis par le dialogue :

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == SIMPLE_FRAGMENT_REQUEST_CODE &&
            resultCode == DatePickerFragment.DATE_PICKED_RESULT_CODE) {
        long dateSelectionneeEnMillisecondes = data.getLongExtra(
                DatePickerFragment.DATE_PICKED_INTENT_KEY, 0);
        mFavoriteDate = new DateTime(datePickedInMilliseconds);
        updateFavoriteDateTextView();
    }
    else {
        super.onActivityResult(requestCode, resultCode, data);
    }
}

Références à mattpic qui a donné une excellente réponse auparavant.

2voto

Saad Farooq Points 2507

J'ai réfléchi à cela moi-même pendant un certain temps et finalement la façon la plus pratique que j'ai trouvée de le faire est de diviser mon activité en diverses méthodes qui représentent chaque unité de contrôle du flux. Par exemple, si les activités au sein de mon activité sont : charger des variables à partir de l'intent, vérifier certaines données, les traiter et continuer si elles sont disponibles, sinon faire un appel en arrière-plan, attendre une interaction de l'utilisateur, démarrer une autre activité.

En général, je conserve les parties qui sont communes, les deux premières et la dernière dans ce cas. J'encapsule les premières dans onCreate() et crée une méthode distincte pour la dernière... disons startAnotherActivity(Data). Vous pouvez organiser les parties intermédiaires de telle sorte qu'elles consistent en un checkData(Data) (éventuellement fusionné dans le onCreate()) qui appelle soit processAvailableData(Data) soit performBackgroundTask(Data). La tâche en arrière-plan effectuera une opération en arrière-plan et renverra le contrôle à onBackgroundTaskCompleted(OtherData).

Maintenant, à la fois processAvailableData(Data) et onBackgroundTaskCompleted(OtherData) appellent la méthode getUserResponse() qui à son tour peut soit appeler startAnotherActivity(Data) soit fusionner ses fonctions avec elle-même.

Je trouve que cette approche offre plusieurs avantages.

  1. Elle permet de résoudre le problème du retour des données auquel votre question fait référence en "avançant" au lieu de retourner des données.
  2. Elle facilite l'ajout de nouvelles fonctionnalités. Par exemple, si nous voulions donner à l'utilisateur plus d'options, nous pourrions simplement appeler la méthode appropriée à partir de getUserResponse() qui pourrait affecter les données qui sont finalement transmises à l'activité suivante.
  3. Elle aide à éviter les problèmes de flux inutiles (consultez les questions concernant finish() et return sur SO) lorsque notre hypothèse intuitive est un certain flux et qu'il s'avère être différent.
  4. Elle permet de mieux gérer les variables pour éviter d'avoir beaucoup de champs de niveau de classe pour éviter les problèmes d'accès aux variables dans les classes internes anonymes (onClick(), doInBackground(), etc.).

Je suis assez sûr que d'avoir plus de méthodes ajoute un certain surcoût mais c'est probablement compensé par les avantages en termes de flux, de réutilisation et de simplicité que vous obtenez (j'aimerais bien entendre l'avis d'un expert en compilation là-dessus).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X