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

В уроке мы рассмотрим следующие способы анимации:
View animation - это основной способ анимации View, который был до Android 2.3. У View animation есть небольшой набор изменений, которые мы можем сделать с помощью анимации: alpha, rotate, scale, translate. Главный минус данного подхода состоит в том, что во время анимации не меняется состояние самого View а меняется лишь отображение представления. Представим, что мы сейчас выполняем какую-то анимацию, например, сдвигаем кнопку. И если в процессе анимации нажать на изначальное местоположение кнопки, то это будет расцениваться как клик по кнопке, хотя, визуально ее там нет. Данный подход устарел, не будем рассматривать его детально.
Drawable animation - это способ покадровой анимации, где каждый кадр, это Drawable. Описание как правило делается в xml файле. Такая анимация довольно сложна, т.к. требует загрузки Drawable на каждый из этапов анимации. Такую анимацию используют в редких случаях, например, когда очень сложно описать происходящее на экране другими способами.
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="true">
<item android:drawable="@drawable/rocket_thrust1" android:duration="200" />
<item android:drawable="@drawable/rocket_thrust2" android:duration="200" />
<item android:drawable="@drawable/rocket_thrust3" android:duration="200" />
</animation-list>
ValueAnimator появился в Android 3.0, это базовый движок для анимации. С помощью него можно делать практические любые преобразования. Отличительной особенностью является то, что он никак не привязан к View. Для работы с ValueAnimator необходимо указать: начальное значение, конечное значение и Listener который будет менять какой-либо параметр View.
Рассмотрим анимацию на примере появляющегося View, который движется сверху вниз экрана:
View должно поменять свою прозрачность от 0 до 1:ValueAnimator.ofFloat(0f, 1f)
View должно сдвинуться из крайней верхней точки в крайную нижную точку:ValueAnimator.ofFloat(0f, (root.height - circle.height).toFloat())
Аниматоры можно комбинировать и управлять ими одновременно:
val alphaAnimator = ValueAnimator.ofFloat(0f, 1f)
alphaAnimator.addUpdateListener { animation -> circle.alpha = animation.animatedValue as Float }
val yAnimator = ValueAnimator.ofFloat(0f, (root.height - circle.height).toFloat())
yAnimator.addUpdateListener { circle.y = yAnimator.animatedValue as Float }
val set = AnimatorSet()
set.playTogether(alphaAnimator, yAnimator)
set.duration = ANIMATION_DURATION
set.start()

Давайте рассмотрим как работает движок аниматора. Пускай, мы хотим анимировать целое число от 0 до 255.
ValueAnimator.ofInt(0, 255)
Время в Animator представляется как значение от 0 до 1. Время обновления экрана в Android это 16ms. Допустим, анимация длится 167 мс, тогда, для отрисовки нам понадобится 10 фреймов. Для примера рассмотрим что происходит во время отрисовки 5го фрейма:
Cначала значение 0.5 попадает в TimeInterpolator, это функция которая показывает как должна изменяться скорость анимации. Можно, например, сделать что бы анимация разгонялась со временем или наоборот тормозила. Воспользуемся AccelerateInterpolator:
f(x) = x^2
0.5^2 = 0.25
Рассчитанное значение подается на вход TypeEvaluator. TypeEvaluator определяет как должен меняться объект в процессе анимации относительно времени. В простых случаях это разница конечного и начального значения умноженная на время:
(x2 - x1) * t
(255-0) * 0.25 = 63.75
Получившееся значение записывается в Animator и передается в Listener.
Интерполяторов существует множество, рассмотрим самые базовые:
AccelerateInterpolator. Определяет функцию с увеличивающейся скоростью. Он, как правило, должен использоваться для элементов, которые собираются пропасть с экрана. Элемент должен большую часть времени элемент провести на экране, чтобы пользователь его точно заметил.
DecelerateInterpolator. Определяет функцию с уменьшающейся скоростью. Как правило, используется для элементов, которые следует показать на экране.
AccelerateDecelerateInterpolator. Определяет функцию, скорость которой сначала увеличивается, а потом снижается. Следует использовать для анимации View, которое будет находится в приделах экрана.

В Android 5 появились новые интерполяторы, аналогичные перечисленным выше. Считается, что они работают более плавно и естественно: FastOutLinearInInterpolator, LinearOutSlowInInterpolator, FastOutSlowInInterpolator.
Вернемся к TypeEvaluator. Как правило не приходится задавать свой TypeEvaluator, потому-что обычно это мы изменяем достаточно простой величины. Но, если требуется выполнить какие-то не простые манипуляции, то следует определить свой TypeEvaluator. Например, если мы хотим сделать переливание цветов. Цвет задается в формате ARGB, где на каждый канал отведено по 2 байта. Если мы просто будет анимировать цвет как обычный int, то цвета будут меняться неочевидным образом.
ObjectAnimator является расширением ValueAnimator. Все свойства ValueAnimator также применимы и к ObjectAnimator. Главным отличием является то, что нам нет необходимости задавать Listener, вместо этого в ObjectAnimator представлено понятие Property, которое отвечает за изменяемый параметр.
val yAnimator = ObjectAnimator.ofFloat(circle, Y, circle.y, root.height - circle.height)
Давайте рассмотрим, что такое Property:
public static final Property<View, Float> Y = new FloatProperty<View>("y") {
@Override
public void setValue(View object, float value) {
object.setY(value);
}
@Override
public Float get(View object) {
return object.getY();
}
};
Видно, что Property по сути делает ту же работу, которую мы обычно делали вручную при использовании ValueAnimator.
Property можно задавать двумя способами:
Property:ObjectAnimator.ofFloat(circle, View.Y, circle.y, root.height - circle.height)
ObjectAnimator.ofFloat(circle, "y", circle.y, root.height - circle.height)
В случае, если мы используем строку, то доступ к анимируемым полям будет получаться при помощи reflection, что может несколько замедлить работу. А так же, обязательным условием является наличие методов setX и getX.
View уже содержат в себе базовый набор Property:
ViewPorpertyAnimator также работает на основе ValueAnimator. Главный плюс ViewPorpertyAnimator состоит в том, что он предоставляет удобный API для анимации View. В случае анимирования нескольких значений, он может быть незначительно быстрее ObjectAnimator. Минусом является то, что нам нельзя анимировать какие-то свои параметры, можно использовать только те, которые есть в стандартном наборе.
val yAnimator = circle.animate().x((root.height - circle.height).toFloat()).setDuration(ANIMATION_DURATION).setInterpolator(interpolator)
#### Какой аниматор выбрать?
Мы рассмотрели несколько способов, с помощью которых можно анимировать View, давайте определимся с тем, когда следует использовать каждый из предложенных вариантов:
ViewAnimation. Устаревший. Лучше не использовать;DrawableAnimation. Использовать только в очень сложных случаях;ValueAnimator. Стоит использовать только в тех случаях, когда нам не удается написать Property;ObjectAnimator. Стоит использовать по возможности всегда;ViewPropertyAnimator. Стоит использовать для простых случаев.#### Завершение анимации
Для того, что завершить анимацию, раньше чем ее завершит аниматор следует использовать следующие способы:
view.clearAnimation(). Для View animation;animator.cancel(). Для ValueAnimator и ObjectAnimator;view.animate().cancel(). Для ViewPropertyAnimator.В случае вызова неверного метода, анимация не будет остановлена.Лучше всегда сохранять ссылку на аниматор, для того, что бы можно было остановить анимацию в любой момент. Также, следует останавливать анимации в методах:
Activity.onStop();Fragment.onStop();View.onDetachFromWidow().Это следует делать во избежание утечек памяти.
LayoutTransition(флаг animateLayoutChanges) работает на основе ValueAnimator. Позволяет при изменениях внутри дочерних View анимировать родительский View.
Подходит для простых случаев:
root.layoutTransition = LayoutTransition()

Анимация запускается только для последнего действия. Например, если в одной иерархии мы захотим и добавить и удалить View, то анимация запустится только для последнего действия, то есть для удаления View.
Transition Framework вводит такие понятия, как Transition, который включает в себя целый класс анимаций. Transition Framework анимирует всю иерархию внутри контейнера, не только ближайшие вложенные элементы, а все, которые находятся в заданном родителе. Данный фрейморк работает только для Android 4.4 и выше.
Для того, что бы задать анимацию, достаточно описать какие эффекты мы хотим применять к изменениям лейаута, а дальше выполнить метод beginDelayedTransition. Далее, все изменения родителя будут анимрованы указанным способом.
Добиться анимации как в предыдущем случае можно при помощи следующего кода:
val transitionSet = TransitionSet().addTransition(Fade())
TransitionManager.beginDelayedTransition(root, transitionSet)
circle2.visibility = VISIBLE
Кроме базовых анимаций есть несколько необычных, например Slide. Этот способ анимации заставляет View “прилететь” в заданную позицию:
val transitionSet = TransitionSet().addTransition(Fade()).addTransition(new Slide()
TransitionManager.beginDelayedTransition(root, transitionSet)
circle2.visibility = VISIBLE

Кроме заданного набора анимация, можно реализовать свои анимации. На, следует помнить, что на Transition Framework накладывает ограничения ограничение на минимальную версию Android.
Что лучше использовать LayoutTransition или Transition Framework? LayoutTransition стоит использовать для простых иерархий, где анимируется не большое количество элементов и/или для старых версий Android. Transition Framework следует использовать для более сложных случаях и/или для новых версий Android.
Мы живем в реальном мире, который подчиняется физическим законам и взаимодействую с интерфейсом, человеку хочется что бы интерфейс реагировал также привычным образом. Для это в Android добавлена библиотека Dynamic animation, или также известна как Physic-based animation.
Сравните как выглядят анимации. Слева обычная анимация, справа Physic-based animation:

На текущий момент библиотека включает в себя два вида анимаций, FlingAnimation и SpringAnimation.
FlingAnimation. Предназначена для тех случаев, когда пользователь своими жестами инициирует какую-то анимацию, например свайп: когда пользователь поднимает палец, элемент должен сдвинуться примерно с той же самой скоростью, с которой пользователь двигал палец.
SpringAnimation. Анимация отмены действия или возврата к начальному состоянию. Чем-то похожа на пружину.
Пример использования Dynamic Animation можно посмотреть тут
Кроме анимаций, которые могут применяться к любым View, есть анимации, которые предназначены для каких-то определенных ситуаций. Например:
ItemAnimator. Этот аниматор работает только в RecyclerViewAnimatedVectorDrawable. Аниматор для работы с векторными изображениями;Activity Transition. Способ анимации между Activity;Fragment Transition. Способ анимации между фрагментами.Android постоянно обновляется и вместе с тем добавляются новые способы анимации объектов. С умом подойдите в выбору способа анимации и следите за обновлениями.