Shared Element Transition в Android: що ламається без нього
Користувач тапає по картці товару — і замість того, щоб картинка плавно перелетіла на своє місце в детальному екрані, застосунок на мить показує білий прямокутник, а потім різко перемальовує весь layout.
Список і деталь живуть як два незв'язані світи: користувач втрачає контекст, йому доводиться заново шукати очима той самий елемент, а відчуття «важкого» застосунку складається з десятків таких мікророзривів.
Shared Element Transition в Android вирішує саме це: один і той самий візуальний елемент безперервно переходить з одного екрана в інший, і навігація між екранами читається як єдиний рух, а не як підміна картинки.
Без спільних переходів страждає не лише естетика. Кожен різкий swap екрана — це перебудова дерева представлень і повне перемальовування, через що на середніх пристроях помітні пропуски кадрів при поверненні назад.
Користувач не розуміє, звідки він прийшов і куди повернеться: кнопка «назад» відчувається як відкат у невідомість, а не як повернення до того самого місця списку.
Плавність інтерфейсу напряму впливає на сприйману швидкість — екран, який «перетікає», суб'єктивно здається швидшим, навіть якщо час відгуку той самий.
Окремий біль — розсинхрон між підходами.
У класичних Activity і Fragment переходи будуються на спільних елементах і парах переходів, у Fragment додатково потрібно узгоджувати життєвий цикл і відкладені переходи, а в Compose модель інша — спільні елементи прив'язуються до ключів усередині навігації.
Коли частина екранів на одному механізмі, а частина на іншому, візуальна зв'язність рветься рівно на стиках: анімація є на одному переході й зникає на сусідньому, і користувач бачить недбалість, а не усвідомлений дизайн.
Розбираємо, як це закривається: як вибудувати єдину мову переходів поверх Activity, Fragment і Compose, щоб елемент не «стрибав» між екранами і навігація залишалася передбачуваною.
Що дає впровадження спільних переходів
Спільні переходи — це коли елемент (обкладинка товару, аватар, картка) не зникає на одному екрані й з'являється на іншому, а фізично перелітає між ними. Користувач бачить безперервний зв'язок між списком і деталлю, тому сприймає навігацію як один рух, а не як зміну кадрів.
На Android це реалізується через спільні імена елементів і єдиний контейнер переходу — у Fragment, Activity і Compose механіка різниться, але результат для користувача один.
Що змінюється в метриках інтерфейсу
Головний ефект — падіння частки перерваних переходів. Коли користувач тапає по картці й миттєво бачить, що саме відкрилося, він рідше натискає «назад» на півдорозі.
За нашою практикою на каталогах і стрічках частка повернень на попередній екран у перші секунди падає на 20-40%, а глибина перегляду зростає: людина проходить на 1-2 екрани далі, бо не втрачає контекст.
Швидкість сприйняття теж змінюється без прискорення самого завантаження. Навіть якщо дані деталі ще підвантажуються, спільний перехід уже показує, куди ми потрапили, — інтерфейс відчувається чутливим. Плавність анімації тут не прикраса, а спосіб приховати реальну затримку мережі.
Де ефект найпомітніший
- Каталоги та стрічки: перехід «плитка → картка товару» тримає увагу на об'єкті.
- Профілі та соцфункції: аватар і обкладинка перелітають у шапку, знижуючи відчуття розриву.
- Медіа та галереї: зображення розкривається на весь екран без білого спалаху між екранами.
Щоб це працювало стабільно, спільні імена задаються явно, а перехід обмежується за тривалістю та кривою — інакше на слабких пристроях анімація починає смикатися і ефект обертається проти користувача.
Підходи до реалізації: Activity, Fragment, Compose
Загальний перехід будується по-різному залежно від того, де живе екран. У класичних Activity пара «вихідний елемент → цільовий елемент» зв'язується через sharedElementEnterTransition і sharedElementReturnTransition, а збіг задається рядковим ключем transitionName.
У Fragment той самий механізм працює через FragmentTransaction.addSharedElement(), але додається умова: транзакція має бути відкладена до готовності розмітки, інакше цільовий вид ще не існує і перехід мовчки не спрацьовує.
У Compose окремого API для спільних переходів довго не було, і до появи штатного механізму команди збирали його вручну на AnimatedContent та узгоджених зміщеннях.
| Варіант | Коли підходить | Обмеження |
|---|---|---|
| Activity | Перехід між незалежними екранами, різними точками входу | Ключі transitionName унікальні в межах вікна; повернення вимагає явного налаштування sharedElementReturnTransition |
| Fragment | Екрани всередині однієї Activity, спільний контейнер і навігація | Потрібна відкладена транзакція і готовність розмітки; при перестворенні виду перехід втрачається |
| Compose | Єдиний UI-шар, навігація всередині одного дерева композиції | Вимагає узгодження ключів між екранами; частину анімацій доводиться збирати вручну |
Що визначає вибір
Вирішальний фактор — не «модність» підходу, а те, скільки разів за життя застосунку екран перестворюється і наскільки жорстко пов'язані вихідний і цільовий елементи.
Якщо екрани живуть у різних Activity і викликаються із зовнішніх точок входу, класичний механізм залишається найпередбачуванішим: він не залежить від дерева композиції і переживає перестворення вікна.
Якщо навігація вже зібрана на фрагментах, дешевше залишитися в ній — але доведеться дисципліновано відкладати транзакції і стежити за життєвим циклом виду.
Де найчастіше ламається
Основні збої — розсинхрон ключів і втрата стану при повороті екрана. Ключ, заданий в одному місці і не збіглий в іншому, дає не помилку, а просто відсутність анімації — це найдорожче в налагодженні.
У Compose додається ризик, що елемент перестворюється при рекомпозиції і анімація обривається на середині. Тому ми фіксуємо ключі в одному місці, перевіряємо поведінку при перестворенні і окремо проганяємо повернення — саме зворотний перехід найчастіше залишається недоробленим.
Як ми впроваджуємо Shared Element Transition в Android
Впровадження починаємо не з коду, а з аудиту навігації: інвентаризуємо екрани, позначаємо спільні візуальні елементи (обкладинки, аватари, картки товару) і фіксуємо, між якими екранами перехід дає реальний виграш, а де він надлишковий.
На цьому ж кроці перевіряємо, як влаштована навігація зараз — один Activity чи кілька, де живуть фрагменти, чи є Compose-екран у ланцюжку, — бо від цього залежить вибір механіки переходу.
- Аудит навігації та відбір сценаріїв. Складаємо карту переходів, позначаємо пари «джерело → приймач» зі спільним елементом і пріоритезуємо за частотою використання. Для кожного сценарію фіксуємо очікувану поведінку: що саме перелітає, що розчиняється, що залишається на місці.
- Проєктування переходів. Для кожної пари обираємо механіку: класичний перехід між Activity, перехід між фрагментами або спільний елемент у Compose. Визначаємо спільні імена елементів, щоб зв'язка знаходилася автоматично, і задаємо тривалість та криву руху.
- Налаштування переходів. Налаштовуємо зв'язування спільних елементів, відкладені переходи для екранів, які ще підвантажують контент, і поведінку при поверненні назад. Окремо опрацьовуємо випадок, коли цільовий екран відкривається зі списку або зі сповіщення — там джерело переходу інше.
- Сумісність і відкат. Перевіряємо поведінку на різних версіях системи та при вимкненій анімації в налаштуваннях доступності: перехід має не ламатися, а коректно згортатися у звичайний.
- Тестування анімації. Прогоняємо сценарії на слабких і топових пристроях, заміряємо пропущені кадри та час відгуку, дивимося, чи не смикається список при поверненні. Окремо перевіряємо поворот екрана та згортання застосунку в момент переходу.
- Передача документації. Віддаємо карту переходів, список спільних імен елементів, прийняті тривалості та криві, а також інструкцію, як додати новий перехід за нашим шаблоном.
Кожен етап закінчується артефактом, який можна перевірити: карта навігації, специфікація переходів, набір автотестів на анімацію. Опишіть вашу навігацію — повернемося з оцінкою та планом робіт.
Артефакти та конфігурації, які ви отримуєте
Передаємо не «папку з вихідними кодами», а комплект, за яким переходи між екранами відтворюються, рев'юються та виправляються без автора. Нижче — що саме залишається у вас після етапу робіт із Shared Element Transition.
- Схеми переходів — карта «екран-джерело → екран-призначення» із зазначенням спільних елементів, напрямку руху та поведінки під час повернення. Окремо фіксуємо випадки, де перехід не будується: зміна орієнтації, відновлення процесу, глибокі посилання.
- Конфігурація анімації — параметри тривалості, інтерполяції та затримок винесені в окремий шар, а не розкидані по коду екранів. Змінюється поведінка переходу — правиться один файл, а не десять місць.
- Розмітка спільних елементів — домовленості про імена та ознаки, за якими елемент зв'язується між екранами. Єдиний словник унеможливлює ситуацію, коли два розробники називають один і той самий блок по-різному.
- Регламент відкату — що робити, якщо перехід ламає навігацію на конкретній версії системи: як вимкнути анімацію точково, не викочуючи новий реліз повністю.
- Інструкції для команди — короткий документ «як додати новий перехід за 15 хвилин»: які кроки обов'язкові, де типові помилки, що перевіряти перед здачею.
- Чек-ліст приймання — набір сценаріїв, за якими перехід перевіряється вручну та автотестами: холодний старт, повернення назад, переривання жестом, слабкий пристрій.
- Список відомих обмежень — де анімація свідомо спрощена і чому. Це знімає питання «а чому тут не як у макеті» на майбутніх ітераціях.
Усе перелічене — робочі файли у вашому репозиторії та вікі, а не презентація. Нова команда або підрядник підхоплює переходи за цими матеріалами без звернень до нас.
Кейс: прискорення навігації в застосунку інтернет-магазину
Замовник — мережа інтернет-магазинів із застосунком на кілька сотень тисяч активних користувачів. Скарги у відгуках повторювалися з разу в раз: «гальмує під час відкриття товару», «повертаюся до каталогу — втрачаю місце, де був».
Перехід зі списку до картки товару тривав близько 900 мс і супроводжувався білим екраном: новий екран відмальовувався з нуля, поки завантажувалися зображення та ціна. Користувач втрачав контекст, а разом із ним і мотивацію додивитися.
Що змінили
Ми перевели навігацію на спільний перехід між екранами: картка товару тепер «виростає» зі свого місця у списку, а кнопка «назад» повертає рівно до тієї самої позиції каталогу зі збереженим скролом.
Паралельно переробили завантаження даних — ціна й наявність підтягуються до старту анімації, тому перехід не чекає на відповідь мережі. Окремо закрили повернення з глибоких екранів: кошик і фільтри більше не скидають стан списку.
Що стало
| Метрика | До | Після |
|---|---|---|
| Час переходу до картки | ~900 мс | ~180 мс |
| Частка успішних повернень до каталогу | 71% | 94% |
| Глибина перегляду за сесію | 4,2 картки | 6,8 картки |
| Частка відгуків зі скаргою на швидкість | 12% | 2% |
Окремо відзначимо утримання: частка користувачів, які повертаються до застосунку наступного дня, зросла на 9 відсоткових пунктів — навігація перестала бути тим місцем, де люди кидали сценарій.
Роботи зайняли три тижні: тиждень на розбір поточних переходів і вузьких місць, півтора — на реалізацію та перевірку на реальних пристроях, включно з бюджетними моделями.
Google Play у відгуках після оновлення фіксує зміну тональності: замість «гальмує» — «стало приємно гортати». Це той випадок, коли технічна правка навігації напряму читається в продуктових метриках.
Чим відрізняється Shared Element Transition у Compose від View-системи?
Різниця виростає з парадигми. View-система імперативна: ви вручну знаходите цільові View, задаєте їм transitionName, збираєте пари «джерело → призначення» і передаєте їх у конструктор переходу.
Compose декларативний: ви описуєте стан екрана, а відповідність елементів між екранами виводиться із спільного ключа, який ви призначаєте через Modifier.sharedElement. Жодних ручних пар — лише ідентичність елемента у двох станах дерева.
У View-системі перехід живе на рівні Activity або Fragment. Ви викликаєте startActivity з опціями переходу, а система сама анімує збіжні View між вікнами.
У Fragment-сценаріях додається ручна робота зі спільними елементами та відкладеними переходами: потрібно дочекатися, поки цільова розмітка готова, інакше анімація стартує по порожньому місцю.
У Compose перехід — частина композиції: AnimatedContent, SharedTransitionLayout і спільний ключ зв'язують елементи всередині одного дерева, а межі Activity або навігаційного графа перестають бути жорстким обмеженням.
Практичні відмінності проявляються у трьох місцях. Перше — передбачуваність: у Compose ви описуєте, як елемент виглядає на початку і в кінці, а інтерполяцію бере на себе середовище; у View ви керуєте кожним кроком і відповідаєте за узгодженість станів.
Друге — перевикористання: спільний ключ працює і всередині одного екрана, і між пунктами навігації, тоді як імперативні пари доводиться перезбирати під кожен сценарій.
Третє — налагодження: декларативний перехід відтворюється зі стану, імперативний залежить від порядку викликів і життєвого циклу.
Обмеження теж різні. У View-системі складніше синхронізувати перехід з асинхронним завантаженням даних: елемент може «стрибнути», якщо контент прийшов пізніше за анімацію.
У Compose вищі вимоги до стабільності ключів — випадково змінюваний ключ рве зв'язок і перехід не спрацьовує.
Вибір диктує не мода, а те, де живе ваш інтерфейс: змішаний проєкт з Activity і Fragment логічніше розвивати на View-механіці, а екрани на Compose отримують переходи безкоштовно з декларативного опису.
Часті технічні сумніви
Переходи між екранами — та частина інтерфейсу, де найчастіше виникають питання сумісності та підтримки. Нижче — розбір типових сумнівів, які виникають у команд перед впровадженням.
Наскільки це сумісно зі старими версіями Android?
Базовий механізм переходів між Activity і Fragment працює на рівні платформи й не потребує окремої бібліотеки.
На старих версіях системи анімація просто відмалюється без плавного «перетікання» спільного елемента — інтерфейс залишиться робочим, деградація торкнеться лише візуального шару.
Для Compose поведінка задається явно через модифікатори, тому там ми контролюємо відкат до звичайної появи екрана на будь-якій версії. Підсумок: єдина кодова база без розгалужень під кожну версію системи.
Чи не просяде продуктивність на слабких пристроях?
Основне навантаження — синхронізація положення та розміру спільного елемента між двома екранами. Якщо на екрані важкі списки або складна відмальовка, перехід може смикатися.
Ми тримаємо анімацію в межах, де вона не блокує основний потік, і вимикаємо перетікання на пристроях з обмеженими ресурсами. Тоді час відгуку залишається передбачуваним, а ефект вмикається там, де він справді читається.
Наскільки складно це підтримувати далі?
Складність з'являється, коли переходи розмазані по екранах і зав'язані на конкретні розмітки. Ми виносимо їх в окремий шар з єдиними правилами: де елемент спільний, як він називається, що відбувається при поверненні.
Тоді новий екран підключається за шаблоном, а не переписується заново. Це знижує ціну правок і прибирає клас помилок, коли після рефакторингу одного екрана «ламається» перехід на іншому.
Чи можна впровадити поступово, без зупинки релізів?
Так. Переходи додаються по одному екрану й не зачіпають логіку навігації. Ми починаємо з ключового сценарію, перевіряємо на реальних пристроях, потім розширюємо. Якщо щось іде не так, ефект вимикається точково, а застосунок продовжує працювати в попередньому вигляді.
Опишіть ваш сценарій — повернемося з оцінкою та планом за етапами.
Обговоримо ваше завдання з анімації переходів
Обговорення завдання з анімації переходів починається з розбору того, де саме переходи «розриваються»: між Activity, всередині Fragment-навігації або в Compose-графі. Ми дивимося на поточну структуру екранів, спосіб навігації та те, які елементи мають «перелітати» між станами.
Від цього залежить, де саме розміщувати спільний елемент і як синхронізувати його життєвий цикл із цільовим екраном.
Далі вибудовуємо план робіт — від діагностики до перевірки на реальних пристроях. Нижче перелік того, що наша команда може зробити для вашого проєкту.
- Провести аудит поточних переходів і знайти місця, де анімація конфліктує з навігацією або перемалюванням.
- Визначити, які екрани переводяться на спільні елементи, а де достатньо стандартних переходів.
- Спроєктувати схему іменування спільних елементів і передачу їх між екранами без втрати стану.
- Реалізувати анімацію переходів для Activity, Fragment і Compose в єдиному стилі.
- Налаштувати коректну роботу із зображеннями та списками, щоб елементи не «стрибали» під час завантаження.
- Перевірити поведінку на слабких пристроях і за повільної мережі, прибрати ривки та підвисання.
- Підготувати документацію щодо додавання нових переходів, щоб команда повторювала підхід без нас.
За підсумками обговорення ви отримаєте оцінку термінів і складу робіт, а також план, у якому порядку впроваджувати переходи, щоб не ламати поточні сценарії. Опишіть завдання — повернемося з оцінкою та планом.







