SlideShare a Scribd company logo
1 of 176
Download to read offline
Анимация плавных
переходов на Android
Артем Шевченко
Киев 2018
https://github.com/shevart/AndroidTransitions
shevartsoft@gmail.com
Пример плавного перехода
Transition API и разработчик
Мудрость и опыт
План доклада:
Зачем
нужна
анимация?
Scene
Transition’s
TransitionManager
Анимация переходов
Activity
Анимация переходов
Fragments
Разбор
кейсов и
проблем
Анимация переходов
теория
Или причины зачем разработчику напрягаться?
Причина №1
Это красиво!
Причина №2
Дизайнерское исследование
Маша
Эксперт
Дизайнерское исследование
🧠Мозг
🏄Транзишены
💊Транзишены
$Не навреди
Дизайнерское исследование
🧠
Мозг:
Любит упрощать (оптимизировать)
Искать взаимосвязь
Дизайнерское исследование
Транзишены:
Простота и однозначность
Правильная физика 🏄
Дизайнерское исследование
Транзишены:
Обратная связь
Понимание происходящего 💊Развлечение
Дизайнерское исследование
Не навреди
Нет лагам
Нет усложнению
Не мешает работе с приложением
$
Идем дальше
Зачем
нужна
анимация?
Scene
Transition’s
TransitionManager
Анимация переходов
Activity
Анимация переходов
Fragments
Разбор
кейсов и
проблем
Scene
Что такое Scene?
Scene - основные понятия
Не с документации! :)
sceneRoot
Scene - основа
ViewGroup
Где делаем изменения?
Холст для Scene
Обязательное поле
contentView
Scene - основа
Какие делаем изменения?
Что рисуем на холсте?
Необязательное поле
Это абстракция
contentView
Scene - основа
Это может быть:
View
xml

Разметка
Ничего
Ну

почти ничего
Enter/Exit-Actions
Scene - основа
Callback’s для enter() и exit()
Можем здесь сделать изменения в sceneRoot
Необязательны
Можем изменять contentView только здесь
Runnable
Применение Scene
Scene - основа
Это вызов метода enter()
Изменение contentView внутри sceneRoot
Выход/Отмена Scene
Scene - основа
Это вызов метода exit()
Под капотом - вызов Exit Action
* Бонус - вызывается автоматически
Scene - еще чуток
теории
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() подробнее
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
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);
}
Битые ссылки
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
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?
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
Пример
Картинка + Текст
Сделаем при помощи Scene
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"/>
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>
Scene
contentView
ImageView
TextView
Scene
Применяем - код
val scene = Scene.getSceneForLayout(cardView,
R.layout.layout_scene_content,
context = this)
Создаем Scene
Применяем Scene
scene.enter()
Scene
Результат
А где анимация?
Transition API
Scene
Как это работает?
Scene A Scene B
Transition
Scene
Scene
TransitionManager
scene.enter()
Меняем
TransitionManager.go(scene)
На
Scene
Результат
Хм, это не та анимация!?
Scene
TransitionManager - анимация по умолчанию
Удобно - работает с коробки
Можно указать собственную анимацию
Давайте сделаем
свою анимацию!
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)
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)
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)
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)
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)
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)
Результат
Получилось!
Transition
Что такое Transition?
Что делает Transition?
Transition - основа
Scene A Scene B
View До View После
Transition
Анимирует переход
Принцип работы
Transition - основа
Scene A Scene B
Сохраняем параметры SceneA
Сохраняем параметры SceneB
Только «свои» параметры
Transition
Создание анимации изменения
2 главные задачи
Transition - основа
Сохранение
параметров View в
SceneA и SceneB
Создание анимации
для
изменения View
Transition - теория
Transition - теория
Как Transition сохраняет параметры
View?
void captureStartValues(@NonNull TransitionValues transitionValues)
void captureEndValues(@NonNull TransitionValues transitionValues)
А что такое TransitionValues?
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
}
Transition - теория
TransitionValues - пример
Есть кнопка
100%
Alpha на SceneA
0%
Alpha на SceneB
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
Transition - теория
TransitionValues - пример
void captureStartValues(@NonNull TransitionValues transitionValues) {
float alpha = transitionValues.view.getAlpha();
transitionValues.values.put("ButtonAlpha", alpha);
}
Transition - теория
Transition - создание анимации
Transition.java
@Nullable
public Animator createAnimator(@NonNull ViewGroup sceneRoot,
@Nullable TransitionValues startValues,
@Nullable TransitionValues endValues) {
return null;
}
Transition - теория
Transition - targets
Transition - теория
Transition - targets
Как Transition определяет какую View анимировать а какую нет?
Плавно появляется только картинка
Приезжает только текст
Transition - теория
Transition - targets
По умолчанию:
Targets - все View
Можно указывать
конкретные Target’s Удобно
Transition - теория
Target-ом может быть:
TransitionName
ID - View
View
Class
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
Transition - теория
Как добавить target:
<changeBounds>
<targets>
<target android:targetId="@+id/ivSmallAvatar" />
</targets>
</changeBounds>
Код
val changeBounds = ChangeBounds()
changeBounds.addTarget(R.id.ivBigAvatar)
XML
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
Transition - теория
Неочевидно!
if (exclude) {
list = ArrayListManager.add(list, target);
} else {
list = ArrayListManager.remove(list, target);
}
Transition.java
Роберт Мартин
exclude (исключить) == true -> add?
Transition - теория
Как удалить target:
val changeBounds = ChangeBounds()
changeBounds.removeTarget(R.id.ivBigAvatar)
Код
Transition - теория
Резюме Target’s
Точная настройка
анимации
Экономия
времени на поиск
совпадений
Transition - теория
Базовые параметры анимации
Задержка Продолжительность Интерполятор
Transition - теория
Задержка анимации
val transition = Fade()
transition.startDelay = 200L
Код
XML
<fade android:startDelay="2000"/>
Transition - теория
Продолжительность анимации
val transition = Fade()
transition.duration = 200L
Код
XML
<fade android:duration="2000"/>
Transition - теория
Interpolator для анимации
val transition = Fade()
transition.interpolator = BounceInterpolator()
Код
XML
<fade android:interpolator="@android:interpolator/bounce"/>
Есть еще «приятность»!
Transition - теория
За ходом анимации можно следить!
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);
}
Базовый набор Transition’s
Transition - базовый набор
Fade Slide Explode ChangeBounds
ChangeTransform
ArcMotion* PatternPathMotion*
ChangeImageTransform ChangeClipTransform ChangeScroll AutoTransition TransitionSet
*Это не совсем Transition
Transition - базовый набор
Fade
Код
Плавно показывает или скрывает View
Fade()
Transition - базовый набор
Slide
Плавно перемещает View за пределы экрана
Плавно перемещает View на экран
Направление движения устанавливает
Gravity
Код
Slide()
Transition - базовый набор
Explode
Двигает View в сторону от центра или
указанного эпицентра
Код
Explode()
Transition - базовый набор
Explode - пример
Transition - базовый набор
Explode
Есть набор View для Explode
Все они одинаковые
Эпицентром будет верхний левый квадрат
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
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 с координатами эпицентра
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
Transition - базовый набор
Explode
val explode = Explode()
explode.epicenterCallback = createEpicenterCallback()
Создаем Explode с EpicenterCallback
Transition - базовый набор
Explode
Что-то пошло не так…
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)
}
Transition - базовый набор
Explode
Получилось!
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)
}
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)
}
Отлично!
Transition - базовый набор
ChangeBounds
Анимирует изменение местоположения View
Анимирует изменение размеров View
Код
ChangeBounds()
Transition - базовый набор
ArcMotion
Делает перемещение View по окружности
Работает в связке с ChangeBounds
Код
ChangeBounds().apply {
setPathMotion(ArcMotion())
}
Настройка угла окружности
Transition - базовый набор
PatternPathMotion
Перемещение View по кастомному пути
Работает в связке с ChangeBounds
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)
}
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)
}
Указываем
свой
путь движения
Сравнение ChangeBounds/Arc/Pattern
Transition - базовый набор
Transition - базовый набор
ChangeClipBounds
Анимирует изменение clipBounds у View
Код
ChangeClipBounds()
Transition - базовый набор
ChangeImageTransform
Анимирует матричное изменение
внутри ImageView
Код
ChangeImageTransform() + ChangeBounds
Transition - базовый набор
ChangeTransform
Анимирует изменение размера View и
поворот
Код
ChangeTransform() +ChangeBounds
Transition - базовый набор
ChangeScroll
Анимирует изменение scrollX и scrollY
Код
ChangeScroll()
Transition - базовый набор
AutoTransition
Анимиция появления/исчезновения
Код
AutoTransition()
Анимиция размеров и местоположения
View
Fade + ChangeBounds
Transition - базовый набор
TransitionSet
Позволяет группировать Transitions вместе
Код
TransitionSet()
Сам по себе не создает анимацию
Transition - базовый набор
TransitionSet
XML
<?xml version="1.0" encoding="utf-8"?>
<transitionSet>
<changeBounds/>
// …
</transitionSet>
Transition - базовый набор
TransitionSet
Порядок проигрывания анимации
public static final int ORDERING_TOGETHER = 0;
public static final int ORDERING_SEQUENTIAL = 1;
public TransitionSet setOrdering(int ordering)
TransitionManager
TransitionManager
Обзор
TransitionManager
Transition Scene
TransitionManager
Основные методы
setTransition() - сохраняет пару Scene-Transition
transitionTo() - применяет Scene с указанными или по умолчанию Transition-ом
endTransition() - останавливет все текущие и отложенные Transition’s для View
static go() - применяет Scene + Transition (default или custom)
static beginDelayedTransition() - применяет Scene + Transition (default или custom)
TransitionManager
beginDelayedTransition
flTestContent.visibility = View.INVISIBLE
val transition = AutoTransition()
transition.doOnEnd { showWithAlpha() }
TransitionManager.beginDelayedTransition(cardView, transition)
flTestContent.setHeight(height = 500) // your height
TransitionManager
Как это сделать просто
flTestContent.visibility = View.INVISIBLE
val transition = AutoTransition()
transition.doOnEnd { showWithAlpha() }
TransitionManager.beginDelayedTransition(cardView, transition)
flTestContent.setHeight(height = 500) // your height
TransitionManager
Как это сделать просто
Скрываем содержимое cardView
flTestContent.visibility = View.INVISIBLE
val transition = AutoTransition()
transition.doOnEnd { showWithAlpha() }
TransitionManager.beginDelayedTransition(cardView, transition)
flTestContent.setHeight(height = 500) // your height
TransitionManager
Как это сделать просто
Создаем наш Transition
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
}
flTestContent.visibility = View.INVISIBLE
val transition = AutoTransition()
transition.doOnEnd { showWithAlpha() }
TransitionManager.beginDelayedTransition(cardView, transition)
flTestContent.setHeight(height = 500) // your height
TransitionManager
Как это сделать просто
Готовимся анимировать изменения
flTestContent.visibility = View.INVISIBLE
val transition = AutoTransition()
transition.doOnEnd { showWithAlpha() }
TransitionManager.beginDelayedTransition(cardView, transition)
flTestContent.setHeight(height = 500) // your height
TransitionManager
Это действительно просто
Делаем изменения
Здорово!
Идем дальше
Scene
Transition’s
TransitionManager
Анимация переходов
Activity
Анимация переходов
Fragments
Разбор
кейсов и
проблем
TransitionПереходы
между экранами
Transitions между экранами
Transitions между экранами
2 вида анимаций
Content transitions
или
Анимация содержимого
Shared elements

или
Общие элементы
Transitions между экранами
Content Transitions
Анимация содержимого
Transitions между экранами
Итак, у нас есть…
Screen B
Screen A
2 экрана
4Анимации
Transitions между экранами
4 анимации
Screen BScreen A
ScreenA - уходит - Exit Transition
ScreenB - появляеться - Enter Transition
Переход ScreenA - ScreenB
Возвращаемся ScreenB - ScreenA
ScreenA - снова приходит - Reenter Transition
ScreenB - закрывается - Return Transition
Transitions между экранами
4 анимации - в профиль
Screen BScreen A
С ScreenA
открыли ScreenB
Exit Transition Enter Transition
Screen B Screen A
Закрыли ScreenB
вернулись на ScreenA
Return Transition Reenter Transition
Transitions между экранами
Плюсы Content transitions
Transitions Targets Каждую View
можно
анимировать отдельно
Анимация Activity
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
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
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
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 анимации
Content transitions - Activity
Результат
Что-то оно не очень…
Content transitions - Activity
Slide vs AutoTransition
Content transitions - Activity
Код
ActivityA
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// set content transitions
setContentTransition()
setContentView(R.layout.activity_a)
// ...
}
Content transitions - Activity
Код
ActivityA - перед вызовом setContentView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// set content transitions
setContentTransition()
setContentView(R.layout.activity_a)
// ...
}
Content transitions - Activity
Код
ActivityA - запроси фичу, укажи transition
private fun setContentTransition() {
window.apply {
requestFeature(Window.FEATURE_CONTENT_TRANSITIONS)
exitTransition = Explode()
// set other transitions
}
}
Content transitions - Activity
Код
ActivityB - аналогично
private fun setContentTransition() {
window.apply {
requestFeature(Window.FEATURE_CONTENT_TRANSITIONS)
enterTransition = Fade()
}
}
Content transitions - Activity
Код
Запуск ActivityB - важный момент
startActivity(intent)startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle())
Content transitions - Activity
Результат
Анимация Fragment
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()
}
Content transitions - Fragments
Transitions для FragmentA
val fragment = FragmentA()
fragment.exitTransition = // transition
fragment.reenterTransition = // transition
Бонус
Перед каждой транзакцией можно менять transitions!
Content transitions - Activity
Результат
С фрагментами удобно
Shared Elements
Общие элементы
Shared Elements - теория
Почти тоже самое
Screen B
Screen A
2 экрана
2
Анимации
у Fragments
1
Target
4
Анимации
у Activity
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
Shared Elements - теория
4 анимации - Activity
ScreenB - появляеться - Shared Elements Enter Transition
Достаточно указать одну:
Shared Elements - теория
2 анимации - Fragments
SharedElementsEnterTransition - при открытии фрагмента
Переход ScreenA - ScreenB
SharedElementsReturnTransition - при удалении (pop) фрагмента из стека
Shared Elements - теория
2 анимации - Fragments
SharedElementsEnterTransition - FragmentB
Можно указать только 1
Shared Elements - теория
1 target
TransitionName
Shared Elements Activity
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
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)
// ...
}
Shared Elements - Activity
Запуск ActivityB
В ActivityА
val intent = Intent(this, ActivityB::class.java)
val activityOptions = ActivityOptionsCompat
.makeSceneTransitionAnimation(this, view, view.transitionName)
startActivity(intent, activityOptions.toBundle())
Shared Elements - Activity
Готовим ActivityB
В ActivityB
yourView.transitionName = «transitionName» // your transitionName
Shared Elements Fragment
Shared Elements - Fragments
Fragment
В транзакции
supportFragmentManager
.beginTransaction()
.replace(R.id.flFragmentsContainer, FragmentB())
.addToBackStack(TAG)
.addSharedElement(view, view.transitionName)
.commit()
Shared Elements - Fragments
Fragment
Добавляем sharedElement
supportFragmentManager
.beginTransaction()
.replace(R.id.flFragmentsContainer, FragmentB())
.addToBackStack(TAG)
.addSharedElement(view, view.transitionName)
.commit()
Shared Elements - Fragments
Fragment
Вернутся красиво
supportFragmentManager
.beginTransaction()
.replace(R.id.flFragmentsContainer, FragmentB())
.addToBackStack(TAG)
.addSharedElement(view, view.transitionName)
.commit()
Shared Elements - Fragments
FragmentB
Понадобится Transition
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
sharedElementEnterTransition = TransitionInflater
.from(context)
.inflateTransition(android.R.transition.move)
}
Идем дальше
Анимация переходов
Activity
Анимация переходов
Fragments
Разбор
кейсов и
проблем
Дополнение
Дополнение
Transitions + AsyncWorld =
postponeEnterTransition()
viewWithLongLoading.doOnPreDraw {
startPostponedEnterTransition()
}
Дополнение
Почему не работают sharedElements?
TransitionName - либо не указано либо дубликаты
View measure - View еще не знает свои размеры/положение
SharedElementsEnterTransition - null/не тот Transition
Keyboard - сначала закрой потом анимируй
SharedElementsReturnTransition - null - не сработает переход обратно

More Related Content

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

automation is iOS development
automation is iOS developmentautomation is iOS development
automation is iOS developmentIvan Trifonov
 
КРИ 2008. Проектирование игр: функциональный подход
КРИ 2008. Проектирование игр: функциональный подходКРИ 2008. Проектирование игр: функциональный подход
КРИ 2008. Проектирование игр: функциональный подходKirill Lebedev
 
C# Web. Занятие 02.
C# Web. Занятие 02.C# Web. Занятие 02.
C# Web. Занятие 02.Igor Shkulipa
 
Moxy. Как правильно пользоваться? / Юрий Шмаков (Arello Mobile)
Moxy. Как правильно пользоваться? / Юрий Шмаков (Arello Mobile)Moxy. Как правильно пользоваться? / Юрий Шмаков (Arello Mobile)
Moxy. Как правильно пользоваться? / Юрий Шмаков (Arello Mobile)Ontico
 
Статический анализ кода в DDD
Статический анализ кода в DDDСтатический анализ кода в DDD
Статический анализ кода в DDDAleksei Alekseev
 
Правильный Xamarin для мобильных приложений (Microsoft DevCon School 2016) [R...
Правильный Xamarin для мобильных приложений (Microsoft DevCon School 2016) [R...Правильный Xamarin для мобильных приложений (Microsoft DevCon School 2016) [R...
Правильный Xamarin для мобильных приложений (Microsoft DevCon School 2016) [R...Binwell
 
Wild Async .NET world: AID Kit for boy-scouts
Wild Async .NET world: AID Kit for boy-scoutsWild Async .NET world: AID Kit for boy-scouts
Wild Async .NET world: AID Kit for boy-scoutsHYS Enterprise
 
20100425 model based_testing_kuliamin_lectures01-03
20100425 model based_testing_kuliamin_lectures01-0320100425 model based_testing_kuliamin_lectures01-03
20100425 model based_testing_kuliamin_lectures01-03Computer Science Club
 
Превышаем скоростные лимиты с Angular 2 / Алексей Охрименко (IPONWEB)
Превышаем скоростные лимиты с Angular 2 / Алексей Охрименко (IPONWEB)Превышаем скоростные лимиты с Angular 2 / Алексей Охрименко (IPONWEB)
Превышаем скоростные лимиты с Angular 2 / Алексей Охрименко (IPONWEB)Ontico
 
Превышаем скоростные лимиты с Angular 2
Превышаем скоростные лимиты с Angular 2Превышаем скоростные лимиты с Angular 2
Превышаем скоростные лимиты с Angular 2Oleksii Okhrymenko
 
Объять необъятное, или как использовать несколько MVVM фреймворков в одном XA...
Объять необъятное, или как использовать несколько MVVM фреймворков в одном XA...Объять необъятное, или как использовать несколько MVVM фреймворков в одном XA...
Объять необъятное, или как использовать несколько MVVM фреймворков в одном XA...Denis Tsvettsih
 
Moscow Python Conf 2016. Почему 100% покрытие это плохо?
Moscow Python Conf 2016. Почему 100% покрытие это плохо?Moscow Python Conf 2016. Почему 100% покрытие это плохо?
Moscow Python Conf 2016. Почему 100% покрытие это плохо?Ivan Tsyganov
 
Mera Dev Fest - Swift vs. Obj-C
Mera Dev Fest - Swift vs. Obj-CMera Dev Fest - Swift vs. Obj-C
Mera Dev Fest - Swift vs. Obj-CSergey Pronin
 
20100927 28 reqformalization-kuliamin
20100927 28 reqformalization-kuliamin20100927 28 reqformalization-kuliamin
20100927 28 reqformalization-kuliaminComputer Science Club
 
FPUG Dzyga presentation
FPUG Dzyga presentationFPUG Dzyga presentation
FPUG Dzyga presentationIvan Filimonov
 
Qualitative reconstruction of the camera and geometry of a scene, as a key to...
Qualitative reconstruction of the camera and geometry of a scene, as a key to...Qualitative reconstruction of the camera and geometry of a scene, as a key to...
Qualitative reconstruction of the camera and geometry of a scene, as a key to...Alexander Lavrov
 
Обзор программных средств Майкрософт для графики и визуализации: коммерческой...
Обзор программных средств Майкрософт для графики и визуализации: коммерческой...Обзор программных средств Майкрософт для графики и визуализации: коммерческой...
Обзор программных средств Майкрософт для графики и визуализации: коммерческой...Dmitri Soshnikov
 

Similar to Android Transition - плавные переходы на Android (20)

automation is iOS development
automation is iOS developmentautomation is iOS development
automation is iOS development
 
КРИ 2008. Проектирование игр: функциональный подход
КРИ 2008. Проектирование игр: функциональный подходКРИ 2008. Проектирование игр: функциональный подход
КРИ 2008. Проектирование игр: функциональный подход
 
Async
AsyncAsync
Async
 
Luxoft async.net
Luxoft async.netLuxoft async.net
Luxoft async.net
 
C# Web. Занятие 02.
C# Web. Занятие 02.C# Web. Занятие 02.
C# Web. Занятие 02.
 
Viper architecture
Viper architectureViper architecture
Viper architecture
 
Moxy. Как правильно пользоваться? / Юрий Шмаков (Arello Mobile)
Moxy. Как правильно пользоваться? / Юрий Шмаков (Arello Mobile)Moxy. Как правильно пользоваться? / Юрий Шмаков (Arello Mobile)
Moxy. Как правильно пользоваться? / Юрий Шмаков (Arello Mobile)
 
Статический анализ кода в DDD
Статический анализ кода в DDDСтатический анализ кода в DDD
Статический анализ кода в DDD
 
Правильный Xamarin для мобильных приложений (Microsoft DevCon School 2016) [R...
Правильный Xamarin для мобильных приложений (Microsoft DevCon School 2016) [R...Правильный Xamarin для мобильных приложений (Microsoft DevCon School 2016) [R...
Правильный Xamarin для мобильных приложений (Microsoft DevCon School 2016) [R...
 
Wild Async .NET world: AID Kit for boy-scouts
Wild Async .NET world: AID Kit for boy-scoutsWild Async .NET world: AID Kit for boy-scouts
Wild Async .NET world: AID Kit for boy-scouts
 
20100425 model based_testing_kuliamin_lectures01-03
20100425 model based_testing_kuliamin_lectures01-0320100425 model based_testing_kuliamin_lectures01-03
20100425 model based_testing_kuliamin_lectures01-03
 
Превышаем скоростные лимиты с Angular 2 / Алексей Охрименко (IPONWEB)
Превышаем скоростные лимиты с Angular 2 / Алексей Охрименко (IPONWEB)Превышаем скоростные лимиты с Angular 2 / Алексей Охрименко (IPONWEB)
Превышаем скоростные лимиты с Angular 2 / Алексей Охрименко (IPONWEB)
 
Превышаем скоростные лимиты с Angular 2
Превышаем скоростные лимиты с Angular 2Превышаем скоростные лимиты с Angular 2
Превышаем скоростные лимиты с Angular 2
 
Объять необъятное, или как использовать несколько MVVM фреймворков в одном XA...
Объять необъятное, или как использовать несколько MVVM фреймворков в одном XA...Объять необъятное, или как использовать несколько MVVM фреймворков в одном XA...
Объять необъятное, или как использовать несколько MVVM фреймворков в одном XA...
 
Moscow Python Conf 2016. Почему 100% покрытие это плохо?
Moscow Python Conf 2016. Почему 100% покрытие это плохо?Moscow Python Conf 2016. Почему 100% покрытие это плохо?
Moscow Python Conf 2016. Почему 100% покрытие это плохо?
 
Mera Dev Fest - Swift vs. Obj-C
Mera Dev Fest - Swift vs. Obj-CMera Dev Fest - Swift vs. Obj-C
Mera Dev Fest - Swift vs. Obj-C
 
20100927 28 reqformalization-kuliamin
20100927 28 reqformalization-kuliamin20100927 28 reqformalization-kuliamin
20100927 28 reqformalization-kuliamin
 
FPUG Dzyga presentation
FPUG Dzyga presentationFPUG Dzyga presentation
FPUG Dzyga presentation
 
Qualitative reconstruction of the camera and geometry of a scene, as a key to...
Qualitative reconstruction of the camera and geometry of a scene, as a key to...Qualitative reconstruction of the camera and geometry of a scene, as a key to...
Qualitative reconstruction of the camera and geometry of a scene, as a key to...
 
Обзор программных средств Майкрософт для графики и визуализации: коммерческой...
Обзор программных средств Майкрософт для графики и визуализации: коммерческой...Обзор программных средств Майкрософт для графики и визуализации: коммерческой...
Обзор программных средств Майкрософт для графики и визуализации: коммерческой...
 

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

  • 1. Анимация плавных переходов на Android Артем Шевченко Киев 2018 https://github.com/shevart/AndroidTransitions shevartsoft@gmail.com
  • 3. Transition API и разработчик
  • 6. Анимация переходов теория Или причины зачем разработчику напрягаться?
  • 10. Дизайнерское исследование 🧠 Мозг: Любит упрощать (оптимизировать) Искать взаимосвязь
  • 11. Дизайнерское исследование Транзишены: Простота и однозначность Правильная физика 🏄
  • 13. Дизайнерское исследование Не навреди Нет лагам Нет усложнению Не мешает работе с приложением $
  • 15. Scene
  • 17. Scene - основные понятия Не с документации! :)
  • 18. sceneRoot Scene - основа ViewGroup Где делаем изменения? Холст для Scene Обязательное поле
  • 19. contentView Scene - основа Какие делаем изменения? Что рисуем на холсте? Необязательное поле Это абстракция
  • 20. contentView Scene - основа Это может быть: View xml Разметка Ничего Ну почти ничего
  • 21. Enter/Exit-Actions Scene - основа Callback’s для enter() и exit() Можем здесь сделать изменения в sceneRoot Необязательны Можем изменять contentView только здесь Runnable
  • 22. Применение Scene Scene - основа Это вызов метода enter() Изменение contentView внутри sceneRoot
  • 23. Выход/Отмена Scene Scene - основа Это вызов метода exit() Под капотом - вызов Exit Action * Бонус - вызывается автоматически
  • 24. Scene - еще чуток теории
  • 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. 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. 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. 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. 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. 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
  • 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>
  • 36. Scene Применяем - код val scene = Scene.getSceneForLayout(cardView, R.layout.layout_scene_content, context = this) Создаем Scene Применяем Scene scene.enter()
  • 40. Scene Как это работает? Scene A Scene B Transition Scene
  • 43. Хм, это не та анимация!?
  • 44. Scene TransitionManager - анимация по умолчанию Удобно - работает с коробки Можно указать собственную анимацию
  • 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. 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. 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. 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. 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. 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)
  • 56. Что делает Transition? Transition - основа Scene A Scene B View До View После Transition Анимирует переход
  • 57. Принцип работы Transition - основа Scene A Scene B Сохраняем параметры SceneA Сохраняем параметры SceneB Только «свои» параметры Transition Создание анимации изменения
  • 58. 2 главные задачи Transition - основа Сохранение параметров View в SceneA и SceneB Создание анимации для изменения View
  • 60. Transition - теория Как Transition сохраняет параметры View? void captureStartValues(@NonNull TransitionValues transitionValues) void captureEndValues(@NonNull TransitionValues transitionValues) А что такое TransitionValues?
  • 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. Transition - теория TransitionValues - пример Есть кнопка 100% Alpha на SceneA 0% Alpha на SceneB
  • 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. Transition - теория TransitionValues - пример void captureStartValues(@NonNull TransitionValues transitionValues) { float alpha = transitionValues.view.getAlpha(); transitionValues.values.put("ButtonAlpha", alpha); }
  • 65. Transition - теория Transition - создание анимации Transition.java @Nullable public Animator createAnimator(@NonNull ViewGroup sceneRoot, @Nullable TransitionValues startValues, @Nullable TransitionValues endValues) { return null; }
  • 67. Transition - теория Transition - targets Как Transition определяет какую View анимировать а какую нет? Плавно появляется только картинка Приезжает только текст
  • 68. Transition - теория Transition - targets По умолчанию: Targets - все View Можно указывать конкретные Target’s Удобно
  • 69. Transition - теория Target-ом может быть: TransitionName ID - View View Class
  • 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. Transition - теория Как добавить target: <changeBounds> <targets> <target android:targetId="@+id/ivSmallAvatar" /> </targets> </changeBounds> Код val changeBounds = ChangeBounds() changeBounds.addTarget(R.id.ivBigAvatar) XML
  • 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. Transition - теория Неочевидно! if (exclude) { list = ArrayListManager.add(list, target); } else { list = ArrayListManager.remove(list, target); } Transition.java Роберт Мартин exclude (исключить) == true -> add?
  • 74. Transition - теория Как удалить target: val changeBounds = ChangeBounds() changeBounds.removeTarget(R.id.ivBigAvatar) Код
  • 75. Transition - теория Резюме Target’s Точная настройка анимации Экономия времени на поиск совпадений
  • 76. Transition - теория Базовые параметры анимации Задержка Продолжительность Интерполятор
  • 77. Transition - теория Задержка анимации val transition = Fade() transition.startDelay = 200L Код XML <fade android:startDelay="2000"/>
  • 78. Transition - теория Продолжительность анимации val transition = Fade() transition.duration = 200L Код XML <fade android:duration="2000"/>
  • 79. Transition - теория Interpolator для анимации val transition = Fade() transition.interpolator = BounceInterpolator() Код XML <fade android:interpolator="@android:interpolator/bounce"/>
  • 81. Transition - теория За ходом анимации можно следить!
  • 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); }
  • 84. Transition - базовый набор Fade Slide Explode ChangeBounds ChangeTransform ArcMotion* PatternPathMotion* ChangeImageTransform ChangeClipTransform ChangeScroll AutoTransition TransitionSet *Это не совсем Transition
  • 85. Transition - базовый набор Fade Код Плавно показывает или скрывает View Fade()
  • 86. Transition - базовый набор Slide Плавно перемещает View за пределы экрана Плавно перемещает View на экран Направление движения устанавливает Gravity Код Slide()
  • 87. Transition - базовый набор Explode Двигает View в сторону от центра или указанного эпицентра Код Explode()
  • 88. Transition - базовый набор Explode - пример
  • 89. Transition - базовый набор Explode Есть набор View для Explode Все они одинаковые Эпицентром будет верхний левый квадрат
  • 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. 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. 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. Transition - базовый набор Explode val explode = Explode() explode.epicenterCallback = createEpicenterCallback() Создаем Explode с EpicenterCallback
  • 94. Transition - базовый набор Explode
  • 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. Transition - базовый набор Explode Получилось!
  • 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. 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) }
  • 101. Transition - базовый набор ChangeBounds Анимирует изменение местоположения View Анимирует изменение размеров View Код ChangeBounds()
  • 102. Transition - базовый набор ArcMotion Делает перемещение View по окружности Работает в связке с ChangeBounds Код ChangeBounds().apply { setPathMotion(ArcMotion()) } Настройка угла окружности
  • 103. Transition - базовый набор PatternPathMotion Перемещение View по кастомному пути Работает в связке с ChangeBounds
  • 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. 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) } Указываем свой путь движения
  • 107. Transition - базовый набор ChangeClipBounds Анимирует изменение clipBounds у View Код ChangeClipBounds()
  • 108. Transition - базовый набор ChangeImageTransform Анимирует матричное изменение внутри ImageView Код ChangeImageTransform() + ChangeBounds
  • 109. Transition - базовый набор ChangeTransform Анимирует изменение размера View и поворот Код ChangeTransform() +ChangeBounds
  • 110. Transition - базовый набор ChangeScroll Анимирует изменение scrollX и scrollY Код ChangeScroll()
  • 111. Transition - базовый набор AutoTransition Анимиция появления/исчезновения Код AutoTransition() Анимиция размеров и местоположения View Fade + ChangeBounds
  • 112. Transition - базовый набор TransitionSet Позволяет группировать Transitions вместе Код TransitionSet() Сам по себе не создает анимацию
  • 113. Transition - базовый набор TransitionSet XML <?xml version="1.0" encoding="utf-8"?> <transitionSet> <changeBounds/> // … </transitionSet>
  • 114. Transition - базовый набор TransitionSet Порядок проигрывания анимации public static final int ORDERING_TOGETHER = 0; public static final int ORDERING_SEQUENTIAL = 1; public TransitionSet setOrdering(int ordering)
  • 117. TransitionManager Основные методы setTransition() - сохраняет пару Scene-Transition transitionTo() - применяет Scene с указанными или по умолчанию Transition-ом endTransition() - останавливет все текущие и отложенные Transition’s для View static go() - применяет Scene + Transition (default или custom) static beginDelayedTransition() - применяет Scene + Transition (default или custom)
  • 119. flTestContent.visibility = View.INVISIBLE val transition = AutoTransition() transition.doOnEnd { showWithAlpha() } TransitionManager.beginDelayedTransition(cardView, transition) flTestContent.setHeight(height = 500) // your height TransitionManager Как это сделать просто
  • 120. flTestContent.visibility = View.INVISIBLE val transition = AutoTransition() transition.doOnEnd { showWithAlpha() } TransitionManager.beginDelayedTransition(cardView, transition) flTestContent.setHeight(height = 500) // your height TransitionManager Как это сделать просто Скрываем содержимое cardView
  • 121. flTestContent.visibility = View.INVISIBLE val transition = AutoTransition() transition.doOnEnd { showWithAlpha() } TransitionManager.beginDelayedTransition(cardView, transition) flTestContent.setHeight(height = 500) // your height TransitionManager Как это сделать просто Создаем наш Transition
  • 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. flTestContent.visibility = View.INVISIBLE val transition = AutoTransition() transition.doOnEnd { showWithAlpha() } TransitionManager.beginDelayedTransition(cardView, transition) flTestContent.setHeight(height = 500) // your height TransitionManager Как это сделать просто Готовимся анимировать изменения
  • 124. flTestContent.visibility = View.INVISIBLE val transition = AutoTransition() transition.doOnEnd { showWithAlpha() } TransitionManager.beginDelayedTransition(cardView, transition) flTestContent.setHeight(height = 500) // your height TransitionManager Это действительно просто Делаем изменения
  • 129. Transitions между экранами 2 вида анимаций Content transitions или Анимация содержимого Shared elements
 или Общие элементы
  • 132. Transitions между экранами Итак, у нас есть… Screen B Screen A 2 экрана 4Анимации
  • 133. Transitions между экранами 4 анимации Screen BScreen A ScreenA - уходит - Exit Transition ScreenB - появляеться - Enter Transition Переход ScreenA - ScreenB Возвращаемся ScreenB - ScreenA ScreenA - снова приходит - Reenter Transition ScreenB - закрывается - Return Transition
  • 134. Transitions между экранами 4 анимации - в профиль Screen BScreen A С ScreenA открыли ScreenB Exit Transition Enter Transition Screen B Screen A Закрыли ScreenB вернулись на ScreenA Return Transition Reenter Transition
  • 135. Transitions между экранами Плюсы Content transitions Transitions Targets Каждую View можно анимировать отдельно
  • 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. 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. 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. 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. Content transitions - Activity Результат
  • 142. Что-то оно не очень…
  • 143. Content transitions - Activity Slide vs AutoTransition
  • 144. Content transitions - Activity Код ActivityA override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // set content transitions setContentTransition() setContentView(R.layout.activity_a) // ... }
  • 145. Content transitions - Activity Код ActivityA - перед вызовом setContentView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // set content transitions setContentTransition() setContentView(R.layout.activity_a) // ... }
  • 146. Content transitions - Activity Код ActivityA - запроси фичу, укажи transition private fun setContentTransition() { window.apply { requestFeature(Window.FEATURE_CONTENT_TRANSITIONS) exitTransition = Explode() // set other transitions } }
  • 147. Content transitions - Activity Код ActivityB - аналогично private fun setContentTransition() { window.apply { requestFeature(Window.FEATURE_CONTENT_TRANSITIONS) enterTransition = Fade() } }
  • 148. Content transitions - Activity Код Запуск ActivityB - важный момент startActivity(intent)startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle())
  • 149. Content transitions - Activity Результат
  • 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. Content transitions - Fragments Transitions для FragmentA val fragment = FragmentA() fragment.exitTransition = // transition fragment.reenterTransition = // transition Бонус Перед каждой транзакцией можно менять transitions!
  • 153. Content transitions - Activity Результат
  • 156. Shared Elements - теория Почти тоже самое Screen B Screen A 2 экрана 2 Анимации у Fragments 1 Target 4 Анимации у Activity
  • 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. Shared Elements - теория 4 анимации - Activity ScreenB - появляеться - Shared Elements Enter Transition Достаточно указать одну:
  • 159. Shared Elements - теория 2 анимации - Fragments SharedElementsEnterTransition - при открытии фрагмента Переход ScreenA - ScreenB SharedElementsReturnTransition - при удалении (pop) фрагмента из стека
  • 160. Shared Elements - теория 2 анимации - Fragments SharedElementsEnterTransition - FragmentB Можно указать только 1
  • 161. Shared Elements - теория 1 target TransitionName
  • 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. 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. 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. Shared Elements - Activity Готовим ActivityB В ActivityB yourView.transitionName = «transitionName» // your transitionName
  • 167.
  • 169. Shared Elements - Fragments Fragment В транзакции supportFragmentManager .beginTransaction() .replace(R.id.flFragmentsContainer, FragmentB()) .addToBackStack(TAG) .addSharedElement(view, view.transitionName) .commit()
  • 170. Shared Elements - Fragments Fragment Добавляем sharedElement supportFragmentManager .beginTransaction() .replace(R.id.flFragmentsContainer, FragmentB()) .addToBackStack(TAG) .addSharedElement(view, view.transitionName) .commit()
  • 171. Shared Elements - Fragments Fragment Вернутся красиво supportFragmentManager .beginTransaction() .replace(R.id.flFragmentsContainer, FragmentB()) .addToBackStack(TAG) .addSharedElement(view, view.transitionName) .commit()
  • 172. Shared Elements - Fragments FragmentB Понадобится Transition override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) sharedElementEnterTransition = TransitionInflater .from(context) .inflateTransition(android.R.transition.move) }
  • 173. Идем дальше Анимация переходов Activity Анимация переходов Fragments Разбор кейсов и проблем
  • 175. Дополнение Transitions + AsyncWorld = postponeEnterTransition() viewWithLongLoading.doOnPreDraw { startPostponedEnterTransition() }
  • 176. Дополнение Почему не работают sharedElements? TransitionName - либо не указано либо дубликаты View measure - View еще не знает свои размеры/положение SharedElementsEnterTransition - null/не тот Transition Keyboard - сначала закрой потом анимируй SharedElementsReturnTransition - null - не сработает переход обратно