--- Catatan untuk moderator: Hari ini (15 Juli), saya perhatikan seseorang telah menghadapi masalah ini di sini . Tapi saya tidak yakin apakah pantas untuk menutup ini sebagai duplikat, karena saya pikir saya memberikan penjelasan yang lebih baik tentang masalah ini. Saya tidak yakin apakah saya harus mengedit pertanyaan lain dan menempelkan konten ini di sana, tetapi saya tidak nyaman terlalu banyak mengubah pertanyaan orang lain. ---
Saya punya sesuatu yang aneh di sini.
Saya tidak berpikir masalahnya tergantung pada SDK yang Anda buat. Versi perangkat OS adalah yang penting.
Masalah # 1: tidak konsisten secara default
DatePickerDialog
telah diubah (?) di Jelly Bean dan sekarang hanya menyediakan tombol Selesai . Versi sebelumnya menyertakan tombol Batal , dan ini dapat memengaruhi pengalaman pengguna (inkonsistensi, memori otot dari versi Android sebelumnya).
Replika: Buat proyek dasar. Masukkan inionCreate
:
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();
Diharapkan: Sebuah Batal tombol untuk muncul dalam dialog.
Saat ini: Sebuah Batal tombol tidak muncul.
Tangkapan layar: 4.0.3 (OK) dan 4.1.1 (mungkin salah?).
Masalah # 2: perilaku salah memberhentikan
Dialog memanggil pendengar mana pun yang seharusnya dipanggil, dan kemudian selalu memanggil OnDateSetListener
pendengar. Membatalkan masih memanggil metode yang ditetapkan, dan mengaturnya memanggil metode dua kali.
Replika: Gunakan kode # 1, tetapi tambahkan kode di bawah ini (Anda akan melihat ini menyelesaikan # 1, tetapi hanya secara visual / UI):
picker.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Log.d("Picker", "Cancel!");
}
});
Diharapkan:
- Menekan tombol BACK atau mengklik di luar dialog seharusnya tidak melakukan apa-apa .
- Menekan "Cancel" harus mencetak Picker Cancel! .
- Menekan "Set" harus mencetak Picker Set! .
Arus:
- Menekan tombol BACK atau mengklik di luar dialog akan mencetak Picker Set! .
- Menekan "Cancel" mencetak Picker Cancel! dan kemudian Picker Set! .
- Menekan "Set" mencetak Picker Set! dan kemudian Picker Set! .
Baris log yang menunjukkan perilaku:
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!
Catatan dan komentar lainnya
- Membungkusnya di sekitar
DatePickerFragment
tidak masalah. Saya menyederhanakan masalah untuk Anda, tetapi saya telah mengujinya.
Jawaban:
Catatan: Diperbaiki pada Lollipop , sumber di sini . Kelas otomatis untuk digunakan dalam klien (kompatibel dengan semua versi Android) diperbarui juga.
TL; DR: 1-2-3 langkah mudah mati untuk solusi global:
OnDateSetListener
dalam aktivitas Anda (atau ubah kelas sesuai kebutuhan Anda).Trigger dialog dengan kode ini (dalam sampel ini, saya menggunakannya di dalam a
Fragment
):Dan hanya itu yang dibutuhkan! Alasan saya masih menjaga jawaban saya sebagai "diterima" adalah karena saya masih lebih suka solusi saya karena memiliki jejak yang sangat kecil dalam kode klien, ini membahas masalah mendasar (pendengar dipanggil dalam kelas kerangka kerja), berfungsi dengan baik di seluruh perubahan konfigurasi dan itu merutekan logika kode ke implementasi default di versi Android sebelumnya yang tidak terganggu oleh bug ini (lihat sumber kelas).
Jawaban asli (disimpan karena alasan historis dan didaktik):
Sumber bug
OK, sepertinya itu memang bug dan orang lain sudah mengisinya. Masalah 34833 .
Saya telah menemukan bahwa masalahnya mungkin di
DatePickerDialog.java
. Di mana bunyinya:Saya kira itu bisa saja:
Sekarang jika seseorang dapat memberi tahu saya bagaimana saya bisa mengusulkan laporan tambalan / bug ke Android, saya akan senang melakukannya. Sementara itu, saya menyarankan perbaikan yang mungkin (sederhana) sebagai versi terlampir
DatePickerDialog.java
dalam Masalah di sana.Konsep untuk menghindari bug
Atur pendengar ke
null
dalam konstruktor dan buatBUTTON_POSITIVE
tombol Anda sendiri nanti . Itu dia, detailnya di bawah.Masalahnya terjadi karena
DatePickerDialog.java
, seperti yang Anda lihat di sumber, memanggil variabel global (mCallBack
) yang menyimpan pendengar yang diteruskan dalam konstruktor:Jadi, triknya adalah menyediakan
null
pendengar untuk disimpan sebagai pendengar, dan kemudian menggulung set tombol Anda sendiri (di bawah ini adalah kode asli dari # 1, diperbarui):Sekarang ini akan berfungsi karena kemungkinan koreksi yang saya posting di atas.
Dan karena
DatePickerDialog.java
memeriksa untuknull
setiap kali dibacamCallback
( sejak hari-hari API 3 / 1,5 tampaknya --- tidak dapat memeriksa Honeycomb tentu saja), itu tidak akan memicu pengecualian. Mengingat Lollipop memperbaiki masalah ini, saya tidak akan memeriksanya: cukup gunakan implementasi default (tercakup dalam kelas yang saya berikan).Awalnya saya takut tidak menelepon
clearFocus()
, tapi saya sudah menguji di sini dan garis-garis Log bersih. Jadi kalimat yang saya usulkan itu mungkin tidak diperlukan, tetapi saya tidak tahu.Kompatibilitas dengan level API sebelumnya (diedit)
Seperti yang saya tunjukkan dalam komentar di bawah, itu adalah sebuah konsep, dan Anda dapat mengunduh kelas yang saya gunakan dari akun Google Drive saya . Cara saya menggunakan, implementasi sistem default digunakan pada versi yang tidak terpengaruh oleh bug.
Saya mengambil beberapa asumsi (nama tombol dll.) Yang cocok untuk kebutuhan saya karena saya ingin mengurangi kode boilerplate di kelas klien seminimal mungkin. Contoh penggunaan penuh:
sumber
Saya akan menambahkan riff saya sendiri pada solusi yang diposting David Cesarino, jika Anda tidak menggunakan Fragmen, dan ingin cara mudah untuk memperbaikinya di semua versi (2.1 hingga 4.1):
sumber
android.R.string.ok
danandroid.R.string.cancel
bidang, bukan milik pengguna. Dan terima kasih atas jawabannya.dateToShow
? Null lainnya sebenarnya ada "fix" sehingga harus ada. Kamu versi apa?import
untuk apaField
? Saya memiliki 6 opsi dan tidak ada yang bekerja.Sampai bug diperbaiki, saya sarankan untuk tidak menggunakan DatePickerDialog atau TimePickerDialog. Gunakan AlertDialog yang dibuat khusus dengan widget TimePicker / DatePicker;
Ubah TimePickerDialog dengan;
Ubah DatePickerDialog dengan;
sumber
Yang untuk TimePicker berdasarkan pada solusi oleh David Cesarino, "TL; DR: 1-2-3 langkah mudah mati untuk solusi global"
TimePickerDialog tidak menyediakan fungsionalitas seperti DatePickerDialog.getDatePicker. Jadi, pendengar OnTimeSetListener harus disediakan. Hanya untuk menjaga kesamaan dengan solusi penyelesaian DatePicker, saya telah mempertahankan konsep mListener lama. Anda dapat mengubahnya jika perlu.
Panggilan dan Pendengar sama dengan solusi asli. Cukup sertakan
memperpanjang kelas induk,
Melaksanakan
contoh memanggil
(Diperbarui untuk menangani pembatalan)
sumber
Jika ada yang menginginkan solusi cepat, inilah kode yang saya gunakan:
}
Di mana layout.date_picker_view adalah sumber daya tata letak sederhana dengan DatePicker karena itu hanya elemen:
Inilah tutorial lengkap jika Anda tertarik.
sumber
Solusi sederhana saya. Ketika Anda ingin menjalankannya lagi jalankan saja "resetFired" (katakanlah ketika membuka dialog lagi).
sumber
onDateSet
akan dipanggil sekali ketika dialog ditutup dengan cara apa pun, dan jika itu berarti mengatur waktu maka akan dipecat lagi, jadi kita perlu menangkap panggilan kedua, bukan yang pertama seperti yang Anda lakukan,isJellyBeanOrAbove()
, versi lebih rendah dari Jellybean tidak memiliki bug di mana semua pertanyaan ini adalah tentang, & mengingat kami ingin menangkap panggilan kedua, kode tidak akan berjalan kecuali jika kami melakukan pemeriksaan ini, percayalah, saya sudah mencoba kode pada Emulator & perangkat nyata (dengan versi yang berbeda) beberapa kali & ini berfungsi seperti pesonaMenurut jawaban brilian Ankur Chaudhary pada
TimePickerDialog
masalah yang sama , jika kami memeriksa di dalamonDateSet
apakah tampilan yang diberikanisShown()
atau tidak, itu akan menyelesaikan seluruh masalah dengan upaya minimal, tanpa perlu memperluas pemilih atau memeriksa beberapa bendera mengerikan yang mengelilingi kode atau bahkan memeriksa versi OS, lakukan saja hal berikut:dan tentu saja hal yang sama dapat dilakukan
onTimeSet
sesuai jawaban Ankursumber
Cara saya mengelola situasi ini menggunakan bendera dan mengganti metode onCancel dan onDismiss.
onCancel dipanggil hanya ketika pengguna menyentuh di luar dialog atau tombol kembali. onDismiss selalu dipanggil
Mengatur bendera di metode onCancel dapat membantu menyaring dalam metode onDismiss maksud pengguna: batalkan tindakan atau tindakan yang dilakukan. Di bawah ini beberapa kode yang menunjukkan ide.
sumber
Ada solusi yang sangat sederhana, jika aplikasi Anda tidak menggunakan panel tindakan. Perhatikan bahwa beberapa aplikasi bergantung pada fungsi ini untuk bekerja, karena membatalkan pemilih tanggal memiliki arti khusus (misalnya, membersihkan bidang tanggal ke string kosong, yang untuk beberapa aplikasi adalah jenis input yang valid dan bermakna. ) dan menggunakan bendera boolean untuk mencegah tanggal ditetapkan dua kali pada OK tidak akan membantu Anda dalam kasus ini.
Kembali. perbaikan yang sebenarnya, Anda tidak perlu membuat tombol baru atau dialog Anda sendiri. Intinya adalah agar kompatibel dengan keduanya, versi Android yang lebih lama, yang buggy (4. ) dan yang di masa depan, meskipun yang terakhir tidak mungkin untuk dipastikan, tentu saja. Perhatikan bahwa di Android 2. , onStop () untuk android.app.Dialog tidak melakukan apa-apa, dan di 4. * ia melakukan mActionBar.setShowHideAnimationEnabled (false) yang penting hanya jika aplikasi Anda memiliki bilah tindakan. OnStop () di DatePickerDialog, yang mewarisi dari Dialog, hanya berkontribusi mDatePicker.clearFocus () (pada perbaikan terbaru ke sumber-sumber Android 4.3), yang tampaknya tidak penting.
Oleh karena itu mengganti onStop () dengan metode yang tidak melakukan apa pun dalam banyak hal harus memperbaiki aplikasi Anda dan memastikan bahwa itu akan tetap seperti itu di masa mendatang. Dengan demikian cukup memperluas kelas DatePickerDialog dengan Anda sendiri dan menimpa onStop () sedikit pun dengan metode dummy. Anda juga harus menyediakan satu atau dua konstruktor, sesuai kebutuhan Anda. Perhatikan juga bahwa seseorang tidak boleh tergoda untuk mencoba melakukan perbaikan yang berlebihan ini dengan misalnya mencoba melakukan sesuatu dengan bilah aktivitas secara langsung, karena ini akan membatasi kompatibilitas Anda hanya untuk versi terbaru Android. Perhatikan juga bahwa akan lebih baik untuk memanggil super untuk DatePicker's onStop () karena bug hanya ada di onStop () di DatePickerDialog sendiri, tetapi tidak di super class DatePickerDialog. Namun, ini akan mengharuskan Anda untuk memanggil super.super.onStop () dari kelas khusus Anda, Java mana yang tidak akan membiarkan Anda melakukannya, karena bertentangan dengan filosofi enkapsulasi :) Di bawah ini adalah kelas kecil saya yang saya gunakan untuk memverifikasi DatePickerDialog. Semoga komentar ini bermanfaat bagi seseorang. Wojtek Jarosz
}
sumber
Coba konsep di bawah ini.
metode onDateSet () memanggil dua kali (jika Anda memeriksa emulator.it memanggil dua kali. Jika menggunakan perangkat nyata maka itu akan memanggil dengan benar satu kali. Jika Anda menggunakan emulator maka gunakan counter. Jika Anda bekerja di perangkat nyata maka abaikan variabel penghitung. Untuk perangkat nyata ini berfungsi untuk saya.)
ketika pengguna mengklik tombol di DatePickerDialog.
untuk ini, Anda harus mempertahankan nilai penghitung dan tidak ada yang dilakukan saat mothod memanggil pertama kali dan melakukan operasi ketika metode memanggil 2nd time.
Lihat cuplikan kode di bawah ini
Untuk membatalkan datepicker, dilalog-nya berfungsi untuk saya. Untuk emulatornya tidak wokring
Ini bekerja untuk saya untuk perangkat nyata. Tetapi untuk emulator tidak bekerja dengan benar. Saya pikir ini adalah bug emulator android.
sumber
Solusi sederhana akan menggunakan boolean untuk melewati run kedua
sumber
onStop
memanggil metode ketika seharusnya tidak ... itu menembakkan dua kali merupakan konsekuensi dari bug.onDateSet
. Dengan demikian, rusak.onDateSet
akan dipanggil sekali, tetapi ketika memilih "selesai" atau "set" maka akan dipanggil dua kali. Untuk itu kita hanya perlu melewati yang pertama, jadi jika itu dipanggil dua kali maka & hanya kemudian kita memiliki tanggal yang benarAnda dapat mengganti onCancel () dan menggunakan setOnDismissListener () untuk mendeteksi tindakan pengguna yang negatif. Dan dengan DatePickerDialog.BUTTON_POSITIVE Anda tahu bahwa pengguna ingin menetapkan tanggal baru.
kemudian periksa setDate:
sumber
Ini adalah kelas penyelesaian masalah saya untuk DatePickerDialog pada tombol batal serta meninggalkannya dengan tombol kembali. Salin & gunakan dengan gaya DatePickerDialog (Karena pendengarnya stateful, kita harus membuat instance baru saat digunakan, jika tidak diperlukan lebih banyak kode untuk membuatnya berfungsi)
Menggunakan:
Kelas:
}
sumber
Saya menggunakan pemilih tanggal, pemilih waktu dan pemilih nomor. Pemilih nomor memanggil pada ValueChanged setiap kali pengguna memilih nomor, sebelum pemetik dipecat, jadi saya sudah memiliki struktur seperti ini untuk melakukan sesuatu dengan nilai hanya ketika pemetik dipecat:
Saya memperluas ini untuk mengatur onClickListeners khusus untuk tombol saya, dengan argumen untuk melihat tombol mana yang diklik. Sekarang saya dapat memeriksa tombol mana yang diketuk sebelum saya menetapkan nilai akhir saya:
Dan kemudian saya memperluasnya untuk bekerja dengan tipe tanggal dan waktu untuk pemilih tanggal dan waktu serta tipe int untuk pemilih nomor.
Saya memposting ini karena saya pikir itu lebih sederhana daripada beberapa solusi di atas, tetapi sekarang saya sudah memasukkan semua kode, saya kira itu tidak jauh lebih sederhana! Tapi itu pas dengan struktur yang sudah saya miliki.
Pembaruan untuk Lollipop: Rupanya bug ini tidak terjadi pada semua perangkat Android 4.1-4.4, karena saya menerima beberapa laporan dari pengguna yang pemetik tanggal dan waktunya tidak memanggil callback onDateSet dan onTimeSet. Dan bug itu secara resmi diperbaiki di Android 5.0. Pendekatan saya hanya bekerja pada perangkat di mana bug hadir, karena tombol kustom saya tidak memanggil dialog handler onClick, yang merupakan satu-satunya tempat yang onDateSet dan onTimeSet dipanggil ketika bug tidak ada. Saya memperbarui kode saya di atas untuk memanggil onClick dialog, jadi sekarang berfungsi apakah bug ada atau tidak.
sumber
Saya menyukai jawaban David Cesarino di atas, tetapi menginginkan sesuatu yang merupakan pengganti untuk dialog yang rusak dan akan bekerja pada dialog apa pun yang mungkin tidak ada, batalkan / ada perilaku pembatalan yang salah. Berikut adalah turunan kelas untuk DatePickerDialog / TimePickerDialog yang seharusnya berfungsi sebagai pengganti pengganti. Ini bukan tampilan khusus. Ini menggunakan dialog sistem, tetapi hanya mengubah perilaku tombol batal / kembali untuk bekerja seperti yang diharapkan.
Ini harus bekerja pada API level 3 dan lebih tinggi. Jadi, pada dasarnya semua versi Android (saya mengujinya pada jellybean dan lollipop khusus).
DatePickerDialog:
TimePickerDialog:
sumber
Versi kerja saya dengan ClearButton menggunakan Ekspresi Lambda:
sumber
Untuk TimePickerDialog solusinya dapat sebagai berikut:
Saya mendelegasikan semua acara untuk membungkus KitKatSetTimeListener, dan hanya menjalankan kembali ke OnTimeSetListener asli jika BUTTON_POSITIVE diklik.
sumber
Setelah menguji beberapa saran yang diposting di sini, saya pribadi berpikir solusi ini adalah yang paling sederhana. Saya meneruskan "null" sebagai pendengar saya di konstruktor DatePickerDialog, dan kemudian ketika saya mengklik tombol "OK" saya memanggil onDateSearchSetListener saya:
sumber
Saya tahu posting ini telah ada di sini selama hampir satu tahun tetapi saya pikir saya harus memposting temuan saya. Anda masih bisa menjaga pendengar (alih-alih mengaturnya untuk merenung) dan masih memiliki pekerjaan ini seperti yang diharapkan. Kuncinya adalah secara implisit mengatur tombol "OK" atau (dan) "batal". Saya mengujinya dan itu bekerja untuk saya. Pendengar tidak dipecat dua kali.
Lihat contoh ini,
sumber
timePickerListener
masih dipanggil terlepas dari apa yang Anda lakukan dalam dialog Anda. Saya bahkan tidak perlu menguji untuk mengetahui hal itu, Anda hanya perlu melihat sumbernya : jika Anda tidak menyetelnyanull
,tryNotifyTimeSet()
akan memanggil pendengaronTimeSet()
baik dalamonClick()
danonStop()
.