--- Note aux modérateurs: Aujourd'hui (15 juillet), j'ai remarqué que quelqu'un était déjà confronté à ce problème ici . Mais je ne suis pas sûr qu'il soit approprié de fermer cela en double, car je pense avoir fourni une bien meilleure explication du problème. Je ne suis pas sûr de devoir modifier l'autre question et y coller ce contenu, mais je ne suis pas à l'aise de trop changer la question de quelqu'un d'autre. ---
J'ai quelque chose de bizarre ici.
Je ne pense pas que le problème dépende du SDK sur lequel vous construisez. La version du système d'exploitation de l'appareil est ce qui compte.
Problème n ° 1: incohérence par défaut
DatePickerDialog
a été modifié (?) dans Jelly Bean et ne fournit plus qu'un bouton Terminé . Les versions précédentes incluaient un bouton Annuler , ce qui peut affecter l'expérience utilisateur (incohérence, mémoire musculaire des versions précédentes d'Android).
Répliquer: créez un projet de base. Mettez ceci dansonCreate
:
DatePickerDialog picker = new DatePickerDialog(
this,
new OnDateSetListener() {
@Override
public void onDateSet(DatePicker v, int y, int m, int d) {
Log.d("Picker", "Set!");
}
},
2012, 6, 15);
picker.show();
Attendu: unbouton Annuler apparaît dans la boîte de dialogue.
Actuel: Unbouton Annuler n'apparaît pas.
Captures d'écran: 4.0.3 (OK) et 4.1.1 (peut-être faux?).
Problème n ° 2: mauvais comportement de renvoi
Dialog appelle l'auditeur qu'il doit effectivement appeler, puis appelle toujours l'OnDateSetListener
auditeur. L'annulation appelle toujours la méthode set et sa définition appelle la méthode deux fois.
Répliquer: utilisez le code n ° 1, mais ajoutez le code ci-dessous (vous verrez que cela résout le n ° 1, mais uniquement visuellement / UI):
picker.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Log.d("Picker", "Cancel!");
}
});
Attendu:
- Appuyer sur la touche RETOUR ou cliquer en dehors de la boîte de dialogue ne devrait rien faire .
- Appuyer sur "Annuler" devrait imprimer Picker Cancel! .
- Appuyer sur "Set" devrait imprimer Picker Set! .
Actuel:
- Appuyer sur la touche RETOUR ou cliquer en dehors de la boîte de dialogue imprime le jeu de sélecteurs! .
- Appuyez sur "Annuler" pour imprimer le sélecteur Annuler! puis Picker Set! .
- En appuyant sur "Set" imprime Picker Set! puis Picker Set! .
Lignes de journal montrant le comportement:
07-15 12:00:13.415: D/Picker(21000): Set!
07-15 12:00:24.860: D/Picker(21000): Cancel!
07-15 12:00:24.876: D/Picker(21000): Set!
07-15 12:00:33.696: D/Picker(21000): Set!
07-15 12:00:33.719: D/Picker(21000): Set!
Autres notes et commentaires
- L'envelopper
DatePickerFragment
n'a pas d'importance. J'ai simplifié le problème pour vous, mais je l'ai testé.
Réponses:
Remarque: corrigé à partir de Lollipop , source ici . Classe automatisée pour une utilisation dans les clients (compatible avec toutes les versions d'Android) également mise à jour.
TL; DR: 1-2-3 étapes faciles pour une solution globale:
OnDateSetListener
votre activité (ou modifiez la classe en fonction de vos besoins).Déclenchez le dialogue avec ce code (dans cet exemple, je l'utilise dans un
Fragment
):Et c'est tout ce qu'il faut! La raison pour laquelle je garde toujours ma réponse comme «acceptée» est parce que je préfère toujours ma solution car elle a une très petite empreinte dans le code client, elle résout le problème fondamental (l'auditeur étant appelé dans la classe du framework), fonctionne bien à travers les changements de configuration et il achemine la logique du code vers l'implémentation par défaut dans les versions précédentes d'Android non affectées par ce bogue (voir la source de la classe).
Réponse originale (conservée pour des raisons historiques et didactiques):
Source de bogue
OK, on dirait que c'est en effet un bug et que quelqu'un d'autre l'a déjà rempli. Émission 34833 .
J'ai constaté que le problème était peut-être là
DatePickerDialog.java
. Où il lit:Je suppose que cela aurait pu être:
Maintenant, si quelqu'un peut me dire comment je peux proposer un rapport de correctif / bogue à Android, j'en serais ravi. Pendant ce temps, j'ai suggéré une solution possible (simple) en tant que version jointe de
DatePickerDialog.java
dans le problème.Concept pour éviter le bug
Définissez l'écouteur sur
null
dans le constructeur et créez votre propreBUTTON_POSITIVE
bouton ultérieurement . Voilà, détails ci-dessous.Le problème se produit car
DatePickerDialog.java
, comme vous pouvez le voir dans la source, appelle une variable globale (mCallBack
) qui stocke l'écouteur qui a été passé dans le constructeur:Donc, l'astuce consiste à fournir un
null
auditeur à stocker en tant qu'auditeur, puis à lancer votre propre ensemble de boutons (ci-dessous se trouve le code d'origine du n ° 1, mis à jour):Maintenant, cela fonctionnera à cause de la correction possible que j'ai publiée ci-dessus.
Et comme il
DatePickerDialog.java
vérifie un ànull
chaque fois qu'il litmCallback
( depuis les jours de l'API 3 / 1.5, il semble --- impossible de vérifier Honeycomb bien sûr), cela ne déclenchera pas l'exception.Étant donné que Lollipop a résolu le problème, je ne vais pas l'examiner: utilisez simplement l'implémentation par défaut (couverte dans la classe que j'ai fournie).Au début, j'avais peur de ne pas appeler le
clearFocus()
, mais j'ai testé ici et les lignes de Log étaient propres. Donc, cette ligne que j'ai proposée n'est peut-être même pas nécessaire après tout, mais je ne sais pas.Compatibilité avec les niveaux d'API précédents (modifiés)
Comme je l'ai souligné dans le commentaire ci-dessous, c'était un concept, et vous pouvez télécharger la classe que j'utilise à partir de mon compte Google Drive . La façon dont j'ai utilisé, l'implémentation système par défaut est utilisée sur les versions non affectées par le bogue.
J'ai pris quelques hypothèses (noms de boutons, etc.) qui conviennent à mes besoins car je voulais réduire au minimum le code standard dans les classes clientes. Exemple d'utilisation complet:
la source
Je vais ajouter mon propre riff sur la solution publiée par David Cesarino, au cas où vous n'utilisez pas Fragments, et que vous souhaitez un moyen simple de le réparer dans toutes les versions (2.1 à 4.1):
la source
android.R.string.ok
etandroid.R.string.cancel
plutôt que ceux de l'utilisateur. Et merci pour la réponse.dateToShow
? L'autre null est en fait le "correctif", donc cela devrait être là. Sur quelle version êtes-vous?import
que vous aviez pourField
? J'ai 6 options et aucune d'elles n'a fonctionné.Jusqu'à ce que le bogue soit corrigé, je suggère de ne pas utiliser DatePickerDialog ou TimePickerDialog. Utilisez AlertDialog personnalisé avec le widget TimePicker / DatePicker;
Changer TimePickerDialog avec;
Changez DatePickerDialog avec;
la source
Celui de TimePicker basé sur la solution de David Cesarino, "TL; DR: 1-2-3 étapes faciles pour une solution globale"
TimePickerDialog ne fournit pas les fonctionnalités telles que DatePickerDialog.getDatePicker. Ainsi, l' écouteur OnTimeSetListener doit être fourni. Juste pour garder la similitude avec la solution de contournement de DatePicker, j'ai conservé l'ancien concept mListener. Vous pouvez le modifier si vous en avez besoin.
L'appel et l'auditeur sont identiques à la solution d'origine. Incluez simplement
étendre la classe parente,
Mettre en place
exemple appel
(Mis à jour pour gérer l'annulation)
la source
Au cas où quelqu'un voudrait une solution de contournement rapide, voici le code que j'ai utilisé:
}
Où layout.date_picker_view est une ressource de mise en page simple avec un DatePicker comme seul élément:
Voici le tutoriel complet au cas où vous seriez intéressé.
la source
Ma solution simple. Lorsque vous voulez le faire redémarrer, lancez simplement "resetFired" (par exemple, lorsque vous ouvrez à nouveau la boîte de dialogue).
la source
onDateSet
il sera appelé une fois lorsque la boîte de dialogue est fermée par quelque moyen que ce soit, et si ce moyen était de régler l'heure, il sera à nouveau déclenché, nous devons donc attraper le deuxième appel, pas le premier comme vous l'avez fait,isJellyBeanOrAbove()
, les versions inférieures à Jellybean n'ont pas le bogue que toute cette question concerne, et étant donné que nous voulons attraper le deuxième appel, le code ne fonctionnera pas à moins que nous ne fassions cette vérification, croyez-moi, j'ai essayé mon code sur les émulateurs et les appareils réels (avec différentes versions) plusieurs fois et cela fonctionne comme un charmeSelon la brillante réponse d'Ankur Chaudhary sur le
TimePickerDialog
problème similaire , si nous vérifions à l'intérieuronDateSet
si la vue donnéeisShown()
ou non, cela résoudra tout le problème avec un minimum d'effort, sans avoir besoin d'étendre le sélecteur ou de vérifier certains drapeaux hideux autour du code. ou même en vérifiant la version du système d'exploitation, procédez comme suit:et bien sûr, la même chose peut être faite pour
onTimeSet
selon la réponse d'Ankurla source
La façon dont j'ai géré cette situation consistait à utiliser un indicateur et à remplacer les méthodes onCancel et onDismiss.
onCancel est appelé uniquement lorsque l'utilisateur touche en dehors de la boîte de dialogue ou du bouton de retour. onDismiss est toujours appelé
La définition d'un indicateur dans la méthode onCancel peut aider à filtrer dans la méthode onDismiss l'intention de l'utilisateur: annuler l'action ou l'action terminée. Ci-dessous un code qui montre l'idée.
la source
Il existe une solution de contournement très simple, si votre application n'utilise pas la barre d'action. Notez au passage que certaines applications s'appuient sur cette fonctionnalité pour fonctionner, car l'annulation du sélecteur de date a une signification particulière (par exemple, elle efface le champ de date en une chaîne vide, ce qui pour certaines applications est un type d'entrée valide et significatif ) et l'utilisation d'indicateurs booléens pour éviter que la date ne soit réglée deux fois sur OK ne vous aidera pas dans ce cas.
Ré. le correctif réel, vous n'avez pas à créer de nouveaux boutons ou votre propre boîte de dialogue. Le but est d'être compatible avec les deux, les anciennes versions d'Android, les buggy (4. ) et les futures, bien que ce dernier soit impossible à certifier, bien sûr. Notez que dans Android 2. , onStop () pour android.app.Dialog ne fait rien du tout, et dans 4. * il fait mActionBar.setShowHideAnimationEnabled (false), ce qui n'est important que si votre application a une barre d'action. OnStop () dans DatePickerDialog, qui hérite de Dialog, ne contribue qu'à mDatePicker.clearFocus () (à partir du dernier correctif des sources Android 4.3), ce qui ne semble pas indispensable.
Par conséquent, le remplacement de onStop () par une méthode qui ne fait rien devrait, dans de nombreux cas, réparer votre application et garantir qu'elle le restera dans un avenir prévisible. Ainsi, étendez simplement la classe DatePickerDialog avec la vôtre et remplacez onStop () avec une méthode factice. Vous devrez également fournir un ou deux constructeurs, selon vos besoins. Notez également qu'il ne faut pas être tenté d'essayer d'exagérer ce correctif en essayant par exemple de faire quelque chose avec la barre d'activité directement, car cela limiterait votre compatibilité aux dernières versions d'Android uniquement. Notez également que ce serait bien de pouvoir appeler le super pour onStop () de DatePicker car le bogue est uniquement dans le onStop () de DatePickerDialog lui-même, mais pas dans la super classe de DatePickerDialog. Cependant, cela vous obligerait à appeler super.super.onStop () à partir de votre classe personnalisée, ce que Java ne vous laissera pas faire, car cela va à l'encontre de la philosophie d'encapsulation :) Voici ma petite classe que j'ai utilisée pour verrouiller DatePickerDialog. J'espère que ce commentaire sera utile à quelqu'un. Wojtek Jarosz
}
la source
Essayez les concepts ci-dessous.
la méthode onDateSet () appelle deux fois (si vous enregistrez des appels emulator.it deux fois.Si vous utilisez un appareil réel, il appellera correctement une seule fois.Si vous utilisez l'émulateur, utilisez le compteur.Si vous travaillez dans un appareil réel, alors ignorer la variable de compteur. Pour un appareil réel, cela fonctionne pour moi.)
lorsque l'utilisateur clique sur le bouton dans DatePickerDialog.
pour cela, vous devez maintenir une valeur de compteur et ne rien faire lorsque le mothod appelle la première fois et effectuer l'opération lorsque la méthode appelle la deuxième fois.
Reportez-vous aux extraits de code ci-dessous
Pour annuler le dilalogue datepicker, cela fonctionne pour moi.Pour l'émulateur, il ne fonctionne pas
Cela fonctionne pour moi pour un vrai appareil.Mais pour l'émulateur, cela ne fonctionne pas correctement.Je pense que c'est un bogue d'émulateur Android.
la source
Une solution simple consisterait à utiliser un booléen pour sauter la deuxième exécution
la source
onStop
appeler la méthode alors qu'elle ne devrait pas ... elle se déclenche deux fois est une conséquence du bogue.onDateSet
. Ainsi, cassé.onDateSet
sera appelée une fois par quelque moyen que ce soit, mais lorsque vous choisissez "done" ou "set", elle sera appelée deux fois. Par conséquent, nous devons sauter uniquement le premier, donc s'il est appelé deux fois alors et alors seulement nous avons la date correcteVous pouvez remplacer onCancel () et utiliser setOnDismissListener () pour détecter les actions utilisateur négatives. Et avec un DatePickerDialog.BUTTON_POSITIVE, vous savez que l'utilisateur souhaite définir une nouvelle date.
puis vérifiez setDate:
la source
Voici ma classe de contournement pour DatePickerDialog sur le bouton d'annulation ainsi que son abandon par le bouton de retour. Copier et utiliser dans le style de DatePickerDialog (comme l'écouteur est avec état, nous devons créer une nouvelle instance lors de l'utilisation, sinon plus de code est nécessaire pour le faire fonctionner)
Utilisation:
Classe:
}
la source
J'utilise des sélecteurs de dates, des sélecteurs de temps et des sélecteurs de nombres. Les sélecteurs de numéros appellent onValueChanged chaque fois que l'utilisateur sélectionne un numéro, avant que le sélecteur ne soit rejeté, donc j'avais déjà une structure comme celle-ci pour faire quelque chose avec la valeur uniquement lorsque le sélecteur est rejeté:
J'ai étendu cela pour définir des onClickListeners personnalisés pour mes boutons, avec un argument pour voir quel bouton a été cliqué. Maintenant, je peux vérifier quel bouton a été touché avant de définir ma valeur finale:
Et puis j'ai étendu cela pour travailler avec les types de date et d'heure pour les sélecteurs de date et d'heure ainsi que le type int pour les sélecteurs de nombres.
J'ai posté ceci parce que je pensais que c'était plus simple que certaines des solutions ci-dessus, mais maintenant que j'ai inclus tout le code, je suppose que ce n'est pas beaucoup plus simple! Mais cela s'intègre parfaitement dans la structure que j'avais déjà.
Mise à jour pour Lollipop: Apparemment, ce bogue ne se produit pas sur tous les appareils Android 4.1-4.4, car j'ai reçu quelques rapports d'utilisateurs dont les sélecteurs de date et d'heure n'appelaient pas les rappels onDateSet et onTimeSet. Et le bogue a été officiellement corrigé dans Android 5.0. Mon approche ne fonctionnait que sur les appareils sur lesquels le bogue est présent, car mes boutons personnalisés n'appelaient pas le gestionnaire onClick de la boîte de dialogue, qui est le seul endroit où onDateSet et onTimeSet sont appelés lorsque le bogue n'est pas présent. J'ai mis à jour mon code ci-dessus pour appeler onClick de la boîte de dialogue, donc maintenant cela fonctionne, que le bogue soit présent ou non.
la source
J'ai aimé la réponse de David Cesarino ci-dessus, mais je voulais quelque chose qui remplacerait la boîte de dialogue cassée et qui fonctionnerait sur n'importe quelle boîte de dialogue qui pourrait manquer d'annuler / avoir un comportement d'annulation incorrect. Voici les classes dérivées pour DatePickerDialog / TimePickerDialog qui devraient fonctionner comme des remplacements. Ce ne sont pas des vues personnalisées. Il utilise la boîte de dialogue système, mais modifie simplement le comportement du bouton d'annulation / retour pour qu'il fonctionne comme prévu.
Cela devrait fonctionner au niveau d'API 3 et supérieur. Donc, pratiquement n'importe quelle version d'Android (je l'ai testé sur jellybean et sucette spécifiquement).
DatePickerDialog:
TimePickerDialog:
la source
Ma version de travail avec ClearButton utilisant des expressions Lambda:
la source
Pour TimePickerDialog, la solution de contournement peut être la suivante:
Je délègue tous les événements au wrapper KitKatSetTimeListener, et je ne renvoie à OnTimeSetListener d'origine qu'en cas de clic sur BUTTON_POSITIVE.
la source
Après avoir testé certaines des suggestions publiées ici, je pense personnellement que cette solution est la plus simple. Je passe "null" comme auditeur dans le constructeur DatePickerDialog, puis lorsque je clique sur le bouton "OK" j'appelle mon onDateSearchSetListener:
la source
Je sais que cet article est ici depuis presque un an, mais j'ai pensé que je devrais publier mes conclusions. Vous pouvez toujours conserver l'auditeur (au lieu de le définir sur mull) et continuer à travailler comme prévu. La clé est de définir implicitement les boutons "OK" ou (et) les boutons "Annuler". Je l'ai testé et cela fonctionne avec gratitude pour moi. L'auditeur n'est pas renvoyé deux fois.
Regardez cet exemple,
la source
timePickerListener
est toujours appelé indépendamment de ce que vous faites dans votre boîte de dialogue. Je n'ai même pas besoin de tester pour le savoir, il vous suffit de regarder les sources : si vous ne le définissez pas surnull
,tryNotifyTimeSet()
appellera l'auditeur à laonTimeSet()
fois dans sononClick()
etonStop()
.