Android “Best Practice” renvoyer des valeurs à partir d’une boîte de dialog

Quelle est la manière “correcte” de renvoyer les valeurs à l’activité appelante à partir d’une boîte de dialog personnalisée complexe – par exemple, champs de texte, sélecteur de date ou d’heure, un tas de boutons radio, plus un bouton “Enregistrer” et “Annuler”?

Parmi les techniques que j’ai vues sur le Web, citons:

… plus que j’ai déjà oublié.

Existe-t-il une technique particulière considérée comme la méthode canoniquement correcte ou «meilleure pratique»?

J’utilise les moyens suivants:

  1. Toutes mes activités ont une seule et même activité parentale (disons ControlActivity). ControlActivity a private volatile Bundle controlBundle; avec getter / setter approprié
  2. Lorsque je commence le dialog, j’appelais le dialog par ma propre méthode:

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

Donc, chaque fois que je connais des parameters envoyés pour dialogr

  1. Lorsque la boîte de dialog est sur le sharepoint se terminer, je crée un dialog avec un autre Bundle avec les valeurs nécessaires, puis je les place dans mon setter de bundles d’ Activity :
 ((ControlActivity )this.getOwnerActivity).setControlBundle(bundle); 

Donc, à la fin, lorsque le dialog se termine, je sais que la valeur “retournée” de la boîte de dialog. Je sais que ce n’est pas comme int retCode=this.showMyDialog(); c’est un peu plus complexe, mais c’est réalisable.

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

 builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { // run whatever code you want to run here // if you need to pass data back, just call a function in your // activity and pass it some parameters } }) 

C’est comme ça que j’ai toujours manipulé les données des boîtes de dialog.

EDIT: Laissez-moi vous donner un exemple plus concret qui répondra mieux à votre question. Je vais voler un exemple de code de cette page, que vous devriez lire:

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

 // Alert Dialog code (mostly copied from the Android docs AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("Pick a color"); builder.setItems(items, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int item) { myFunction(item); } }); AlertDialog alert = builder.create(); 

 // Now elsewhere in your Activity class, you would have this function private void myFunction(int result){ // Now the data has been "returned" (as pointed out, that's not // the right terminology) } 

Pour mon application MIDI, j’avais besoin de boîtes de dialog de confirmation yes / no / cancel, j’ai donc d’abord créé une classe StandardDialog:

 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, Ssortingng title, Ssortingng 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("Yes", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { mResponseHandler.sendEmptyMessage(mDialogId + dlgResultYes); } }); mDialogBuilder.setNegativeButton("No", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { mResponseHandler.sendEmptyMessage(mDialogId + dlgResultNo); } }); if (dialogType == dlgTypeYesNoCancel) { mDialogBuilder.setNeutralButton("Cancel", 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 messages pour les mises à jour de l’interface utilisateur à partir d’autres threads. J’ai donc ajouté du code pour traiter les messages des boîtes de dialog. En utilisant un paramètre dialogId différent lorsque j’instancie StandardDialog pour diverses fonctions de 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 à des boîtes de dialog personnalisées complexes en envoyant un lot de données bien que ce soit beaucoup plus lent qu’un simple message entier.

 private Handler uiMsgHandler = new Handler() { @Override public void handleMessage(Message msg) { if (msg != null) { // {Code to check for other UI messages here} // Check for dialog box responses if (msg.what == (clearDlgId + StandardDialog.dlgResultYes)) { doClearDlgYesClicked(); } else if (msg.what == (recordDlgId + StandardDialog.dlgResultYes)) { doRecordDlgYesClicked(); } else if (msg.what == (recordDlgId + StandardDialog.dlgResultNo)) { doRecordDlgNoClicked(); } } } }; 

Il ne me rest plus qu’à définir les méthodes do {Whatever} () dans l’activité. Pour faire apparaître une boîte de dialog, 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().getSsortingng(R.ssortingng.dlgTitleClear), getResources().getSsortingng(R.ssortingng.dlgMsgClear), StandardDialog.dlgTypeYesNo, clearDlgId); } 

clearDlgId est défini comme un entier unique ailleurs. Cette méthode permet d’afficher une boîte de dialog Oui / Non devant l’activité, qui perd le focus jusqu’à la fermeture de la boîte de dialog. À ce moment, l’activité reçoit un message avec le résultat du dialog. Le gestionnaire de messages appelle ensuite la méthode doClearDlgYesClicked() si le bouton “Oui” a été cliqué. (Je n’ai pas eu 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 facilite le retour des résultats depuis un dialog.

J’ai moi-même réfléchi à cela pendant un certain temps et finalement, la manière la plus commode de le faire est de diviser mon activité en différentes méthodes qui représentent chaque unité de stream de contrôle. Par exemple, si les activités de mon activité sont les suivantes: charger des variables intentionnellement, rechercher des données, traiter et poursuivre si disponible, sinon lancer un appel en arrière-plan, attendre l’interaction de l’utilisateur, démarrer une autre activité.

Je ramasse généralement les parties communes, les deux premières et les dernières dans ce cas. Je vais envelopper les premiers dans onCreate() et en faire un autre pour le dernier … disons startAnotherActivity(Data) . Vous pouvez organiser les parties du milieu pour qu’elles consistent en un checkData(Data) (éventuellement fusionné dans le onCreate() ) qui appelle processAvailableData(Data) ou performBackgroundTask(Data) . La tâche en arrière-plan effectuera une opération en arrière-plan et renverra le contrôle à onBackgroundTaskCompleted(OtherData) .

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

Je pense que cette approche apporte un certain nombre d’avantages.

  1. Cela vous aide à résoudre le problème de retour de données auquel votre question est confrontée en “se déplaçant vers l’avant” au lieu de renvoyer des données.
  2. Il permet d’append plus facilement de nouvelles fonctionnalités. Par exemple, si nous voulions donner à l’utilisateur plus d’options, nous pourrions simplement appeler la méthode appropriée de getUserResponse() qui pourrait affecter les données qui sont finalement transmises à l’activité suivante.
  3. Cela permet d’éviter les problèmes de stream inutiles (consultez les questions relatives à finish() et return sur SO) lorsque notre hypothèse intuitive est un certain stream et qu’il se révèle être un autre.
  4. Permet de mieux gérer les variables afin de ne pas avoir beaucoup de champs au niveau de la classe pour éviter les problèmes d’access aux variables dans les classes internes anonymes (onClick (), doInBackground (), etc.).

Je suis sûr que le fait d’avoir plus de méthodes ajoute un peu de surcharge, mais il est probablement compensé par les avantages de stream, de réutilisation et de simplicité que vous obtiendrez (j’aimerais entendre les opinions d’un expert en compilation sur ce point).

Je vais expliquer une solution avec l’exemple de deux fragments. Imaginez qu’il existe un SimpleFragment qui ne comporte qu’un seul champ de texte pour rendre une date. Ensuite, il y a un DatePickerFragment qui permet de choisir une date particulière. Ce que je veux, c’est que DatePickerFragment passe la valeur de date à l’appel SimpleFragment chaque fois que l’utilisateur confirme sa sélection.

SimpleFragment

Donc, tout d’abord, nous commençons le DatePickerFragment depuis SimpleFragment :

 private DateTime mFavoriteDate; // Joda-Time date private void launchDatePicker() { DatePickerFragment datePickerFragment = new DatePickerFragment(); Bundle extras = new Bundle(); // Pass an initial or the last value for the date picker long dateInMilliSeconds = mFavoriteDate.getMillis(); extras.putLong(BundleKeys.LAST_KNOWN_DATE, dateInMilliSeconds); datePickerFragment.setArguments(extras); datePickerFragment.setTargetFragment(this, SIMPLE_FRAGMENT_REQUEST_CODE); datePickerFragment.show(getActivity().getSupportFragmentManager(), DatePickerFragment.FRAGMENT_TAG); } 

DatePickerFragment

Dans le fragment de dialog, nous nous préparons à renvoyer la date sélectionnée lorsque l’utilisateur appuie sur le bouton positif:

 public static final Ssortingng DATE_PICKED_INTENT_KEY = "DATE_PICKED_INTENT_KEY"; public static final int DATE_PICKED_RESULT_CODE = 123; @Override public Dialog onCreateDialog(Bundle savedInstanceState) { // ... Long dateInMilliSeconds = getArguments().getLong(BundleKeys.LAST_KNOWN_DATE); DateTime date = new DateTime(dateInMilliSeconds); initializePickerUiControl(date); AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(activity); dialogBuilder .setPositiveButton(R.ssortingng.date_picker_positive, (dialog, which) -> { // Pass date to caller passBackDate(); }) .setNegativeButton(R.ssortingng.date_picker_negative, (dialog, which) -> { // Nothing to do here }); 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 de demande, nous consommons ce qui a été transmis par la boîte de dialog:

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

Des références à mattpic qui ont donné une excellente réponse auparavant.

Après quelques recherches, je me suis installé sur une interface de rappel. Mon code est le suivant:

MyFragment.java

 public class MyFragment extends Fragment { 

 private void displayFilter() { FragmentManager fragmentManager = getFragmentManager(); FilterDialogFragment filterDialogFragment = new FilterDialogFragment(); Bundle bundle = new Bundle(); bundle.putSerializable("listener", new FilterDialogFragment.OnFilterClickListener() { @Override public void onFilterClickListener() { System.out.println("LISTENER CLICKED"); } }); filterDialogFragment.setArguments(bundle); filterDialogFragment.show(fragmentManager, DIALOG_FILTER); } 

MyDialog.java

 public class MyDialog extends DialogFragment { private ImageButton mBtnTest; private OnFilterClickListener mOnFilterClickListener; @Override public Dialog onCreateDialog(Bundle savedInstanceState) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); // Get the layout inflater LayoutInflater inflater = getActivity().getLayoutInflater(); View filterLayout = inflater.inflate(R.layout.filter_dialog, null); // Inflate and set the layout for the dialog // Pass null as the parent view because its going in the dialog layout builder.setView(filterLayout) .setTitle("Filter"); Dialog dialog = builder.create(); mOnFilterClickListener = (OnFilterClickListener) getArguments().getSerializable("listener"); mBtnTest = (ImageButton)filterLayout.findViewById(R.id.fandb); mBtnTest.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { mOnFilterClickListener.onFilterClickListener(); dismiss(); } }); return dialog; } public interface OnFilterClickListener extends Serializable { void onFilterClickListener(); } }