Inżynieria w Sprout: Tworzenie selektora miesiąca dla Androida

Opublikowany: 2020-06-26

Uwaga: ten artykuł jest oparty na Material Components w wersji 1.2.0-beta01 z dnia 1 czerwca 2020 r . .

W ciągu trzech i pół roku pracy w małym zespole ds. Androida w Sprout Social jedną z głównych rzeczy, które motywują mnie do codziennego przychodzenia do pracy, jest wolność i zaufanie naszej firmy do rozwiązywania problemów w sposób, który uznamy za najlepszy.

Swoboda badania i odkrywania wielu różnych rozwiązań problemu, który uważamy za niezbędny, przy jednoczesnym uwzględnieniu ram czasowych na dostarczenie aktualizacji produktów, pozwala nam znaleźć najlepsze rozwiązanie zarówno dla naszych klientów, jak i dla naszego oprogramowania.

Jednym z takich wyzwań było zbudowanie komponentu interfejsu użytkownika dla naszej nowej funkcji raportowania mobilnego. Ten nowy składnik był selektorem miesiąca, który umożliwiał naszym użytkownikom określenie zakresu dat dla raportu analitycznego.

Wybranym przez nas miejscem początkowym była istniejąca Biblioteka komponentów materiałowych. Zamiast zaczynać od zera, ta biblioteka jest aktywnie utrzymywana i dopasowywana do specyfikacji materiałów. Mając tę ​​bibliotekę jako podstawę, prawdopodobnie moglibyśmy zmniejszyć ilość logiki, którą musielibyśmy sami napisać.

W tym artykule omówię, w jaki sposób podeszliśmy do tego procesu, kilka unikalnych czynników w tworzeniu aplikacji Sprout na Androida, kilka „gotchas”, które pojawiły się (i zostały naprawione) po drodze i co wiedzieć, jeśli jesteś pracuje nad podobnym projektem.

Wstęp

W wersji 1.1.0 Android Material Components wprowadzono nowy składnik interfejsu użytkownika Date Picker. Jednym z mile widzianych dodatków tego nowego MaterialDatePicker w AppCompat CalendarView jest możliwość wyboru zakresu dat przy użyciu widoku kalendarza lub pola wprowadzania tekstu.

Stary AppCompat CalendarView nie był zbyt elastyczny. Był to dobry komponent w przypadku ograniczonego użycia, który miał rozwiązać; oznacza to wybranie pojedynczej daty oraz opcjonalnych dat minimalnych i maksymalnych w celu określenia dozwolonego zakresu dat.

Nowy MaterialDatePicker został zbudowany z większą elastycznością, aby umożliwić korzystanie z rozszerzonej funkcjonalności zachowania. Działa poprzez szereg interfejsów, które można zaimplementować, aby dostosować i zmodyfikować zachowanie selektora.

Ta modyfikacja zachowania jest wykonywana w czasie wykonywania za pomocą zestawu funkcji wzorca konstruktora w klasie MaterialDatePicker.Builder .

Oznacza to, że jesteśmy w stanie rozszerzyć podstawowe zachowanie tego MaterialDatePicker o komponowalne składniki interfejsu.

Uwaga: Chociaż istnieje wiele różnych komponentów, z których korzysta MaterialDatePicker , w tym artykule omówimy tylko komponent wyboru daty.

Selektor zakresu dat

Zespół Sprout Social Android był w trakcie tworzenia naszej sekcji raportów analitycznych.

Ta nowa sekcja pozwoli naszym użytkownikom wybrać zestaw filtrów i zestaw zakresów dat, które obejmie raport.

MaterialDatePicker zawierał kilka gotowych składników, które mogliśmy wykorzystać do realizacji naszego przypadku użycia.

W naszym najczęstszym przypadku, pozwalającym użytkownikowi na wybranie zakresu dat, wystarczyłby wstępnie skompilowany MaterialDatePicker :

Dzięki temu blokowi kodu otrzymujemy selektor dat, który pozwala użytkownikom wybrać zakres dat.

Selektor miesięcznych dat

Jednym z raportów Sprout Social, który ma bardziej unikalny wybór dat, jest raport o trendach na Twitterze.

Ten raport różni się od innych tym, że zamiast zezwalać na dowolny zakres dat, wymusza wybór jednego miesiąca, co oznacza, że ​​użytkownik może wybrać tylko marzec 2020 r. w porównaniu z 3 marca do 16 marca 2020 r.

Nasza aplikacja internetowa obsługuje to za pomocą rozwijanego pola formularza:

MaterialDatePicker nie ma możliwości wymuszenia takiego ograniczenia za pomocą wstępnie skompilowanego selektora zakresu dat materiału omówionego w poprzedniej sekcji. Na szczęście MaterialDatePicker został zbudowany z części, które można komponować, co pozwala nam rozszerzyć domyślne zachowanie dla naszego konkretnego przypadku użycia.

Zachowanie wyboru daty

MaterialDatePicker wykorzystuje DateSelector jako interfejs używany do logiki wyboru selektora.

Z Javadoc:

„Interfejs dla użytkowników {@link MaterialCalendar<S>} do kontrolowania sposobu wyświetlania i zwracania zaznaczeń w Kalendarzu…”

Zauważysz, że MaterialDatePicker.Builder.dateRangePicker() zwraca wystąpienie konstruktora RangeDateSelector , którego użyliśmy w powyższym przykładzie.

Ta klasa jest wstępnie zbudowanym selektorem, który implementuje DateSelector .

Burza mózgów, zachowanie dotyczące miesięcznego wyboru daty

W naszym przypadku użycia chcieliśmy, aby nasi użytkownicy wybrali cały miesiąc jako wybrany zakres dat; np. maj 2020, kwiecień 2020 itd.

Pomyśleliśmy, że wstępnie skompilowany RangeDateSelector , o którym mowa powyżej, zapewnił nam większość drogi. Komponent pozwalał użytkownikowi wybrać zakres dat i wymusić [dolną, górną] granicę .

Jedyne, czego brakowało, to sposób na wymuszenie wyboru w celu automatycznego wyboru przez cały miesiąc. Domyślne zachowanie RangeDateSelector na tym, że użytkownik wybiera datę rozpoczęcia i datę zakończenia.

Chcieliśmy, aby po wybraniu przez użytkownika dnia w miesiącu selektor automatycznie wybierał cały miesiąc jako zakres dat.

Rozwiązaniem, na które zdecydowaliśmy się, było rozszerzenie RangeDateSelector , a następnie zastąpienie zachowania wyboru dnia, aby zamiast tego automatycznie wybrać cały miesiąc.

Na szczęście istnieje funkcja, którą możemy nadpisać z interfejsu DateSelector o nazwie: select(selection: Long) .

Ta funkcja zostanie wywołana, gdy użytkownik wybierze dzień w selektorze, a wybrany dzień minie w milisekundach UTC od epoki.

Wdrażanie zachowania comiesięcznego wyboru daty

Implementacja okazała się najprostszą częścią, ponieważ mamy wyraźną funkcję, którą możemy nadpisać, aby uzyskać pożądane zachowanie.

Podstawowa logika będzie taka:

  1. Użytkownik wybiera dzień.
  2. Funkcja select() jest wywoływana z wybranym dniem w milisekundach czasu UTC z epoki.
  3. Znajdź pierwszy i ostatni dzień miesiąca z podanego dnia, który nam minął.
  4. Zadzwoń do super.select(1st of month) i super.select(last day of month)
  5. Zachowanie nadrzędne z RangeDateSelector powinno działać zgodnie z oczekiwaniami i wybrać miesiąc jako zakres dat.

Kładąc wszystko razem

Teraz, gdy mamy już nasz Custom MonthRangeDateSelector , możemy skonfigurować nasz MaterialDatePicker .

Kontynuując przykład, możemy przetworzyć wynik selekcji w następujący sposób:

Wynik będzie wyglądał tak:

Gotchas

Był tylko jeden poważny problem, który utrudniał znalezienie tego rozwiązania.

Podstawowymi komponentami użytymi do zbudowania naszego MonthRangeDateSelector były klasa RangeDateSelector i interfejs DateSelector . Wersja biblioteki użyta w tym artykule (1.2.0-beta01) ograniczyła widoczność tych dwóch plików, aby zniechęcić do ich rozszerzania lub wdrażania.

W rezultacie, chociaż mogliśmy z powodzeniem skompilować nasz nowy MonthRangeDateSelector , kompilator pokazał bardzo przerażające ostrzeżenie, aby zniechęcić nas do tego:

Jednym ze sposobów ukrycia tego ostrzeżenia kompilatora jest dodanie @Suppress("RestrictedApi") w następujący sposób:

To doświadczenie ilustruje, w jaki sposób, mimo że Biblioteka komponentów materiałowych dostarczyła społeczności Android Developer Community kilka nowych, świetnych komponentów, nadal trwają prace nad nią.

Wielką częścią tej biblioteki jest otwartość na opinie społeczności Androida! Po odkryciu tego ograniczenia widoczności komponentu otworzyłem problem w projekcie Github, a nawet otworzyłem PR, aby od razu go rozwiązać.

Ta otwarta pętla informacji zwrotnych między zespołem ds. komponentów materiałowych a społecznością Androida zapewnia wszystkim doskonałą współpracę i wyniki.

Wniosek

Nowy MaterialDatePicker ma świetną funkcjonalność po wyjęciu z pudełka, która prawdopodobnie obejmie większość przypadków użycia wyboru daty.

Jednak najlepszą częścią tego nad czymś takim jak AppCompat CalendarView jest to, że jest on zbudowany w sposób umożliwiający komponowanie. W związku z tym można go łatwo rozszerzyć i zmodyfikować dla określonych przypadków użycia, podczas gdy wykonanie takich rzeczy w CalendarView byłoby znacznie trudniejsze.

Szczególne podziękowania

Chciałbym zwrócić uwagę na osoby, które pomogły w recenzowaniu tego artykułu:

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