--- Hinweis Moderatoren: Heute (15. Juli), ich habe bemerkt , dass jemand bereits mit diesem Problem konfrontiert hier . Ich bin mir jedoch nicht sicher, ob es angemessen ist, dies als Duplikat zu schließen, da ich denke, dass ich das Problem viel besser erklärt habe. Ich bin mir nicht sicher, ob ich die andere Frage bearbeiten und diesen Inhalt dort einfügen soll, aber ich fühle mich nicht wohl, wenn ich die Frage eines anderen zu sehr ändere. --- ---.
Ich habe hier etwas Seltsames .
Ich glaube nicht, dass das Problem davon abhängt, gegen welches SDK Sie bauen. Auf die Betriebssystemversion des Geräts kommt es an.
Problem Nr. 1: Standardmäßig Inkonsistenz
DatePickerDialog
wurde in Jelly Bean geändert (?) und bietet jetzt nur noch eine Schaltfläche " Fertig" . Frühere Versionen enthielten eine Schaltfläche Abbrechen. Dies kann sich auf die Benutzererfahrung auswirken (Inkonsistenz, Muskelgedächtnis aus früheren Android-Versionen).
Replizieren: Erstellen Sie ein Basisprojekt. Geben Sie dies einonCreate
:
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();
Erwartet: EineSchaltfläche Abbrechen wird im Dialogfeld angezeigt.
Aktuell: EineSchaltfläche Abbrechen wird nicht angezeigt.
Screenshots: 4.0.3 (OK) und 4.1.1 (möglicherweise falsch?).
Problem Nr. 2: Falsches Entlassungsverhalten
Der Dialog ruft den Listener auf, den er tatsächlich aufrufen soll, und ruft dann immer den OnDateSetListener
Listener an. Beim Abbrechen wird die set-Methode weiterhin aufgerufen, und beim Festlegen wird die Methode zweimal aufgerufen.
Replizieren: Verwenden Sie den Code Nr. 1, fügen Sie jedoch den folgenden Code hinzu (Sie werden sehen, dass dies den ersten Code löst, jedoch nur visuell / Benutzeroberfläche):
picker.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Log.d("Picker", "Cancel!");
}
});
Erwartet:
- Das Drücken der Taste ZURÜCK oder das Klicken außerhalb des Dialogfelds sollte nichts bewirken .
- Durch Drücken von "Abbrechen" wird Picker Abbrechen gedruckt ! .
- Durch Drücken von "Set" sollte Picker Set gedruckt werden ! .
Aktuell:
- Durch Drücken der BACK-Taste oder Klicken außerhalb des Dialogfelds wird Picker Set! .
- Durch Drücken von "Abbrechen" wird die Auswahl abgebrochen. Abbrechen! und dann Picker Set! .
- Durch Drücken von "Set" wird das Picker Set gedruckt ! und dann Picker Set! .
Protokollzeilen, die das Verhalten zeigen:
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!
Andere Notizen und Kommentare
- Es
DatePickerFragment
spielt keine Rolle, es um ein zu wickeln. Ich habe das Problem für Sie vereinfacht, aber ich habe es getestet.
Antworten:
Hinweis: Ab Lollipop behoben , Quelle hier . Automatisierte Klasse zur Verwendung in Clients (kompatibel mit allen Android-Versionen) ebenfalls aktualisiert.
TL; DR: 1-2-3 absolut einfache Schritte für eine globale Lösung:
OnDateSetListener
in Ihrer Aktivität (oder ändern Sie die Klasse entsprechend Ihren Anforderungen).Lösen Sie den Dialog mit diesem Code aus (in diesem Beispiel verwende ich ihn in a
Fragment
):Und das ist alles was es braucht! Der Grund, warum ich meine Antwort immer noch als "akzeptiert" behalte, ist, dass ich meine Lösung immer noch bevorzuge, da sie einen sehr geringen Platzbedarf im Clientcode hat, das grundlegende Problem behebt (der Listener wird in der Framework-Klasse aufgerufen) und bei Konfigurationsänderungen einwandfrei funktioniert und es leitet die Codelogik an die Standardimplementierung in früheren Android-Versionen weiter, die nicht von diesem Fehler betroffen sind (siehe Klassenquelle).
Ursprüngliche Antwort (aus historischen und didaktischen Gründen aufbewahrt):
Fehlerquelle
OK, es sieht tatsächlich so aus, als wäre es tatsächlich ein Fehler, und jemand anderes hat ihn bereits gefüllt. Ausgabe 34833 .
Ich habe festgestellt, dass das Problem möglicherweise in liegt
DatePickerDialog.java
. Wo es liest:Ich würde vermuten, dass es gewesen sein könnte:
Wenn mir jetzt jemand sagen kann, wie ich Android einen Patch- / Fehlerbericht vorschlagen kann, würde ich mich freuen. In der Zwischenzeit schlug ich eine mögliche Lösung (einfach) als angehängte Version der
DatePickerDialog.java
dortigen Ausgabe vor.Konzept, um den Fehler zu vermeiden
Stellen Sie den Listener
null
im Konstruktor auf ein und erstellen SieBUTTON_POSITIVE
später Ihre eigene Schaltfläche . Das war's, Details unten.Das Problem tritt auf, weil
DatePickerDialog.java
, wie Sie in der Quelle sehen können, eine globale Variable (mCallBack
) aufgerufen wird, die den Listener speichert, der im Konstruktor übergeben wurde:Der Trick besteht also darin, einen
null
Listener bereitzustellen, der als Listener gespeichert werden soll, und dann Ihre eigenen Schaltflächen zu rollen (unten ist der Originalcode von Nr. 1, aktualisiert):Jetzt wird es aufgrund der möglichen Korrektur funktionieren, die ich oben gepostet habe.
Und da
DatePickerDialog.java
beinull
jedem Lesen nach a gesucht wirdmCallback
( seit den Tagen von API 3 / 1.5 scheint es, dass Honeycomb natürlich nicht überprüft werden kann), wird die Ausnahme nicht ausgelöst. In Anbetracht der Tatsache, dass Lollipop das Problem behoben hat, werde ich mich nicht damit befassen: Verwenden Sie einfach die Standardimplementierung (die in der von mir bereitgestellten Klasse behandelt wird).Zuerst hatte ich Angst, das nicht anzurufen
clearFocus()
, aber ich habe hier getestet und die Protokollleitungen waren sauber. Diese Linie, die ich vorgeschlagen habe, ist vielleicht doch nicht nötig, aber ich weiß es nicht.Kompatibilität mit früheren API-Levels (bearbeitet)
Wie ich im Kommentar unten ausgeführt habe, war dies ein Konzept, und Sie können die von mir verwendete Klasse von meinem Google Drive-Konto herunterladen . So wie ich es verwendet habe, wird die Standardsystemimplementierung für Versionen verwendet, die vom Fehler nicht betroffen sind.
Ich habe einige Annahmen (Schaltflächennamen usw.) getroffen, die für meine Anforderungen geeignet sind, weil ich den Code für Boilerplates in Clientklassen auf ein Minimum reduzieren wollte. Beispiel für die vollständige Verwendung:
quelle
Ich werde mein eigenes Riff zu der von David Cesarino veröffentlichten Lösung hinzufügen, falls Sie keine Fragmente verwenden, und eine einfache Möglichkeit suchen, dies in allen Versionen (2.1 bis 4.1) zu beheben:
quelle
android.R.string.ok
und dieandroid.R.string.cancel
Felder anstelle der eigenen zu verwenden. Und danke für die Antwort.dateToShow
? Die andere Null dort ist eigentlich das "Update", also sollte das da sein. Auf welcher Version bist du?import
du hattestField
? Ich habe 6 Optionen und keine davon hat funktioniert.Bis der Fehler behoben ist, empfehle ich, DatePickerDialog oder TimePickerDialog nicht zu verwenden. Verwenden Sie einen benutzerdefinierten AlertDialog mit dem TimePicker / DatePicker-Widget.
Ändern Sie TimePickerDialog mit;
Ändern Sie DatePickerDialog mit;
quelle
Die für TimePicker basierend auf der Lösung von David Cesarino, "TL; DR: 1-2-3 absolut einfache Schritte für eine globale Lösung"
TimePickerDialog bietet keine Funktionen wie DatePickerDialog.getDatePicker. Daher muss der OnTimeSetListener- Listener bereitgestellt werden. Um die Ähnlichkeit mit der DatePicker-Problemumgehungslösung beizubehalten, habe ich das alte mListener-Konzept beibehalten. Sie können es bei Bedarf ändern.
Calling and Listener entspricht der ursprünglichen Lösung. Einfach einschließen
Elternklasse erweitern,
Implementieren
Beispiel für einen Anruf
(Aktualisiert, um Abbrechen zu behandeln)
quelle
Für den Fall, dass jemand eine schnelle Problemumgehung wünscht, ist hier der Code, den ich verwendet habe:
}}
Wobei layout.date_picker_view eine einfache Layoutressource mit einem DatePicker als einzigem Element ist:
Hier ist das vollständige Tutorial, falls Sie interessiert sind.
quelle
Meine einfache Lösung. Wenn Sie es erneut auslösen möchten, führen Sie einfach "resetFired" aus (z. B. beim erneuten Öffnen des Dialogfelds).
quelle
onDateSet
einmal aufgerufen wird, wenn der Dialog auf irgendeine Weise geschlossen wird. Wenn dieser Mittelwert die Zeit einstellen sollte, wird er erneut ausgelöst, sodass wir den zweiten Anruf abfangen müssen, nicht den ersten wie Sie,isJellyBeanOrAbove()
Versionen, die niedriger als Jellybean sind, gibt es nicht den Fehler, um den es bei dieser ganzen Frage geht. Wenn wir den zweiten Aufruf abfangen möchten, wird der Code erst ausgeführt, wenn wir diese Prüfung durchführen. Glauben Sie mir, ich habe es versucht Code auf Emulatoren & realen Geräten (mit verschiedenen Versionen) mehrmals & es funktioniert wie ein ZauberLaut Ankur Chaudharys brillanter Antwort zu diesem ähnlichen
TimePickerDialog
Problem wird das gesamte Problem mit minimalem Aufwand gelöst , wenn wir nach innen schauen,onDateSet
ob die angegebene AnsichtisShown()
vorliegt oder nicht, ohne dass die Auswahl erweitert oder nach abscheulichen Flaggen gesucht werden muss, die den Code umgehen oder suchen Sie nach der Betriebssystemversion, gehen Sie einfach wie folgt vor:und natürlich kann das Gleiche
onTimeSet
gemäß Ankur's Antwort getan werdenquelle
Die Art und Weise, wie ich mit dieser Situation umgegangen bin, bestand darin, ein Flag zu verwenden und die Methoden onCancel und onDismiss zu überschreiben.
onCancel wird nur aufgerufen, wenn der Benutzer außerhalb des Dialogfelds oder der Zurück-Schaltfläche berührt. onDismiss wird immer aufgerufen
Das Setzen eines Flags in der onCancel-Methode kann dazu beitragen, in der onDismiss-Methode die Absicht des Benutzers zu filtern: Aktion abbrechen oder Aktion ausführen. Unten ein Code, der die Idee zeigt.
quelle
Es gibt eine sehr einfache Problemumgehung, wenn Ihre Anwendung die Aktionsleiste nicht verwendet. Beachten Sie übrigens, dass einige Apps auf diese Funktion angewiesen sind, da das Abbrechen der Datumsauswahl eine besondere Bedeutung hat (z. B. wird das Datumsfeld durch eine leere Zeichenfolge gelöscht, was für einige Apps eine gültige und aussagekräftige Art der Eingabe ist ) und die Verwendung von Booleschen Flags, um zu verhindern, dass das Datum zweimal auf OK gesetzt wird, hilft Ihnen in diesem Fall nicht weiter.
Re. Für die eigentliche Korrektur müssen Sie keine neuen Schaltflächen oder einen eigenen Dialog erstellen. Es geht darum, sowohl mit den älteren Versionen von Android als auch mit den fehlerhaften (4. ) und zukünftigen Versionen kompatibel zu sein, wobei letzteres natürlich nicht sicher ist. Beachten Sie, dass in Android 2. onStop () für android.app.Dialog überhaupt nichts tut und in 4. * mActionBar.setShowHideAnimationEnabled (false), was nur wichtig ist, wenn Ihre App über eine Aktionsleiste verfügt. Das onStop () in DatePickerDialog, das von Dialog erbt, trägt nur mDatePicker.clearFocus () bei (Stand der neuesten Korrektur für Android-Quellen 4.3), was nicht unbedingt erforderlich erscheint.
Daher sollte das Ersetzen von onStop () durch eine Methode, die nichts bewirkt, in vielen Fällen Ihre App reparieren und sicherstellen, dass dies auf absehbare Zeit so bleibt. Erweitern Sie daher einfach die DatePickerDialog-Klasse mit Ihrer eigenen und überschreiben Sie onStop () mit einer Dummy-Methode. Sie müssen auch einen oder zwei Konstruktoren gemäß Ihren Anforderungen bereitstellen. Beachten Sie auch, dass Sie nicht versucht sein sollten, dieses Update zu übertreiben, indem Sie beispielsweise versuchen, etwas direkt mit der Aktivitätsleiste zu tun, da dies Ihre Kompatibilität nur mit den neuesten Versionen von Android einschränken würde. Beachten Sie auch, dass es schön wäre, das Super für DateStickers onStop () aufrufen zu können, da der Fehler nur in onStop () in DatePickerDialog selbst, nicht jedoch in DatePickerDialogs Superklasse auftritt. Dies würde jedoch erfordern, dass Sie super.super.onStop () aus Ihrer benutzerdefinierten Klasse aufrufen. was Java nicht zulässt, da es gegen die Kapselungsphilosophie verstößt :) Unten ist meine kleine Klasse, mit der ich DatePickerDialog verifiziert habe. Ich hoffe, dieser Kommentar wäre für jemanden nützlich. Wojtek Jarosz
}}
quelle
Probieren Sie die folgenden Konzepte aus.
Die onDateSet () -Methode ruft zweimal auf (wenn Sie emulator einchecken. Es wird zweimal aufgerufen. Wenn Sie ein reales Gerät verwenden, wird es einmal korrekt aufgerufen. Wenn Sie einen Emulator verwenden, verwenden Sie den Zähler. Wenn Sie in einem realen Gerät arbeiten, dann
Zählervariable ignorieren. Für echte Geräte funktioniert es für mich.) Wenn der Benutzer auf die Schaltfläche in DatePickerDialog klickt.
Dazu sollten Sie einen Zählerwert beibehalten und nichts tun, wenn die Motte das erste Mal aufruft, und die Operation ausführen, wenn die Methode das zweite Mal aufruft.
Beachten Sie die folgenden Codierungsausschnitte
Zum Abbrechen des Datepicker-Dilalogs funktioniert es für mich. Für den Emulator ist es kein Wokring
Es funktioniert für mich für ein echtes Gerät. Aber für den Emulator funktioniert es nicht richtig. Ich denke, es ist ein Android-Emulator-Fehler.
quelle
Eine einfache Lösung wäre die Verwendung eines Booleschen Werts, um den zweiten Lauf zu überspringen
quelle
onStop
Aufrufen der Methode, wenn dies nicht der Fall sein sollte. Das zweimalige Auslösen ist eine Folge des Fehlers.onDateSet
. Also kaputt.onDateSet
einmal aufgerufen wird, aber wenn Sie "erledigt" oder "setzen" wählen, wird es zweimal aufgerufen. Daher müssen wir nur das erste überspringen. Wenn es also zweimal aufgerufen wird, haben wir nur dann das richtige DatumSie können onCancel () überschreiben und setOnDismissListener () verwenden, um negative Benutzeraktionen zu erkennen. Und mit einem DatePickerDialog.BUTTON_POSITIVE wissen Sie, dass der Benutzer ein neues Datum festlegen möchte.
dann auf setDate prüfen:
quelle
Hier ist meine Problemumgehungsklasse für DatePickerDialog auf der Schaltfläche Abbrechen sowie auf der Schaltfläche Zurück. Kopieren und Verwenden im Stil von DatePickerDialog (Da der Listener statusbehaftet ist, müssen wir bei der Verwendung eine neue Instanz erstellen, da sonst mehr Code erforderlich ist, damit es funktioniert.)
Verwenden:
Klasse:
}}
quelle
Ich benutze Datums-, Zeit- und Nummernwähler. Die Nummernwähler rufen onValueChanged immer dann auf, wenn der Benutzer eine Nummer auswählt, bevor der Picker entlassen wird. Daher hatte ich bereits eine solche Struktur, um nur dann etwas mit dem Wert zu tun, wenn der Picker entlassen wird:
Ich habe dies erweitert, um benutzerdefinierte onClickListener für meine Schaltflächen festzulegen, mit einem Argument, um zu sehen, auf welche Schaltfläche geklickt wurde. Jetzt kann ich überprüfen, welche Schaltfläche getippt wurde, bevor ich meinen Endwert einstelle:
Und dann habe ich das erweitert, um mit den Datums- und Zeittypen für Datums- und Zeitauswahl sowie dem Int-Typ für Nummernauswahl zu arbeiten.
Ich habe dies gepostet, weil ich dachte, es sei einfacher als einige der oben genannten Lösungen, aber jetzt, wo ich den gesamten Code eingefügt habe, ist es wohl nicht viel einfacher! Aber es passte gut in die Struktur, die ich bereits hatte.
Update für Lollipop: Anscheinend tritt dieser Fehler nicht auf allen Android 4.1-4.4-Geräten auf, da ich einige Berichte von Benutzern erhalten habe, deren Datums- und Zeitauswahl die Rückrufe onDateSet und onTimeSet nicht aufgerufen hat. Und der Fehler wurde offiziell in Android 5.0 behoben. Mein Ansatz funktionierte nur auf Geräten, auf denen der Fehler vorliegt, da meine benutzerdefinierten Schaltflächen den onClick-Handler des Dialogfelds nicht aufriefen. Dies ist der einzige Ort, an dem onDateSet und onTimeSet aufgerufen werden, wenn der Fehler nicht vorhanden ist. Ich habe meinen obigen Code aktualisiert, um den onClick des Dialogfelds aufzurufen. Jetzt funktioniert es, ob der Fehler vorliegt oder nicht.
quelle
Ich mochte die Antwort von David Cesarino oben, wollte aber etwas, das den defekten Dialog ersetzt und bei jedem Dialog funktioniert, bei dem möglicherweise ein Abbruch fehlt oder ein falsches Abbruchverhalten vorliegt. Hier sind abgeleitete Klassen für DatePickerDialog / TimePickerDialog, die als Drop-in-Ersetzungen funktionieren sollen. Dies sind keine benutzerdefinierten Ansichten. Es verwendet den Systemdialog, ändert jedoch nur das Verhalten der Schaltfläche Abbrechen / Zurück, um wie erwartet zu funktionieren.
Dies sollte auf API-Ebene 3 und höher funktionieren. Also im Grunde jede Version von Android (ich habe es speziell auf Jellybean und Lollipop getestet).
DatePickerDialog:
TimePickerDialog:
quelle
Meine Arbeitsversion mit ClearButton mit Lambda Expressions:
quelle
Für TimePickerDialog kann die Problemumgehung wie folgt aussehen:
Ich delegiere alle Ereignisse an den Wrapper KitKatSetTimeListener und feuere nur dann auf den ursprünglichen OnTimeSetListener zurück, wenn auf BUTTON_POSITIVE geklickt wird.
quelle
Nachdem ich einige der hier veröffentlichten Vorschläge getestet habe, denke ich persönlich, dass diese Lösung die einfachste ist. Ich übergebe "null" als Listener im DatePickerDialog-Konstruktor. Wenn ich dann auf die Schaltfläche "OK" klicke, rufe ich meinen onDateSearchSetListener auf:
quelle
Ich weiß, dass dieser Beitrag seit fast einem Jahr hier ist, aber ich dachte, ich sollte meine Ergebnisse veröffentlichen. Sie können den Listener weiterhin behalten (anstatt ihn auf Mull zu setzen) und diese Arbeit wie erwartet ausführen. Der Schlüssel besteht darin, implizit die Tasten "OK" oder (und) "Abbrechen" zu setzen. Ich habe es getestet und es funktioniert dankbar für mich. Der Hörer wird nicht zweimal gefeuert.
Schauen Sie sich dieses Beispiel an:
quelle
timePickerListener
wird immer noch aufgerufen, unabhängig davon, was Sie in Ihrem Dialog tun. Ich muss nicht einmal testen, um das zu wissen, Sie müssen sich nur die Quellen ansehen : Wenn Sie es nicht einstellennull
,tryNotifyTimeSet()
wird der HöreronTimeSet()
sowohl in seineronClick()
als auch in seiner Position aufgerufenonStop()
.