Рубрика развивается при поддержке
Разработка
BytePace
275

Анимация в Android: переходы, ч. 1

Анимации в приложениях появились уже достаточно давно, и их красота, а вместе с ней и сложность, растут практически с каждым днём. Исходя из того, что анимация в привычном для человека понимании - это набор кадров, меняющихся с определенной задержкой, мы решили начать изучение анимаций в Android с самых азов.

В закладки

Автор статьи: Андрей Ващенко, разработчик BytePace.

Анимация с помощью Transition представляет собой изменение свойств объекта (View), исходя из местоположения объекта на экране и примененного эффекта. С момента появления Transition в Android (с выхода версии 4.4 KitKat), появилось понятие сцены (Scene), и переход между двумя сценами это и есть Transition. Грубо говоря, чтобы сделать анимацию, нужна начальная сцена и конечная сцена. Например, чтобы сделать анимированное увеличение какого-либо объекта, можно сделать 1 лэйаут, затем скопировать его, и у копии сделать размер элемента больше. В итоге можем получить что-то подобное:

Таких анимаций можно сделать сколько угодно и разной сложности, всё зависит от фантазии разработчика (ну или дизайнера). В моем конкретном случае я использовал механизм Transition для плавного перехода между экранами. Исходя из этого, сценами будут являться уже лэйауты двух активити (или фрагментов), между которыми будет осуществляться переход.

Рис 1. Наглядное представление работы Transition для перехода между экранами​

Практическое применение

Для начала нужно включить отображение Transitions, либо в стилях:

либо в самой активити:

Далее необходимо задать эффекты (типы Transition). Опишу самые распространенные:

- ChangeBounds: отвечает за изменение координат и размеров элементов внутри лэйаута;

- Fade: плавное появление (Fade.IN) или исчезновение (Fade.OUT) элемента;

- Slide: позволяет элементу "выезжать" (или "прибегать") из-за выбранного края;

- ChangeImageTransform: анимирует матричный переход изображения внутри ImageView. C помощью него можно плавно менять размер изображения и scaleType;

- ChangeClipBounds: анимирует изменение параметра clipBounds у элементов;

-AutoTransition: Set of effects changeBouns, Fade.IN, Fade.OUT. It's used by default.

Кроме этого всегда можно написать свой собственный Transition, который не будет иметь ничего общего с вышеперечисленными.

Я решил повторить эффект с рисунка 2 (ниже). В моем случае понадобились эффекты changeBounds и changeImageTransform.

Далее верстаем два лэйаута (layout). У view, которые будут подвергнуты анимации, необходимо задать параметр transitionName, он задается на обоих лэйаутах одинаковый, тем самым обозначаются начальная и конечная сцены.

После этого в следующее активити (activity) нужно передать какие-то данные, которые будут отображаться, а также параметры для воспроизведения анимации - ActivityOptions.

iv1.setOnClickListener { startActivity(Intent(this, ImageActivity::class.java).putExtra("photo", R.drawable.honeycomb), ActivityOptions.makeSceneTransitionAnimation(this, iv1, "photo").toBundle()) }

Как можно увидеть из примера кода, в качестве параметров принимается transitionName и view.

Совсем забыл про эффекты. В ресурсах нужно добавить директорию transition, там создать transitionSet и прописать эффекты, которые вам нужны.

Далее возвращаемся в стили и прописываем transitionSet для анимации входа в активити и выхода из активити.

Конечно же, можно использовать разные наборы, я использовал одинаковые.

Самое время посмотреть, для чего всё это делалось.

Выглядит неплохо, при том, что мы вообще не думали про жизненный цикл активити. Пойдем дальше: провернем такой же эффект, только уже с холдером recyclerView. Всё абсолютно так же, как и в предыдущем случае: задаем одинаковый transitionName у элементов холдера и у элементов целевого активити (target activity / result activity).

Напишем простенький адаптер с колбэком (callback), который по клику на холдер (holder) будет возвращать какие-то данные для следующего активити, а также вью, которые участвуют в анимации. Так как в этом случае я задал 2 view для анимации, воспользуемся перегрузкой функции makeSceneTransitionAnimation, которая принимает в себя переменный массив элементов Pair<View, String>, соответственно, каждый такой элемент передает view и привязанный к нему TransitionName.

Запускаем…

Очень даже хорошо, а главное быстро.Для написания этой статьи я использовал информацию с поста на Habr: https://habr.com/ru/post/243363/, оттуда же примеры анимаций в начале статьи.

Также изучил официальную документацию на портале разработчиков Google: https://developer.android.com/training/transitions/start-activity.

Спасибо за внимание!

Больше статей в нашем блоге:

Материал опубликован пользователем.
Нажмите кнопку «Написать», чтобы поделиться мнением или рассказать о своём проекте.

Написать
{ "author_name": "BytePace", "author_type": "self", "tags": [], "comments": 0, "likes": -1, "favorites": 2, "is_advertisement": false, "subsite_label": "dev", "id": 82736, "is_wide": true, "is_ugc": true, "date": "Thu, 12 Sep 2019 11:42:15 +0300", "is_special": false }
Облачная платформа
Основа для цифровизации бизнеса
0
{ "id": 82736, "author_id": 331975, "diff_limit": 1000, "urls": {"diff":"\/comments\/82736\/get","add":"\/comments\/82736\/add","edit":"\/comments\/edit","remove":"\/admin\/comments\/remove","pin":"\/admin\/comments\/pin","get4edit":"\/comments\/get4edit","complain":"\/comments\/complain","load_more":"\/comments\/loading\/82736"}, "attach_limit": 2, "max_comment_text_length": 5000, "subsite_id": 235819, "last_count_and_date": null }
Комментариев нет
Популярные
По порядку
{ "page_type": "article" }

Прямой эфир

[ { "id": 1, "label": "100%×150_Branding_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox_method": "createAdaptive", "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "ezfl" } } }, { "id": 2, "label": "1200х400", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "ezfn" } } }, { "id": 3, "label": "240х200 _ТГБ_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fizc" } } }, { "id": 4, "label": "Article Branding", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "p1": "cfovx", "p2": "glug" } } }, { "id": 5, "label": "300x500_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "ezfk" } } }, { "id": 6, "label": "1180х250_Interpool_баннер над комментариями_Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "bugf", "p2": "ffyh" } } }, { "id": 7, "label": "Article Footer 100%_desktop_mobile", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fjxb" } } }, { "id": 8, "label": "Fullscreen Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fjoh" } } }, { "id": 9, "label": "Fullscreen Mobile", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fjog" } } }, { "id": 10, "disable": true, "label": "Native Partner Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyb" } } }, { "id": 11, "disable": true, "label": "Native Partner Mobile", "provider": "adfox", "adaptive": [ "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyc" } } }, { "id": 12, "label": "Кнопка в шапке", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "p1": "bscsh", "p2": "fdhx" } } }, { "id": 13, "label": "DM InPage Video PartnerCode", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox_method": "createAdaptive", "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "bugf", "p2": "flvn" } } }, { "id": 14, "label": "Yandex context video banner", "provider": "yandex", "yandex": { "block_id": "VI-223676-0", "render_to": "inpage_VI-223676-0-1104503429", "adfox_url": "//ads.adfox.ru/228129/getCode?pp=h&ps=bugf&p2=fpjw&puid1=&puid2=&puid3=&puid4=&puid8=&puid9=&puid10=&puid21=&puid22=&puid31=&puid32=&puid33=&fmt=1&dl={REFERER}&pr=" } }, { "id": 15, "label": "Баннер в ленте на главной", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "p1": "byudx", "p2": "ftjf" } } }, { "id": 16, "label": "Кнопка в шапке мобайл", "provider": "adfox", "adaptive": [ "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "p1": "byzqf", "p2": "ftwx" } } }, { "id": 17, "label": "Stratum Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fzvb" } } }, { "id": 18, "label": "Stratum Mobile", "provider": "adfox", "adaptive": [ "tablet", "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fzvc" } } }, { "id": 19, "disable": true, "label": "Тизер на главной", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "p1": "cbltd", "p2": "gazs" } } } ] { "page_type": "default" }