{"id":31,"date":"2012-04-28T23:54:00","date_gmt":"2012-04-28T22:54:00","guid":{"rendered":"http:\/\/cyol.fr\/blogwp\/?p=31"},"modified":"2013-11-14T16:26:35","modified_gmt":"2013-11-14T15:26:35","slug":"dev-android-les-dialog-probleme-de-persistance-du-contenu-avec-oncreatedialog","status":"publish","type":"post","link":"https:\/\/cyol.fr\/blog\/post\/dev-android-les-dialog-probleme-de-persistance-du-contenu-avec-oncreatedialog\/","title":{"rendered":"D\u00e9v Android : les Dialog (probl\u00e8me de persistance du contenu avec onCreateDialog)"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" class=\"alignleft size-full wp-image-124\" title=\"Android_Robot_100\" alt=\"Logo Android\" src=\"https:\/\/cyol.fr\/blog\/wp-content\/uploads\/billets\/icones\/Android_Robot_100.png\" width=\"85\" height=\"100\" \/>Jusque l\u00e0 pour cr\u00e9er mes Dialog (genre de popup) o\u00f9 afficher mes r\u00e9sultats, l&rsquo;Aide ou encore le \u00ab\u00a0\u00e0 propos\u00a0\u00bb, j&rsquo;utilisais uniquement un @override de <a title=\"Android Developers onCreateDialog\" href=\"http:\/\/developer.android.com\/reference\/android\/app\/Activity.html#onCreateDialog%28int,%20android.os.Bundle%29\" hreflang=\"en\">onCreateDialog()<\/a>.<br \/>\nSur mes Dialog je propose toujours un bouton pour le fermer. Quand j&rsquo;ai fais mes premiers (pour Aide et A propos), j&rsquo;avais plac\u00e9 sur ce bouton la m\u00e9thode dialog.dismiss() qui marchait fort bien.<\/p>\n<p><!--more--><\/p>\n<p>Quand j&rsquo;ai voulu faire de m\u00eame avec le Dialog de r\u00e9sultat je me suis retrouv\u00e9 face \u00e0 un probl\u00e8me : dialog.dismiss() ne supprime pas l&rsquo;instance du Dialog. Cela se traduisait par le fait que les fois suivantes o\u00f9 j&rsquo;appelais ce m\u00eame Dialog, ce qu&rsquo;il affichait \u00e9tait toujours ce qu&rsquo;il avait affich\u00e9 la premi\u00e8re fois. Visiblement je ne passais plus dans le onCreateDialog().<\/p>\n<p>En cherchant un peu j&rsquo;ai trouv\u00e9 qu&rsquo;en utilisant <a title=\"Android Developers removeDialog\" href=\"http:\/\/developer.android.com\/reference\/android\/app\/Activity.html#removeDialog%28int%29\" hreflang=\"en\">removeDialog(id_du_Dialog)<\/a> \u00e7a marchait correctement, c&rsquo;est \u00e0 dire que je passais \u00e0 nouveau dans le onCreateDialog(), l\u00e0 o\u00f9 j&rsquo;affecte le titre et le message que le Dialog doit pr\u00e9senter. Une b\u00eate erreur de copier-coller (les plus classiques en d\u00e9veloppement) m&rsquo;a amen\u00e9 \u00e0 creuser \u00e0 nouveau la question. En effet, pour l&rsquo;affichage des Logs sur lequel je travaille, j&rsquo;utilise un Dialog. J&rsquo;ai donc copi\u00e9-coll\u00e9 mon bout de code du Dialog de r\u00e9sultat vu qu&rsquo;ils devaient avoir le m\u00eame genre de comportement. Et b\u00eatement, j&rsquo;avais oubli\u00e9 de mettre \u00e0 jour l&rsquo;id_du_Dialog en argument de removeDialog. Du coup, je n&rsquo;arrivais pas \u00e0 avoir une vue \u00e0 jour du contenu du fichier de log. J&rsquo;avais toujours que ce qu&rsquo;il avait affich\u00e9 \u00e0 ma premi\u00e8re instance. En cherchant le pourquoi de cette erreur ailleurs que sur le removeDialog, j&rsquo;en suis donc venu \u00e0 d\u00e9couvrir un peu mieux le fonctionnement d&rsquo;un Dialog et aussi pourquoi il fallait que je modifie mon code sur le Dialog des r\u00e9sultats.<\/p>\n<p>En fait il se trouve que dans le workflow (cycle de vie) d&rsquo;un Dialog, on ne passe dans onCreateDialog que lors de la premi\u00e8re instanciation.<\/p>\n<p>dialog.dismiss() ne d\u00e9truit pas l&rsquo;instance, il se contente de ne plus l&rsquo;afficher. A cot\u00e9 de \u00e7a removeDialog(id_du_Dialog) d\u00e9truit effectivement l&rsquo;instance (ce qui a pour effet de bord de ne plus l&rsquo;afficher). C&rsquo;est pour \u00e7a qu&rsquo;avec removeDialog(id_du_Dialog) on repasse dans la m\u00e9thode onCreateDialog lors d&rsquo;un showDialog.<\/p>\n<p>Sauf que&#8230; Instancier un nouveau Dialog est gourmand en ressources et il existe une m\u00e9thode onPrepareDialog qui va tout le temps agir dans un showDialog et on peut l&rsquo;utiliser pour donner un titre et un contenu (et d&rsquo;autres choses certainement) \u00e0 un Dialog. Reste \u00e0 savoir comment&#8230;<\/p>\n<p>Mon code qui utilisait le onCreateDialog uniquement :<\/p>\n<pre class=\"brush:java\">protected Dialog onCreateDialog(int id, final Bundle bundle) {\r\n\tDialog dialog = null;\r\n\tAlertDialog.Builder builder = new AlertDialog.Builder(this);\r\n\tResources res = getResources();\r\n\t\/\/ Ici on peut d\u00e9finir plusieurs boites de dialogue diff\u00e9rentes\r\n\tswitch (id) {\r\n\t\tcase DIALOG_CONSULT_LOG :\r\n\t\t\t\/\/ On d\u00e9finit le message \u00e0 afficher dans la boite de dialogue\r\n\t\t\tbuilder.setMessage(bundle.getString(\"resultats\"))\r\n\t\t\t\t.setTitle(res.getString(R.string.menu_logs))\r\n\t\t\t\t.setCancelable(false)\r\n\t\t\t\t.setNegativeButton(res.getString(R.string.ok),\r\n\t\t\t\t\tnew DialogInterface.OnClickListener() {\r\n\t\t\t\t\t\t\/\/ On d\u00e9finit l'action pour le OK =&gt; on ferme le dialogue\r\n\t\t\t\t\t\tpublic void onClick(DialogInterface dialog, int id) {\r\n\t\t\t\t\t\t\/\/ On retire la boite de dialogue\r\n\t\t\t\t\t\tremoveDialog(DIALOG_CONSULT_LOG);\r\n\t\t\t\t\t}\r\n\t\t\t\t})\r\n\t\t\t\t.setPositiveButton(res.getString(R.string.log_clear),\r\n\t\t\t\t\tnew DialogInterface.OnClickListener() {\r\n\t\t\t\t\t\t\/\/ On d\u00e9finit l'action pour le log_clear =&gt; on supprime log.txt\r\n\t\t\t\t\t\t\/\/ Et on ferme le dialogue\r\n\t\t\t\t\t\tpublic void onClick(DialogInterface dialog, int id) {\r\n\t\t\t\t\t\t\t_clearLogs();\r\n\t\t\t\t\t\t\t\/\/ On retire la boite de dialogue\r\n\t\t\t\t\t\t\tremoveDialog(DIALOG_CONSULT_LOG);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t);\r\n\t\t\t\tdialog = builder.create();\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\treturn dialog;\r\n\t}<\/pre>\n<p>L&rsquo;id\u00e9e est de r\u00e9ussir \u00e0 effectuer le setMessage dans le onPrepareDialog&#8230; Une fois que ce sera r\u00e9ussi, on pourra remplacer le removeDialog(id_du_Dialog) par un dialog.dismiss();<\/p>\n<p>Bon, premi\u00e8re chose que je vois en \u00e9tudiant <a title=\"Android devlopers function onPrepareDialog\" href=\"http:\/\/developer.android.com\/reference\/android\/app\/Activity.html#onPrepareDialog%28int,%20android.app.Dialog,%20Bundle%20args%29\" hreflang=\"en\">onPrepareDialog<\/a> :<\/p>\n<blockquote><p><strong>This method is deprecated.<\/strong><br \/>\nUse the new <code><a href=\"http:\/\/developer.android.com\/reference\/android\/app\/DialogFragment.html\">DialogFragment<\/a><\/code> class with <code><a href=\"http:\/\/developer.android.com\/reference\/android\/app\/FragmentManager.html\">FragmentManager<\/a><\/code> instead; this is also available on older platforms through the Android compatibility package.<\/p><\/blockquote>\n<p>Bon, par contre, je vois que DialogFragment est \u00e0 partir de : API Level 11. Hors, en visant les Android 2.2, je suis sur l&rsquo;API Level 8. Du coup, m\u00eame si c&rsquo;est deprecated (d\u00e9pr\u00e9ci\u00e9), je vais l&rsquo;utiliser.<\/p>\n<p>Par contre malheureusement ils ne donnent pas d&rsquo;exemple de code. Trouv\u00e9 sur <a title=\"How to change the message(body) of AlertDialog in onPrepareDialog()?\" href=\"http:\/\/stackoverflow.com\/questions\/5000776\/how-to-change-the-messagebody-of-alertdialog-in-onpreparedialog\" hreflang=\"en\">http:\/\/stackoverflow.com\/questions\/5000776\/how-to-change-the-messagebody-of-alertdialog-in-onpreparedialog<\/a>.<\/p>\n<pre class=\"brush:java\">\/**\r\n * Met \u00e0 jour le message du Dialog\r\n *\/\r\n@Override\r\nprotected void onPrepareDialog (int id, Dialog dialog, Bundle bundle){\r\n\tswitch (id) {\r\n\t\tcase DIALOG_CONSULT_LOG :\r\n\t\t\t((AlertDialog) dialog).setMessage(bundle.getString(\"resultats\"));\r\n\t\t\tbreak;\r\n\t}\r\n}<\/pre>\n<p>Et l&rsquo;astuce qu&rsquo;il ne faut pas oublier : dans le onCreateDialog, il faut laisser un .setMessage(\u00ab\u00a0\u00a0\u00bb) sinon, \u00e7a ne marche pas. Plus qu&rsquo;\u00e0 appliquer la m\u00eame chose sur mes Dialog de r\u00e9sultat et \u00e0 restester tout \u00e7a avant de pouvoir sortir la version 1.8 de SRDice.<\/p>\n<h3>Fini ! Ah bein non&#8230;<\/h3>\n<p>Evidemment quand je m&rsquo;attaque au DIALOG_RESULT je tombe sur une nouvelle surprise : Dans la d\u00e9finition de ce dialog, donc dans le onCreateDialog, je positionne le bouton pour enregistrer le r\u00e9sultat et lui d\u00e9fini son action :<\/p>\n<pre class=\"brush:java\">if (mExternalStorageWriteable == true) {\r\n\t\/\/ On a une carte SD, on peut proposer d'enregistrer le log\r\n\tbuilder.setPositiveButton(res.getString(R.string.logResult),\r\n\t\tnew DialogInterface.OnClickListener() {\r\n\t\t\t\/\/ On d\u00e9finit l'action pour le Log =&gt; on enregistre dans les logs et on ferme le dialogue\r\n\t\t\tpublic void onClick(DialogInterface dialog, int id) {\r\n\t\t\t\t_saveResult(bundle);\r\n\t\t\t\t\/\/ On retire la boite de dialogue\r\n\t\t\t\tdialog.dismiss();\r\n\t\t\t}\r\n\t\t});\r\n}<\/pre>\n<p>Malheureusement, du coup le bundle pass\u00e9 en argument est celui de la premi\u00e8re cr\u00e9ation du Dialog (donc le premier lancer de D\u00e9s fait dans l&rsquo;application) et il n&rsquo;est pas mis \u00e0 jour (vu que c&rsquo;est dans le onPrepareDialog qu&rsquo;on fait \u00e7a).<\/p>\n<p>Il faut donc dans le onPrepareDialog red\u00e9finir le Bouton :<\/p>\n<pre class=\"brush:java\">if (mExternalStorageWriteable == true) {\r\n\t\/\/ On a une carte SD, on d\u00e9fini le bouton pour enregistrer le log\r\n\t((AlertDialog) dialog).setButton(DialogInterface.BUTTON_POSITIVE, res.getString(R.string.logResult),\r\n\t\tnew DialogInterface.OnClickListener() {\r\n\t\t\t\/\/ On d\u00e9finit l'action pour le Log =&gt; on enregistre dans les logs et on ferme le dialogue\r\n\t\t\tpublic void onClick(DialogInterface dialog, int id) {\r\n\t\t\t\t_saveResult(bundle);\r\n\t\t\t\t\/\/ On retire la boite de dialogue\r\n\t\t\t\tdialog.dismiss();\r\n\t\t\t}\r\n\t\t}\r\n\t);\r\n}<\/pre>\n<p>Pour \u00ab\u00a0attraper\u00a0\u00bb le bon bouton, il faut le r\u00e9cup\u00e9rer avec DialogInterface. BUTTON_POSITIVE car dans le onCreateDialog j&rsquo;en ai pos\u00e9 les bases avec builder.setPositiveButton.<\/p>\n<p>(Dans un AlertDialog on peut d\u00e9finir 3 boutons : setPositiveButton, setNeutralButton et setNegativeButton qui apparaitrons les uns \u00e0 cot\u00e9s des autres dans cet ordre)<\/p>\n<p>Du coup dans le onCreateDialog on peut faire le message pour ne pr\u00e9senter plus que \u00e7a :<\/p>\n<pre class=\"brush:java\">if (mExternalStorageWriteable == true) {\r\n\t\/\/ On a une carte SD, on peut proposer d'enregistrer le log\r\n\tbuilder.setPositiveButton(res.getString(R.string.logResult),\r\n\t\tnew DialogInterface.OnClickListener() {\r\n\t\t\t\/\/On pose les bases, le listener r\u00e9el est d\u00e9fini dans onPrepareDialog\r\n\t\t\tpublic void onClick(DialogInterface dialog, int id) {\r\n\t\t\t}\r\n\t\t});\r\n}<\/pre>\n<p>Fin cette fois. On va sortir la V1.8<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Jusque l\u00e0 pour cr\u00e9er mes Dialog (genre de popup) o\u00f9 afficher mes r\u00e9sultats, l&rsquo;Aide ou encore le \u00ab\u00a0\u00e0 propos\u00a0\u00bb, j&rsquo;utilisais uniquement un @override de onCreateDialog(). Sur mes Dialog je propose toujours un bouton pour le fermer. Quand j&rsquo;ai fais mes premiers (pour Aide et A propos), j&rsquo;avais plac\u00e9 sur ce bouton la m\u00e9thode dialog. [&#8230;]<\/p>\n","protected":false},"author":1,"featured_media":124,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[10,2,4],"tags":[114,15,62,34,18,112],"class_list":["post-31","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-android","category-developpement","category-srdice","tag-android","tag-des","tag-developpement-2","tag-dialog","tag-java","tag-srdice","odd"],"_links":{"self":[{"href":"https:\/\/cyol.fr\/blog\/wp-json\/wp\/v2\/posts\/31","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cyol.fr\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cyol.fr\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cyol.fr\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/cyol.fr\/blog\/wp-json\/wp\/v2\/comments?post=31"}],"version-history":[{"count":7,"href":"https:\/\/cyol.fr\/blog\/wp-json\/wp\/v2\/posts\/31\/revisions"}],"predecessor-version":[{"id":1089,"href":"https:\/\/cyol.fr\/blog\/wp-json\/wp\/v2\/posts\/31\/revisions\/1089"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cyol.fr\/blog\/wp-json\/wp\/v2\/media\/124"}],"wp:attachment":[{"href":"https:\/\/cyol.fr\/blog\/wp-json\/wp\/v2\/media?parent=31"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cyol.fr\/blog\/wp-json\/wp\/v2\/categories?post=31"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cyol.fr\/blog\/wp-json\/wp\/v2\/tags?post=31"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}