Teknik di Sprout: Membangun pemilih bulan Android

Diterbitkan: 2020-06-26

Catatan: Artikel ini didasarkan pada Komponen Material versi 1.2.0-beta01 per 1 Juni 2020 .

Selama tiga setengah tahun saya bekerja di tim Android kecil di Sprout Social, salah satu hal utama yang memotivasi saya untuk masuk kerja setiap hari adalah kebebasan dan kepercayaan dari perusahaan kami untuk mengatasi masalah dengan cara apa pun yang kami anggap terbaik.

Kebebasan untuk meneliti dan mengeksplorasi banyak solusi berbeda untuk masalah yang kami anggap perlu, sambil memperhitungkan jangka waktu untuk memberikan pembaruan produk, memungkinkan kami menemukan solusi terbaik untuk pelanggan dan perangkat lunak kami.

Salah satu tantangan tersebut adalah membangun komponen UI untuk fitur Pelaporan Seluler baru kami. Komponen baru ini adalah pemilih bulan, yang memungkinkan pengguna kami untuk membuat cakupan rentang tanggal untuk laporan analitik.

Tempat awal yang kami pilih adalah Pustaka Komponen Material yang ada. Daripada memulai dari awal, perpustakaan ini secara aktif dipelihara dan diselaraskan dengan spesifikasi Material. Dengan perpustakaan ini sebagai fondasi, kemungkinan besar kita dapat mengurangi jumlah logika yang harus kita tulis sendiri.

Dalam artikel ini, saya akan membahas bagaimana kami mendekati proses ini, beberapa faktor unik dalam membangun aplikasi Android Sprout, beberapa "gotcha" yang muncul (dan diperbaiki) di sepanjang jalan, dan apa yang perlu diketahui jika Anda mengerjakan proyek serupa.

pengantar

Rilis Android Material Components 1.1.0 memperkenalkan Komponen UI Pemilih Tanggal baru. Salah satu tambahan yang disambut baik dari MaterialDatePicker baru ini melalui AppCompat CalendarView adalah kemampuan untuk memilih rentang tanggal baik menggunakan Tampilan Kalender atau Bidang Input Teks.

AppCompat CalendarView lama tidak terlalu fleksibel. Itu adalah komponen yang bagus untuk kasus penggunaan terbatas yang dimaksudkan untuk dipecahkan; yaitu, memilih satu tanggal dan tanggal minimum dan maksimum opsional untuk menentukan batas rentang tanggal yang diizinkan.

MaterialDatePicker baru dibangun dengan lebih banyak fleksibilitas untuk memungkinkan penggunaan fungsionalitas perilaku yang diperluas. Ia bekerja melalui serangkaian antarmuka yang dapat diterapkan seseorang untuk mengubah dan memodifikasi perilaku pemilih.

Modifikasi perilaku ini dilakukan saat runtime melalui serangkaian fungsi pola builder pada kelas MaterialDatePicker.Builder .

Ini berarti kami dapat memperluas perilaku dasar MaterialDatePicker ini melalui komponen antarmuka yang dapat dikomposisi.

Catatan: Meskipun ada sejumlah komponen berbeda yang digunakan MaterialDatePicker , dalam artikel ini kita hanya akan membahas Komponen Pemilihan Tanggal.

Pemilih rentang tanggal

Tim Sprout Social Android sedang dalam proses membangun Bagian Laporan Analytics kami.

Bagian baru ini akan memungkinkan pengguna kami untuk memilih satu set filter dan satu set rentang tanggal yang akan dicakup oleh laporan.

MaterialDatePicker hadir dengan beberapa komponen bawaan yang dapat kami manfaatkan untuk menyelesaikan kasus penggunaan kami.

Untuk kasus kami yang paling umum, memungkinkan pengguna untuk memilih rentang tanggal, MaterialDatePicker yang dibuat sebelumnya sudah cukup:

Dengan blok kode ini, kami mendapatkan Pemilih Tanggal yang memungkinkan pengguna memilih rentang tanggal.

Pemilih tanggal bulanan

Salah satu Sprout Social report yang memiliki pilihan tanggal lebih unik adalah Twitter Trends Report.

Laporan ini berbeda dari yang lain karena alih-alih mengizinkan segala jenis rentang tanggal, laporan ini memberlakukan pemilihan satu bulan, yang berarti pengguna hanya dapat memilih Maret 2020 vs 3 Maret hingga 16 Maret 2020.

Aplikasi web kami menangani ini dengan menggunakan bidang formulir tarik-turun:

MaterialDatePicker tidak memiliki cara untuk memberlakukan pembatasan seperti itu dengan Pemilih Rentang Tanggal Material yang telah dibuat sebelumnya yang dibahas di bagian sebelumnya. Untungnya, MaterialDatePicker dibangun dengan bagian-bagian yang dapat dikomposisi yang memungkinkan kami untuk memperluas perilaku default untuk kasus penggunaan khusus kami.

Perilaku pemilihan tanggal

MaterialDatePicker memanfaatkan DateSelector sebagai antarmuka yang digunakan untuk logika pemilihan pemilih.

Dari Javadoc:

“Antarmuka bagi pengguna {@link MaterialCalendar<S>} untuk mengontrol bagaimana Kalender menampilkan dan mengembalikan pilihan…”

Anda akan melihat bahwa MaterialDatePicker.Builder.dateRangePicker() mengembalikan instance pembangun RangeDateSelector , yang kita gunakan dalam contoh di atas.

Kelas ini adalah pemilih bawaan yang mengimplementasikan DateSelector .

Brainstorming perilaku pemilihan tanggal bulanan

Untuk kasus penggunaan kami, kami menginginkan cara agar pengguna kami memilih satu bulan penuh sebagai rentang tanggal yang dipilih; misal Mei 2020, April 2020, dll.

Kami berpikir bahwa RangeDateSelector pra-bangun yang direferensikan di atas membawa kami sebagian besar perjalanan ke sana. Komponen memungkinkan pengguna untuk memilih rentang tanggal dan menerapkan batas [bawah, atas] .

Satu-satunya hal yang hilang adalah cara untuk menerapkan pilihan untuk memilih otomatis sepanjang bulan. Perilaku default RangeDateSelector membuat pengguna memilih tanggal mulai dan tanggal akhir.

Kami menginginkan perilaku sehingga ketika pengguna memilih hari dalam bulan tersebut, pemilih kemudian akan secara otomatis memilih seluruh bulan sebagai rentang tanggal.

Solusi yang kami putuskan adalah memperluas RangeDateSelector dan kemudian mengganti perilaku pemilihan hari untuk memilih otomatis seluruh bulan sebagai gantinya.

Untungnya, ada fungsi yang dapat kita timpa dari antarmuka DateSelector yang disebut: select(selection: Long) .

Fungsi ini akan dipanggil saat pengguna memilih hari di pemilih, dengan hari yang dipilih berlalu dalam milidetik UTC dari zaman.

Menerapkan perilaku pemilihan tanggal bulanan

Implementasinya ternyata menjadi bagian yang paling sederhana, karena kami memiliki fungsi yang jelas yang dapat kami timpa untuk mendapatkan perilaku yang kami inginkan.

Logika dasarnya akan seperti ini:

  1. Pengguna memilih hari.
  2. Fungsi select() dipanggil dengan hari yang dipilih dalam milidetik UTC Panjang dari zaman.
  3. Temukan hari pertama dan terakhir bulan itu dari hari yang diberikan kepada kami.
  4. Lakukan panggilan ke super.select(1st of month) & super.select(last day of month)
  5. Perilaku induk dari RangeDateSelector harus berfungsi seperti yang diharapkan, dan pilih bulan sebagai rentang tanggal.

Menyatukan semuanya

Sekarang setelah kami memiliki MonthRangeDateSelector Kustom kami, kami dapat mengatur MaterialDatePicker kami.

Untuk mengambil contoh lebih lanjut, kita dapat memproses hasil seleksi seperti ini:

Hasilnya akan terlihat seperti ini:

Gotcha

Hanya ada satu masalah utama yang membuat sulit untuk sampai pada solusi ini.

Komponen utama yang digunakan untuk membangun MonthRangeDateSelector kami adalah kelas RangeDateSelector dan antarmuka DateSelector . Versi pustaka yang digunakan dalam artikel ini (1.2.0-beta01) membatasi visibilitas kedua file ini, untuk mencegah perluasan atau penerapannya.

Akibatnya, meskipun kami berhasil mengkompilasi MonthRangeDateSelector baru kami, kompiler memang menunjukkan peringatan yang sangat menakutkan untuk mencegah kami melakukannya:

Salah satu cara untuk menyembunyikan peringatan kompiler ini adalah dengan menambahkan @Suppress("RestrictedApi") seperti:

Pengalaman ini mengilustrasikan bagaimana, meskipun Pustaka Komponen Material telah menyediakan beberapa komponen baru yang hebat untuk Komunitas Pengembang Android, ini masih dalam proses.

Bagian terbaik dari perpustakaan ini adalah keterbukaan terhadap masukan dari Komunitas Android! Setelah menemukan batasan visibilitas komponen ini, saya membuka masalah di Proyek Github, dan bahkan membuka PR untuk segera mengatasinya.

Umpan balik terbuka antara Tim Komponen Material dan Komunitas Android menghasilkan kolaborasi dan hasil yang luar biasa untuk semua orang.

Kesimpulan

MaterialDatePicker baru memiliki beberapa fungsionalitas luar biasa yang kemungkinan akan mencakup sebagian besar kasus penggunaan pemilihan tanggal.

Namun, bagian terbaiknya dari sesuatu seperti AppCompat CalendarView adalah ia dibangun dengan cara yang dapat dikomposisi. Oleh karena itu, dapat dengan mudah diperluas dan dimodifikasi untuk kasus penggunaan tertentu, sedangkan akan jauh lebih sulit untuk mencapai hal-hal seperti itu di CalendarView .

Terima kasih khusus

Saya ingin menyoroti beberapa orang yang membantu peer-review artikel ini:

  • Nick Rout (Github)
  • Mike Wolfson (Github)
  • Ryan Phillips (LinkedIn)
  • Lucas Moellers (Github)
  • Mit Patel (LinkedIn)