Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Android Transition - плавные переходы на Android

40 views

Published on

Как сделать красивый плавный переход между экранами на Android.

План:
1 Вступление
2 Зачем нужна анимация - теория
3 Базовые классы - Scene, Transition, TransitionManager
4 Переходы между Activity/Fragment (Content transitions, Shared elements transitions)
5 Дополнение

Published in: Engineering
  • Be the first to comment

  • Be the first to like this

Android Transition - плавные переходы на Android

  1. 1. Анимация плавных переходов на Android Артем Шевченко Киев 2018 https://github.com/shevart/AndroidTransitions shevartsoft@gmail.com
  2. 2. Пример плавного перехода
  3. 3. Transition API и разработчик
  4. 4. Мудрость и опыт
  5. 5. План доклада: Зачем нужна анимация? Scene Transition’s TransitionManager Анимация переходов Activity Анимация переходов Fragments Разбор кейсов и проблем
  6. 6. Анимация переходов теория Или причины зачем разработчику напрягаться?
  7. 7. Причина №1 Это красиво!
  8. 8. Причина №2 Дизайнерское исследование
  9. 9. Маша Эксперт Дизайнерское исследование 🧠Мозг 🏄Транзишены 💊Транзишены $Не навреди
  10. 10. Дизайнерское исследование 🧠 Мозг: Любит упрощать (оптимизировать) Искать взаимосвязь
  11. 11. Дизайнерское исследование Транзишены: Простота и однозначность Правильная физика 🏄
  12. 12. Дизайнерское исследование Транзишены: Обратная связь Понимание происходящего 💊Развлечение
  13. 13. Дизайнерское исследование Не навреди Нет лагам Нет усложнению Не мешает работе с приложением $
  14. 14. Идем дальше Зачем нужна анимация? Scene Transition’s TransitionManager Анимация переходов Activity Анимация переходов Fragments Разбор кейсов и проблем
  15. 15. Scene
  16. 16. Что такое Scene?
  17. 17. Scene - основные понятия Не с документации! :)
  18. 18. sceneRoot Scene - основа ViewGroup Где делаем изменения? Холст для Scene Обязательное поле
  19. 19. contentView Scene - основа Какие делаем изменения? Что рисуем на холсте? Необязательное поле Это абстракция
  20. 20. contentView Scene - основа Это может быть: View xml Разметка Ничего Ну почти ничего
  21. 21. Enter/Exit-Actions Scene - основа Callback’s для enter() и exit() Можем здесь сделать изменения в sceneRoot Необязательны Можем изменять contentView только здесь Runnable
  22. 22. Применение Scene Scene - основа Это вызов метода enter() Изменение contentView внутри sceneRoot
  23. 23. Выход/Отмена Scene Scene - основа Это вызов метода exit() Под капотом - вызов Exit Action * Бонус - вызывается автоматически
  24. 24. Scene - еще чуток теории
  25. 25. Scene - о методе enter() public void enter() { if (mLayoutId > 0 || mLayout != null) { getSceneRoot().removeAllViews(); if (mLayoutId > 0) { LayoutInflater.from(mContext).inflate(mLayoutId, mSceneRoot); } else { mSceneRoot.addView(mLayout); } } if (mEnterAction != null) { mEnterAction.run(); } setCurrentScene(mSceneRoot, this); } О методе enter() подробнее
  26. 26. Scene - о методе enter() public void enter() { if (mLayoutId > 0 || mLayout != null) { getSceneRoot().removeAllViews(); if (mLayoutId > 0) { LayoutInflater.from(mContext).inflate(mLayoutId, mSceneRoot); } else { mSceneRoot.addView(mLayout); } } if (mEnterAction != null) { mEnterAction.run(); } setCurrentScene(mSceneRoot, this); } Приоритет для ContentView * mLayout - это View для contentView
  27. 27. Scene - о методе enter() public void enter() { if (mLayoutId > 0 || mLayout != null) { getSceneRoot().removeAllViews(); if (mLayoutId > 0) { LayoutInflater.from(mContext).inflate(mLayoutId, mSceneRoot); } else { mSceneRoot.addView(mLayout); } } if (mEnterAction != null) { mEnterAction.run(); } setCurrentScene(mSceneRoot, this); } Битые ссылки
  28. 28. Scene - о методе enter() public void enter() { if (mLayoutId > 0 || mLayout != null) { getSceneRoot().removeAllViews(); if (mLayoutId > 0) { LayoutInflater.from(mContext).inflate(mLayoutId, mSceneRoot); } else { mSceneRoot.addView(mLayout); } } if (mEnterAction != null) { mEnterAction.run(); } setCurrentScene(mSceneRoot, this); } Enter Action
  29. 29. Scene - о методе enter() public void enter() { if (mLayoutId > 0 || mLayout != null) { getSceneRoot().removeAllViews(); if (mLayoutId > 0) { LayoutInflater.from(mContext).inflate(mLayoutId, mSceneRoot); } else { mSceneRoot.addView(mLayout); } } if (mEnterAction != null) { mEnterAction.run(); } setCurrentScene(mSceneRoot, this); } Что такое setCurrentScene?
  30. 30. Scene - о методе enter() setCurrentScene(mSceneRoot, this); Что такое setCurrentScene? static void setCurrentScene(View view, Scene scene) { view.setTagInternal(com.android.internal.R.id.current_scene, scene); } Зачем? Scene previousScene = Scene.getCurrentScene(sceneRoot); if (previousScene != null) { previousScene.exit(); } TransitionManager
  31. 31. Пример Картинка + Текст
  32. 32. Сделаем при помощи Scene
  33. 33. Scene sceneRoot <android.support.v7.widget.CardView android:id="@+id/cardView" android:layout_width="match_parent" android:layout_height="320dp" app:cardCornerRadius="4dp" app:cardElevation=«1.5dp"/>
  34. 34. Scene contentView - в XML <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/contentView" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/ivSea" android:layout_width="match_parent" android:layout_height="match_parent" android:adjustViewBounds="true" android:scaleType="centerCrop" android:src="@drawable/sea" /> <TextView android:id="@+id/tvSea" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="start|bottom" android:text="@string/sea" android:textColor="@color/white" android:layout_margin="16dp" android:textSize="32sp" /> </FrameLayout>
  35. 35. Scene contentView ImageView TextView
  36. 36. Scene Применяем - код val scene = Scene.getSceneForLayout(cardView, R.layout.layout_scene_content, context = this) Создаем Scene Применяем Scene scene.enter()
  37. 37. Scene Результат
  38. 38. А где анимация?
  39. 39. Transition API
  40. 40. Scene Как это работает? Scene A Scene B Transition Scene
  41. 41. Scene TransitionManager scene.enter() Меняем TransitionManager.go(scene) На
  42. 42. Scene Результат
  43. 43. Хм, это не та анимация!?
  44. 44. Scene TransitionManager - анимация по умолчанию Удобно - работает с коробки Можно указать собственную анимацию
  45. 45. Давайте сделаем свою анимацию!
  46. 46. Scene Custom Transition для Scene val imageFadeTransition = Fade().addTarget(R.id.ivSea) val textSlideTransition = Slide().addTarget(R.id.tvSea) val customAnimTransition = TransitionSet().apply { duration = 1000L // 1 sec addTransition(imageFadeTransition) addTransition(textSlideTransition) } TransitionManager.go(scene, customAnimTransition)
  47. 47. Scene Плавное появление для картинки val imageFadeTransition = Fade().addTarget(R.id.ivSea) val textSlideTransition = Slide().addTarget(R.id.tvSea) val customAnimTransition = TransitionSet().apply { duration = 1000L // 1 sec addTransition(imageFadeTransition) addTransition(textSlideTransition) } TransitionManager.go(scene, customAnimTransition)
  48. 48. Scene Текст приедет снизу val imageFadeTransition = Fade().addTarget(R.id.ivSea) val textSlideTransition = Slide().addTarget(R.id.tvSea) val customAnimTransition = TransitionSet().apply { duration = 1000L // 1 sec addTransition(imageFadeTransition) addTransition(textSlideTransition) } TransitionManager.go(scene, customAnimTransition)
  49. 49. Scene Создаем TransitionSet val imageFadeTransition = Fade().addTarget(R.id.ivSea) val textSlideTransition = Slide().addTarget(R.id.tvSea) val customAnimTransition = TransitionSet().apply { duration = 1000L // 1 sec addTransition(imageFadeTransition) addTransition(textSlideTransition) } TransitionManager.go(scene, customAnimTransition)
  50. 50. Scene Можно указать время val imageFadeTransition = Fade().addTarget(R.id.ivSea) val textSlideTransition = Slide().addTarget(R.id.tvSea) val customAnimTransition = TransitionSet().apply { duration = 1000L // 1 sec addTransition(imageFadeTransition) addTransition(textSlideTransition) } TransitionManager.go(scene, customAnimTransition)
  51. 51. Scene go() - уже с нашей анимацией val imageFadeTransition = Fade().addTarget(R.id.ivSea) val textSlideTransition = Slide().addTarget(R.id.tvSea) val customAnimTransition = TransitionSet().apply { duration = 1000L // 1 sec addTransition(imageFadeTransition) addTransition(textSlideTransition) } TransitionManager.go(scene, customAnimTransition)
  52. 52. Результат
  53. 53. Получилось!
  54. 54. Transition
  55. 55. Что такое Transition?
  56. 56. Что делает Transition? Transition - основа Scene A Scene B View До View После Transition Анимирует переход
  57. 57. Принцип работы Transition - основа Scene A Scene B Сохраняем параметры SceneA Сохраняем параметры SceneB Только «свои» параметры Transition Создание анимации изменения
  58. 58. 2 главные задачи Transition - основа Сохранение параметров View в SceneA и SceneB Создание анимации для изменения View
  59. 59. Transition - теория
  60. 60. Transition - теория Как Transition сохраняет параметры View? void captureStartValues(@NonNull TransitionValues transitionValues) void captureEndValues(@NonNull TransitionValues transitionValues) А что такое TransitionValues?
  61. 61. Transition - теория Что такое TransitionValues? public class TransitionValues { /** * The set of values tracked by transitions for this scene */ public final Map<String, Object> values = new HashMap<>(); // properties //... public View view; // view }
  62. 62. Transition - теория TransitionValues - пример Есть кнопка 100% Alpha на SceneA 0% Alpha на SceneB
  63. 63. Transition - теория TransitionValues - пример void captureStartValues(@NonNull TransitionValues transitionValues) { float alpha = transitionValues.view.getAlpha(); transitionValues.values.put("ButtonAlpha", alpha);// alpha = 1.0f } void captureEndValues(@NonNull TransitionValues transitionValues) { float alpha = transitionValues.view.getAlpha(); transitionValues.values.put("ButtonAlpha", alpha);// alpha = 0.0f } Сохраняем параметры SceneA Сохраняем параметры SceneB
  64. 64. Transition - теория TransitionValues - пример void captureStartValues(@NonNull TransitionValues transitionValues) { float alpha = transitionValues.view.getAlpha(); transitionValues.values.put("ButtonAlpha", alpha); }
  65. 65. Transition - теория Transition - создание анимации Transition.java @Nullable public Animator createAnimator(@NonNull ViewGroup sceneRoot, @Nullable TransitionValues startValues, @Nullable TransitionValues endValues) { return null; }
  66. 66. Transition - теория Transition - targets
  67. 67. Transition - теория Transition - targets Как Transition определяет какую View анимировать а какую нет? Плавно появляется только картинка Приезжает только текст
  68. 68. Transition - теория Transition - targets По умолчанию: Targets - все View Можно указывать конкретные Target’s Удобно
  69. 69. Transition - теория Target-ом может быть: TransitionName ID - View View Class
  70. 70. Transition - теория Match order public void setMatchOrder(@MatchOrder int... matches) public static final int MATCH_INSTANCE public static final int MATCH_NAME public static final int MATCH_ID public static final int MATCH_ITEM_ID - View - transition name - View ID - Item view ID - для android.widget.Adapter
  71. 71. Transition - теория Как добавить target: <changeBounds> <targets> <target android:targetId="@+id/ivSmallAvatar" /> </targets> </changeBounds> Код val changeBounds = ChangeBounds() changeBounds.addTarget(R.id.ivBigAvatar) XML
  72. 72. Transition - теория Как удалить target: val changeBounds = ChangeBounds() changeBounds.excludeTarget(R.id.ivBigAvatar, exclude = false) Код if (exclude) { list = ArrayListManager.add(list, target); } else { list = ArrayListManager.remove(list, target); } Transition.java
  73. 73. Transition - теория Неочевидно! if (exclude) { list = ArrayListManager.add(list, target); } else { list = ArrayListManager.remove(list, target); } Transition.java Роберт Мартин exclude (исключить) == true -> add?
  74. 74. Transition - теория Как удалить target: val changeBounds = ChangeBounds() changeBounds.removeTarget(R.id.ivBigAvatar) Код
  75. 75. Transition - теория Резюме Target’s Точная настройка анимации Экономия времени на поиск совпадений
  76. 76. Transition - теория Базовые параметры анимации Задержка Продолжительность Интерполятор
  77. 77. Transition - теория Задержка анимации val transition = Fade() transition.startDelay = 200L Код XML <fade android:startDelay="2000"/>
  78. 78. Transition - теория Продолжительность анимации val transition = Fade() transition.duration = 200L Код XML <fade android:duration="2000"/>
  79. 79. Transition - теория Interpolator для анимации val transition = Fade() transition.interpolator = BounceInterpolator() Код XML <fade android:interpolator="@android:interpolator/bounce"/>
  80. 80. Есть еще «приятность»!
  81. 81. Transition - теория За ходом анимации можно следить!
  82. 82. Transition - теория TransitionListener public interface TransitionListener { void onTransitionStart(@NonNull Transition transition); void onTransitionEnd(@NonNull Transition transition); void onTransitionCancel(@NonNull Transition transition); void onTransitionPause(@NonNull Transition transition); void onTransitionResume(@NonNull Transition transition); }
  83. 83. Базовый набор Transition’s
  84. 84. Transition - базовый набор Fade Slide Explode ChangeBounds ChangeTransform ArcMotion* PatternPathMotion* ChangeImageTransform ChangeClipTransform ChangeScroll AutoTransition TransitionSet *Это не совсем Transition
  85. 85. Transition - базовый набор Fade Код Плавно показывает или скрывает View Fade()
  86. 86. Transition - базовый набор Slide Плавно перемещает View за пределы экрана Плавно перемещает View на экран Направление движения устанавливает Gravity Код Slide()
  87. 87. Transition - базовый набор Explode Двигает View в сторону от центра или указанного эпицентра Код Explode()
  88. 88. Transition - базовый набор Explode - пример
  89. 89. Transition - базовый набор Explode Есть набор View для Explode Все они одинаковые Эпицентром будет верхний левый квадрат
  90. 90. Transition - базовый набор Explode - эпицентр fun createEpicenterCallback(): Transition.EpicenterCallback { var rect: Rect? = null val centralView = yourView if (centralView != null) { rect = Rect() centralView.getGlobalVisibleRect(rect) } return object : Transition.EpicenterCallback() { override fun onGetEpicenter(transition: Transition) = rect } } Нужно создать EpicenterCallback
  91. 91. Transition - базовый набор Explode - эпицентр fun createEpicenterCallback(): Transition.EpicenterCallback { var rect: Rect? = null val centralView = yourView if (centralView != null) { rect = Rect() centralView.getGlobalVisibleRect(rect) } return object : Transition.EpicenterCallback() { override fun onGetEpicenter(transition: Transition) = rect } } EpicenterCallback возвращает Rect с координатами эпицентра
  92. 92. Transition - базовый набор Explode - эпицентр fun createEpicenterCallback(): Transition.EpicenterCallback { var rect: Rect? = null val centralView = yourView if (centralView != null) { rect = Rect() centralView.getGlobalVisibleRect(rect) } return object : Transition.EpicenterCallback() { override fun onGetEpicenter(transition: Transition) = rect } } Мы можем взять Rect у нашей View
  93. 93. Transition - базовый набор Explode val explode = Explode() explode.epicenterCallback = createEpicenterCallback() Создаем Explode с EpicenterCallback
  94. 94. Transition - базовый набор Explode
  95. 95. Что-то пошло не так…
  96. 96. Transition - базовый набор Explode Добавляем Targets: val transitionSet = Explode().apply { addTarget(R.id.frameLayout) addTarget(R.id.frameLayout2) addTarget(R.id.frameLayout3) addTarget(R.id.frameLayout4) addTarget(R.id.frameLayout5) addTarget(R.id.frameLayout6) addTarget(R.id.frameLayout7) addTarget(R.id.frameLayout8) addTarget(R.id.frameLayout9) }
  97. 97. Transition - базовый набор Explode Получилось!
  98. 98. Transition - базовый набор Explode Надо что-то улучшить! val transitionSet = Explode().apply { addTarget(R.id.frameLayout) addTarget(R.id.frameLayout2) addTarget(R.id.frameLayout3) addTarget(R.id.frameLayout4) addTarget(R.id.frameLayout5) addTarget(R.id.frameLayout6) addTarget(R.id.frameLayout7) addTarget(R.id.frameLayout8) addTarget(R.id.frameLayout9) }
  99. 99. Transition - базовый набор Explode val transitionSet = TransitionSet().apply { addTransition(Explode().addTarget(FrameLayout::class.java)) } val transitionSet = Explode().apply { addTarget(R.id.frameLayout) addTarget(R.id.frameLayout2) addTarget(R.id.frameLayout3) addTarget(R.id.frameLayout4) addTarget(R.id.frameLayout5) addTarget(R.id.frameLayout6) addTarget(R.id.frameLayout7) addTarget(R.id.frameLayout8) addTarget(R.id.frameLayout9) }
  100. 100. Отлично!
  101. 101. Transition - базовый набор ChangeBounds Анимирует изменение местоположения View Анимирует изменение размеров View Код ChangeBounds()
  102. 102. Transition - базовый набор ArcMotion Делает перемещение View по окружности Работает в связке с ChangeBounds Код ChangeBounds().apply { setPathMotion(ArcMotion()) } Настройка угла окружности
  103. 103. Transition - базовый набор PatternPathMotion Перемещение View по кастомному пути Работает в связке с ChangeBounds
  104. 104. Transition - базовый набор PatternPathMotion Код val path = createPath() return ChangeBounds().apply { setPathMotion(PatternPathMotion(path)) } private fun createPath() = Path().apply { moveTo(0f,0f) quadTo(0f,0f, 1f, 0.0f) quadTo(1f,0.0f, 0.3f, 0.6f) quadTo(0.3f,0.6f, 1f, 1f) }
  105. 105. Transition - базовый набор PatternPathMotion Код val path = createPath() return ChangeBounds().apply { setPathMotion(PatternPathMotion(path)) } private fun createPath() = Path().apply { moveTo(0f,0f) quadTo(0f,0f, 1f, 0.0f) quadTo(1f,0.0f, 0.3f, 0.6f) quadTo(0.3f,0.6f, 1f, 1f) } Указываем свой путь движения
  106. 106. Сравнение ChangeBounds/Arc/Pattern Transition - базовый набор
  107. 107. Transition - базовый набор ChangeClipBounds Анимирует изменение clipBounds у View Код ChangeClipBounds()
  108. 108. Transition - базовый набор ChangeImageTransform Анимирует матричное изменение внутри ImageView Код ChangeImageTransform() + ChangeBounds
  109. 109. Transition - базовый набор ChangeTransform Анимирует изменение размера View и поворот Код ChangeTransform() +ChangeBounds
  110. 110. Transition - базовый набор ChangeScroll Анимирует изменение scrollX и scrollY Код ChangeScroll()
  111. 111. Transition - базовый набор AutoTransition Анимиция появления/исчезновения Код AutoTransition() Анимиция размеров и местоположения View Fade + ChangeBounds
  112. 112. Transition - базовый набор TransitionSet Позволяет группировать Transitions вместе Код TransitionSet() Сам по себе не создает анимацию
  113. 113. Transition - базовый набор TransitionSet XML <?xml version="1.0" encoding="utf-8"?> <transitionSet> <changeBounds/> // … </transitionSet>
  114. 114. Transition - базовый набор TransitionSet Порядок проигрывания анимации public static final int ORDERING_TOGETHER = 0; public static final int ORDERING_SEQUENTIAL = 1; public TransitionSet setOrdering(int ordering)
  115. 115. TransitionManager
  116. 116. TransitionManager Обзор TransitionManager Transition Scene
  117. 117. TransitionManager Основные методы setTransition() - сохраняет пару Scene-Transition transitionTo() - применяет Scene с указанными или по умолчанию Transition-ом endTransition() - останавливет все текущие и отложенные Transition’s для View static go() - применяет Scene + Transition (default или custom) static beginDelayedTransition() - применяет Scene + Transition (default или custom)
  118. 118. TransitionManager beginDelayedTransition
  119. 119. flTestContent.visibility = View.INVISIBLE val transition = AutoTransition() transition.doOnEnd { showWithAlpha() } TransitionManager.beginDelayedTransition(cardView, transition) flTestContent.setHeight(height = 500) // your height TransitionManager Как это сделать просто
  120. 120. flTestContent.visibility = View.INVISIBLE val transition = AutoTransition() transition.doOnEnd { showWithAlpha() } TransitionManager.beginDelayedTransition(cardView, transition) flTestContent.setHeight(height = 500) // your height TransitionManager Как это сделать просто Скрываем содержимое cardView
  121. 121. flTestContent.visibility = View.INVISIBLE val transition = AutoTransition() transition.doOnEnd { showWithAlpha() } TransitionManager.beginDelayedTransition(cardView, transition) flTestContent.setHeight(height = 500) // your height TransitionManager Как это сделать просто Создаем наш Transition
  122. 122. flTestContent.visibility = View.INVISIBLE val transition = AutoTransition() transition.doOnEnd { showWithAlpha() } TransitionManager.beginDelayedTransition(cardView, transition) flTestContent.setHeight(height = 500) // your height TransitionManager Как это сделать просто Что делает showWithAlpha? private fun showWithAlpha() { TransitionManager.beginDelayedTransition(cardView) flTestContent.visibility = View.VISIBLE }
  123. 123. flTestContent.visibility = View.INVISIBLE val transition = AutoTransition() transition.doOnEnd { showWithAlpha() } TransitionManager.beginDelayedTransition(cardView, transition) flTestContent.setHeight(height = 500) // your height TransitionManager Как это сделать просто Готовимся анимировать изменения
  124. 124. flTestContent.visibility = View.INVISIBLE val transition = AutoTransition() transition.doOnEnd { showWithAlpha() } TransitionManager.beginDelayedTransition(cardView, transition) flTestContent.setHeight(height = 500) // your height TransitionManager Это действительно просто Делаем изменения
  125. 125. Здорово!
  126. 126. Идем дальше Scene Transition’s TransitionManager Анимация переходов Activity Анимация переходов Fragments Разбор кейсов и проблем
  127. 127. TransitionПереходы между экранами
  128. 128. Transitions между экранами
  129. 129. Transitions между экранами 2 вида анимаций Content transitions или Анимация содержимого Shared elements
 или Общие элементы
  130. 130. Transitions между экранами
  131. 131. Content Transitions Анимация содержимого
  132. 132. Transitions между экранами Итак, у нас есть… Screen B Screen A 2 экрана 4Анимации
  133. 133. Transitions между экранами 4 анимации Screen BScreen A ScreenA - уходит - Exit Transition ScreenB - появляеться - Enter Transition Переход ScreenA - ScreenB Возвращаемся ScreenB - ScreenA ScreenA - снова приходит - Reenter Transition ScreenB - закрывается - Return Transition
  134. 134. Transitions между экранами 4 анимации - в профиль Screen BScreen A С ScreenA открыли ScreenB Exit Transition Enter Transition Screen B Screen A Закрыли ScreenB вернулись на ScreenA Return Transition Reenter Transition
  135. 135. Transitions между экранами Плюсы Content transitions Transitions Targets Каждую View можно анимировать отдельно
  136. 136. Анимация Activity
  137. 137. Content transitions - Activity Два пути class FirstContentProgrammaticallyActivity : AbsActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // set content transitions setContentTransition() setContentView(R.layout.activity_first_content_programmatically) initToolbar() val adapter = MockSimpleItemAdapter() adapter.updateItems(nextSimpleItemsList()) adapter.setOnItemClickListener(this::onItemSelected) rvPCScreenA.adapter = adapter rvPCScreenA.layoutManager = LinearLayoutManager(this) } private fun setContentTransition() { window.apply { requestFeature(Window.FEATURE_CONTENT_TRANSITIONS) exitTransition = Explode() } } private fun initToolbar() { enableToolbarBackButton() setTitle(R.string.activity_content_programmatically) } private fun onItemSelected(item: SimpleItem) { val intent = Intent(this, SecondContentProgrammaticallyActivity::class.java) intent.putExtra(SIMPLE_ITEM, item) startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle()) } } Код XML - Theme
  138. 138. Content transitions - Activity Theme <style name="ContentTransitionAppTheme" parent="Theme.AppCompat.Light"> <!-- Enable window content transitions --> <item name="android:windowContentTransitions">true</item> <!-- Specify 4 transitions --> <item name="android:windowEnterTransition">@transition/slide</item> <item name="android:windowExitTransition">@transition/slide</item> <item name="android:windowReturnTransition">@transition/slide</item> <item name="android:windowReenterTransition">@transition/slide</item> </style> Создаем тему для Activity
  139. 139. Content transitions - Activity Theme <style name="ContentTransitionAppTheme" parent="Theme.AppCompat.Light"> <!-- Enable window content transitions --> <item name="android:windowContentTransitions">true</item> <!-- Specify 4 transitions --> <item name="android:windowEnterTransition">@transition/slide</item> <item name="android:windowExitTransition">@transition/slide</item> <item name="android:windowReturnTransition">@transition/slide</item> <item name="android:windowReenterTransition">@transition/slide</item> </style> Включаем transitions
  140. 140. Content transitions - Activity Theme <style name="ContentTransitionAppTheme" parent="Theme.AppCompat.Light"> <!-- Enable window content transitions --> <item name="android:windowContentTransitions">true</item> <!-- Specify 4 transitions --> <item name="android:windowEnterTransition">@transition/slide</item> <item name="android:windowExitTransition">@transition/slide</item> <item name="android:windowReturnTransition">@transition/slide</item> <item name="android:windowReenterTransition">@transition/slide</item> </style> Указываем 4 анимации
  141. 141. Content transitions - Activity Результат
  142. 142. Что-то оно не очень…
  143. 143. Content transitions - Activity Slide vs AutoTransition
  144. 144. Content transitions - Activity Код ActivityA override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // set content transitions setContentTransition() setContentView(R.layout.activity_a) // ... }
  145. 145. Content transitions - Activity Код ActivityA - перед вызовом setContentView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // set content transitions setContentTransition() setContentView(R.layout.activity_a) // ... }
  146. 146. Content transitions - Activity Код ActivityA - запроси фичу, укажи transition private fun setContentTransition() { window.apply { requestFeature(Window.FEATURE_CONTENT_TRANSITIONS) exitTransition = Explode() // set other transitions } }
  147. 147. Content transitions - Activity Код ActivityB - аналогично private fun setContentTransition() { window.apply { requestFeature(Window.FEATURE_CONTENT_TRANSITIONS) enterTransition = Fade() } }
  148. 148. Content transitions - Activity Код Запуск ActivityB - важный момент startActivity(intent)startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle())
  149. 149. Content transitions - Activity Результат
  150. 150. Анимация Fragment
  151. 151. Content transitions - Fragments Только код! ;) fun openScreenB() { val fragment = FragmentB() fragment.enterTransition = // your transition fragment.returnTransition = // your transition supportFragmentManager .beginTransaction() .replace(R.id.flContainer, fragment) .addToBackStack(fragment::class.java.simpleName) .commit() }
  152. 152. Content transitions - Fragments Transitions для FragmentA val fragment = FragmentA() fragment.exitTransition = // transition fragment.reenterTransition = // transition Бонус Перед каждой транзакцией можно менять transitions!
  153. 153. Content transitions - Activity Результат
  154. 154. С фрагментами удобно
  155. 155. Shared Elements Общие элементы
  156. 156. Shared Elements - теория Почти тоже самое Screen B Screen A 2 экрана 2 Анимации у Fragments 1 Target 4 Анимации у Activity
  157. 157. Shared Elements - теория 4 анимации - Activity ScreenA - уходит - Shared Elements Exit Transition ScreenB - появляеться - Shared Elements Enter Transition Переход ScreenA - ScreenB Возвращаемся ScreenB - ScreenA ScreenA - снова приходит - Shared Elements Reenter Transition ScreenB - закрывается - Shared Elements Return Transition
  158. 158. Shared Elements - теория 4 анимации - Activity ScreenB - появляеться - Shared Elements Enter Transition Достаточно указать одну:
  159. 159. Shared Elements - теория 2 анимации - Fragments SharedElementsEnterTransition - при открытии фрагмента Переход ScreenA - ScreenB SharedElementsReturnTransition - при удалении (pop) фрагмента из стека
  160. 160. Shared Elements - теория 2 анимации - Fragments SharedElementsEnterTransition - FragmentB Можно указать только 1
  161. 161. Shared Elements - теория 1 target TransitionName
  162. 162. Shared Elements Activity
  163. 163. Shared Elements - Activity Activity - Тема <style name="SharedElementsTheme" parent="Theme.AppCompat.Light"> <item name="android:windowActivityTransitions">true</item> <item name=«android:windowSharedElementEnterTransition"> @android:transition/move </item> </style> ActivityB
  164. 164. Shared Elements - Activity Activity - Код ActivityB private fun sharedElementsInit() { window.apply { requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS) sharedElementEnterTransition = ChangeBounds() } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) sharedElementsInit() // перед setContentView setContentView(R.layout.activity_b) // ... }
  165. 165. Shared Elements - Activity Запуск ActivityB В ActivityА val intent = Intent(this, ActivityB::class.java) val activityOptions = ActivityOptionsCompat .makeSceneTransitionAnimation(this, view, view.transitionName) startActivity(intent, activityOptions.toBundle())
  166. 166. Shared Elements - Activity Готовим ActivityB В ActivityB yourView.transitionName = «transitionName» // your transitionName
  167. 167. Shared Elements Fragment
  168. 168. Shared Elements - Fragments Fragment В транзакции supportFragmentManager .beginTransaction() .replace(R.id.flFragmentsContainer, FragmentB()) .addToBackStack(TAG) .addSharedElement(view, view.transitionName) .commit()
  169. 169. Shared Elements - Fragments Fragment Добавляем sharedElement supportFragmentManager .beginTransaction() .replace(R.id.flFragmentsContainer, FragmentB()) .addToBackStack(TAG) .addSharedElement(view, view.transitionName) .commit()
  170. 170. Shared Elements - Fragments Fragment Вернутся красиво supportFragmentManager .beginTransaction() .replace(R.id.flFragmentsContainer, FragmentB()) .addToBackStack(TAG) .addSharedElement(view, view.transitionName) .commit()
  171. 171. Shared Elements - Fragments FragmentB Понадобится Transition override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) sharedElementEnterTransition = TransitionInflater .from(context) .inflateTransition(android.R.transition.move) }
  172. 172. Идем дальше Анимация переходов Activity Анимация переходов Fragments Разбор кейсов и проблем
  173. 173. Дополнение
  174. 174. Дополнение Transitions + AsyncWorld = postponeEnterTransition() viewWithLongLoading.doOnPreDraw { startPostponedEnterTransition() }
  175. 175. Дополнение Почему не работают sharedElements? TransitionName - либо не указано либо дубликаты View measure - View еще не знает свои размеры/положение SharedElementsEnterTransition - null/не тот Transition Keyboard - сначала закрой потом анимируй SharedElementsReturnTransition - null - не сработает переход обратно

×