Inginerie la Sprout: construirea unui selector de luni Android

Publicat: 2020-06-26

Notă: acest articol s-a bazat pe versiunea 1.2.0-beta01 a componentelor materiale din 1 iunie 2020 .

În cei trei ani și jumătate în care am lucrat într-o echipă mică de Android la Sprout Social, unul dintre principalele lucruri care mă motivează să lucrez în fiecare zi este libertatea și încrederea din partea companiei noastre de a aborda o problemă în orice mod considerăm cel mai bun.

Libertatea de a cerceta și a explora multe soluții diferite la o problemă pe care o considerăm necesară, în timp ce luăm în considerare un interval de timp pentru a livra actualizările produselor, ne permite să găsim cea mai bună soluție atât pentru clienții noștri, cât și pentru software-ul nostru.

O astfel de provocare a implicat construirea unei componente UI pentru noua noastră funcție de raportare pe mobil. Această nouă componentă a fost un selector de luni, ceea ce a permis utilizatorilor noștri să stabilească un interval de date pentru un raport de analiză.

Locul de pornire pe care l-am ales a fost biblioteca existentă de componente materiale. În loc să pornească de la zero, această bibliotecă este întreținută în mod activ și se aliniază cu specificațiile Materialului. Cu această bibliotecă ca fundație, probabil că am putea reduce cantitatea de logică pe care ar trebui să o scriem noi înșine.

În acest articol, voi aborda modul în care am abordat acest proces, câțiva factori unici în construirea aplicației Sprout pentru Android, câteva „probleme” care au apărut (și au fost remediate) pe parcurs și ce să știți dacă sunteți lucrând la un proiect similar.

Introducere

Versiunea Android Material Components 1.1.0 a introdus o nouă componentă a interfeței de utilizare a selectorului de date. Una dintre adăugările binevenite ale acestui nou MaterialDatePicker peste AppCompat CalendarView este capacitatea de a selecta o serie de date folosind fie o vizualizare calendar, fie un câmp de introducere a textului.

Vechiul AppCompat CalendarView nu era foarte flexibil. A fost o componentă bună pentru cazul limitat de utilizare pe care trebuia să-l rezolve; adică selectarea unei singure date și a datelor minime și maxime opționale pentru a specifica un interval de date permis.

Noul MaterialDatePicker a fost construit cu mai multă flexibilitate pentru a permite utilizarea funcționalității extinse de comportament. Funcționează printr-o serie de interfețe pe care le-ar putea implementa pentru a ajusta și modifica comportamentul selectorului.

Această modificare a comportamentului se face în timpul execuției printr-un set de funcții de model de constructor din clasa MaterialDatePicker.Builder .

Aceasta înseamnă că putem extinde comportamentul de bază al acestui MaterialDatePicker prin componente de interfață componabile.

Notă: Deși există o serie de componente diferite pe care MaterialDatePicker le utilizează, în acest articol vom acoperi doar Componenta de selecție a datei.

Selector de interval de date

Echipa Sprout Social Android era în proces de creare a secțiunii noastre de rapoarte Analytics.

Această nouă secțiune ar permite utilizatorilor noștri să selecteze un set de filtre și un set de intervale de date pe care le-ar acoperi raportul.

MaterialDatePicker a venit cu câteva componente prefabricate pe care le-am putea folosi pentru a realiza cazul nostru de utilizare.

Pentru cazul nostru cel mai obișnuit, permițând unui utilizator să selecteze o serie de date, MaterialDatePicker predefinit ar fi suficient:

Cu acest bloc de cod, obținem un selector de date care permite utilizatorilor să selecteze un interval de date.

Selector lunar de date

Unul dintre rapoartele Sprout Social care are o selecție de date mai unică este Twitter Trends Report.

Acest raport diferă de celelalte prin faptul că, în loc să permită orice fel de interval de date, impune o selecție de o singură lună, ceea ce înseamnă că un utilizator poate selecta doar martie 2020 față de 3 martie - 16 martie 2020.

Aplicația noastră web se ocupă de acest lucru utilizând un câmp de formular derulant:

MaterialDatePicker nu are o modalitate de a impune o astfel de restricție cu Selectorul de interval de date pre-construit, discutat în secțiunea anterioară. Din fericire, MaterialDatePicker a fost construit cu părți componabile care ne permit să extindem comportamentul implicit pentru cazul nostru de utilizare particular.

Comportamentul selectării datei

MaterialDatePicker folosește un DateSelector ca interfață utilizată pentru logica de selecție a selectorului.

Din Javadoc:

„Interfață pentru utilizatorii {@link MaterialCalendar<S>} pentru a controla modul în care Calendarul afișează și returnează selecțiile…”

Veți observa că MaterialDatePicker.Builder.dateRangePicker() returnează o instanță de constructor a RangeDateSelector , pe care am folosit-o în exemplul de mai sus.

Această clasă este un selector pre-construit care implementează DateSelector .

Brainstorming un comportament lunar de selecție a datei

Pentru cazul nostru de utilizare, am dorit o modalitate prin care utilizatorii noștri selectează o lună întreagă ca interval de date selectat; de exemplu mai 2020, aprilie 2020 etc.

Ne-am gândit că RangeDateSelector pre-construit la care se face referire mai sus ne-a adus cel mai mult până acolo. Componenta permitea unui utilizator să selecteze un interval de date și să impună o limită [inferioară, superioară] .

Singurul lucru care lipsea a fost o modalitate de a impune o selecție pentru a selecta automat întreaga lună. Comportamentul implicit al RangeDateSelector face ca utilizatorul să selecteze o dată de început și o dată de încheiere.

Ne-am dorit un comportament astfel încât, atunci când un utilizator selectează o zi din lună, selectorul va selecta apoi întreaga lună ca interval de date.

Soluția pentru care am decis a fost să extindem RangeDateSelector și apoi să suprascriem comportamentul de selecție a zilei pentru a selecta automat întreaga lună.

Din fericire, există o funcție pe care o putem suprascrie din interfața DateSelector numită: select(selection: Long) .

Această funcție va fi invocată atunci când un utilizator selectează o zi în selector, ziua selectată fiind trecută în milisecunde UTC din epocă.

Implementarea unui comportament lunar de selecție a datei

Implementarea sa dovedit a fi cea mai simplă parte, deoarece avem o funcție clară pe care o putem suprascrie pentru a obține comportamentul dorit.

Logica de bază va fi aceasta:

  1. Utilizatorul selectează o zi.
  2. Funcția select() este invocată cu ziua selectată într-o milisecunde UTC lungă din epocă.
  3. Găsiți prima și ultima zi a lunii din ziua dată transmisă nouă.
  4. Efectuați un apel la super.select(1st of month) și super.select(last day of month)
  5. Comportamentul părinte de la RangeDateSelector ar trebui să funcționeze conform așteptărilor și să selecteze luna ca interval de date.

Punând totul împreună

Acum că avem personalizat MonthRangeDateSelector , putem configura MaterialDatePicker .

Pentru a continua exemplul, putem procesa rezultatul selecției astfel:

Rezultatul va arăta astfel:

Am înțeles

A existat o singură problemă majoră care a făcut dificilă ajungerea la această soluție.

Componentele principale folosite pentru a construi MonthRangeDateSelector au fost clasa RangeDateSelector și interfața DateSelector . Versiunea bibliotecii folosită în acest articol (1.2.0-beta01) a restricționat vizibilitatea acestor două fișiere, pentru a descuraja extinderea sau implementarea acestora.

Drept urmare, deși am putut compila cu succes noul nostru MonthRangeDateSelector , compilatorul a afișat un avertisment foarte înfricoșător pentru a ne descuraja să facem acest lucru:

O modalitate de a ascunde acest avertisment al compilatorului este să adăugați un @Suppress("RestrictedApi") astfel:

Această experiență ilustrează modul în care, deși Biblioteca de componente materiale a oferit câteva componente noi extraordinare comunității de dezvoltatori Android, este încă o lucrare în curs.

O mare parte a acestei biblioteci este deschiderea către feedback din partea comunității Android! După ce am descoperit această restricție de vizibilitate a componentelor, am deschis o problemă în proiectul Github și chiar am deschis un PR pentru a o rezolva imediat.

Această buclă de feedback deschisă între echipa Material Components și comunitatea Android generează o colaborare excelentă și rezultate pentru toată lumea.

Concluzie

Noul MaterialDatePicker are o funcționalitate excelentă, care va acoperi probabil majoritatea cazurilor de utilizare ale selecției datei.

Cu toate acestea, cea mai bună parte a acestuia față de ceva de genul AppCompat CalendarView este că este construit într-un mod composabil. Prin urmare, poate fi extins și modificat cu ușurință pentru cazuri de utilizare specifice, în timp ce ar fi mult mai greu să realizați astfel de lucruri în CalendarView .

Multumiri speciale

Aș dori să evidențiez câțiva oameni care au ajutat la revizuirea de către colegi a acestui articol:

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