Илюстрация от Вирджиния Полтрак

Анимация по график

Анимации в приложението Google I / O

Наскоро бях част от страхотен екип, работещ върху приложението Google I / O 2018 за Android. Това е приложение за придружител на конференции, което позволява на участниците и отдалечените хора да намерят сесии, да съставят персонализиран график и да резервират места на мястото (ако имате достатъчно късмет да сте там!). Вградихме редица интересни анимирани функции в приложението, които вярвам, че значително подобриха изживяването. Кодът за това приложение току-що е отворен и исках да подчертая няколко от тези случаи и някои интересни подробности за изпълнението.

Някои анимирани елементи в I / O приложението

Като цяло има 3 вида анимации, които използвахме в приложението:

  1. Герои анимации - използват се за засилване на брандирането и донасят моменти на наслада
  2. Преходи на екрана
  3. Промени в държавата

Бих искал да се запозная подробно с някои от тях.

обратно броене

Част от ролята на приложението е да изгради вълнение и очакване за конференцията. Като такава тази година включихме голямо анимирано отброяване към старта на конференцията, показано както на бордовия екран, така и в раздела с информация. Това също беше чудесна възможност да вградите марката на събитието в приложението, носейки много характер.

Отброяването до конференцията започва

Тази анимация е създадена от дизайнер на движения и е предадена като поредица от файлове на Lottie json: всеки 1 секунда показва число, анимиращо „in“ и „out“. Форматът на Lottie улесни пускането на файловете в активи и дори предлагаше удобни методи като setMinAndMaxProgress, които ни позволиха да играем само първата или последната половина на анимация (за да покажем число, анимиращо в или извън).

Интересната част всъщност беше оркестрирането на тези множество анимации в цялостното отброяване. За целта създадохме персонализиран CountdownView, който е доста сложен ConstraintLayout, съдържащ множество LottieAnimationViews. В това създадохме делегат на Котлин, за да капсулираме, като стартираме подходящата анимация. Това ни позволи просто да присвоим Int на всеки делегат на цифрата, която трябва да се покаже и делегатът да настрои и стартира анимацията (ите). Удължихме делегата на ObservableProperty, което гарантира, че изпълняваме анимация само когато цифрата се промени. След това нашият анимационен цикъл просто публикува изпълняемо всяка секунда (когато изгледът е прикачен), който изчислява коя цифра всеки изглед трябва да показва и актуализира делегатите.

резервация

Едно от ключовите действия на приложението е да позволи на участниците да резервират места. Като такова показахме това действие на видно място в FAB на екрана с подробности за сесията. Смятахме, че е важно да съобщим само, че сесията е запазена, след като е била успешно завършена в задния ред (за разлика от не толкова важните действия, като участието в сесия, при което оптимистично актуализираме потребителския интерфейс веднага). Това може да отнеме малко време, докато чакаме отговор от бекенда, за да направим тази по-отзивчива използвахме анимирана икона, за да предоставим обратна връзка, че работим върху нея и плавно да преминем в новото състояние.

Обратна връзка, докато резервирате място на сесия

Това се усложнява от факта, че имаше няколко състояния, които тази икона трябваше да отразява: сесията може да бъде запазена, може вече да са запазили място, ако сесията е пълна, тогава може да има списък с чакащи или те да са в списък с чакащи или близо до сесията, започване на резервации се деактивира. Това доведе до много пермутации на различни състояния, за да оживяваме между тях. За да опростим тези преходи, решихме винаги да преминаваме през „работещо“ състояние; анимираните часовници по-горе. Следователно всеки преход всъщност е двойка: състояние 1 → работа и работа → състояние 2. Това значително опрости нещата. Ние изградихме всяка от тези анимации с помощта на shapehifter; вижте avd_state_to_state файлове тук.

За да покажем това, използвахме персонализиран изглед и AnimatedStateListDravable (ASLD). Ако не сте използвали ASLD преди, това е (както подсказва името му) анимирана версия на StateListDravable, която вероятно сте срещнали - която ви позволява не само да предоставяте различни чертежи за състояние, но и преходи между състояния (под формата на AnimatedVectorDravable или АнимацияDravable). Ето чертожните дефиниращи статичните изображения и преходите в и извън работното състояние на иконата за резервация.

Създадохме персонализиран изглед, за да поддържаме собствените си персонализирани състояния. Изгледите предлагат някои стандартни състояния като натиснати или избрани. По подобен начин можете да определите своя собствена и да имате маршрута View, който да показва всички Drawables, който показва. Дефинирахме нашите собствени state_reservable, state_reserved и т.н. След това създадохме enum от тези различни състояния, капсулирайки състоянието на изгледа плюс всички свързани атрибути, като свързано описание на съдържанието. След това нашата бизнес логика може просто да зададе подходящата стойност от този изглед на изгледа (чрез обвързване на данни), който да актуализира състоянието на чертежа, което стартира анимация чрез ASLD. Комбинацията от персонализирани състояния и AnimatedStateListDravable беше чист начин за реализиране на това, запазвайки множеството състояния в декларативните слоеве, което води до минимален код на изглед.

Преход на високоговорителите

Много от преходите на екрана работеха добре със стандартните анимации на прозореца. Едно място, от което се отклонихме, е преходът в екрана с подробности за високоговорителя. Това показва изображението на високоговорителите от двете страни на прехода и е перфектен кандидат за преход на споделен елемент. Това помага за облекчаване на промяната на контекста между екраните.

Преход на споделен елемент

Това е доста стандартен преход на споделени елементи, като се използват платформите ChangeBounds и ArcMotion класове на ImageView.

По-интересното е как започването на този преход се вписва в модела на събитието, който използвахме за навигация. По същество този модел отделя входните събития (като записване на говорител) от навигационни събития, поставяйки ViewModel отговорен за това как да отговори на въвеждането. В този случай това разединяване означава, че ViewModel изложи LiveData на събитията, които знаеха само идентификационния номер на оратора, за да отиде. Инициирането на преход на споделен елемент изисква споделения изглед, който не сме имали към този момент. Решихме това, като запазихме идентификационния номер на говорителя като маркер в изгледа, когато той е свързан, така че по-късно изгледът да бъде извлечен, когато трябва да преминем към конкретен екран с подробности за високоговорителя.

Филтри

Основната част от приложението за конференция е филтрирането на много събития до тези, които ви интересуват. Всяка тема имаше цвят, свързан с нея за лесно разпознаване и получихме страхотен дизайн за персонализиран „чип“, който да използваме при избора на филтри:

Анимирани чипове за филтри

Разгледахме Chip от Material Components, но избрахме да приложим собствения си персонализиран изглед за по-голям контрол върху дисплея и анимацията между „проверените“ състояния. Това се реализира с помощта на рисунка на платно и StaticLayout за показване на текст. Изгледът има едно свойство за прогрес [0–1] моделиране непроверено - проверено. За да превключим състоянието, ние просто анимираме тази стойност и обезсилваме изгледа и кода за изобразяване линейно интерполира позиции и размери на елементи въз основа на това.

Първоначално при реализирането на това направих изгледът да приложи интерфейса на Checkable и започнах анимацията, когато методът setChecked постави ново състояние. Докато показваме множество филтри в RecyclerView, това имаше злополучния ефект от стартирането на анимацията, ако избран филтър се превърта и изгледът се възстанови до неизбран филтър, превъртащ се в. Затова добавихме отделен метод за стартиране на анимацията, който ни позволява да разграничаваме превключването й с едно кликване и незабавно актуализиране при обвързване на нови данни към изгледа.

Освен това, когато въведохме тази превключвателна анимация, установихме, че тя джанка, т.е. пуска рамки. Виновният ли беше моят анимационен код? Тези филтри се показват в BottomSheet пред главния екран на график на конференцията. Когато филтърът е включен, инициираме логиката на филтриране, която да се прилага към графика (и актуализираме броя на съвпадащите събития в заглавието на филтърния лист). По-късно разбрахме, че решихме, че проблемът е, че когато се прилагат филтрите, ViewPager на RecyclerViews, показващ графика, послушно се оттегли и се актуализира до току-що доставените данни. Това предизвика редица изгледи да бъдат надути и обвързани. Цялата тази работа разпенваше нашия рамков бюджет ... но графикът за актуализиране не се виждаше, тъй като беше зад филтърния лист. Взехме решение да забавим изпълнението на действителното филтриране, докато анимацията не стартира, изтъргувахме още малко сложност на внедряване за по-плавно потребителско изживяване. Първоначално реализирах това с помощта на postDelayed, но това предизвика проблеми за тестовете на потребителския интерфейс. Вместо това сменихме нашия метод, който стартира анимацията, за да приеме ламбда, която да се изпълнява накрая. Това ни позволи да уважаваме по-добре настройките на анимацията на потребителя и да тестваме правилно изпълнението.

Анимирайте се

Като цяло имам чувството, че анимациите наистина допринесоха за изживяването, характера, брандирането и отзивчивостта на приложението. Надяваме се, че тази публикация ви е помогнала да обясните защо и как са били използвани и ви даде указания за тяхното изпълнение.