Базовые анимации. Взято отсюда
Всё что нужно сделать это создать xml со ссылками на каждый кадр:
<?xml version="1.0" encoding="utf-8"?>
<animation-list
xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="true">
<item android:drawable="@drawable/explosion_1" android:duration="15" />
<item android:drawable="@drawable/explosion_2" android:duration="15" />
<item android:drawable="@drawable/explosion_3" android:duration="15" />
...
</animation-list>
И запустить анимацию:
val animation = image.drawable as AnimationDrawable
animation.start()
Применение:
Сложные по графике анимации, небольших размеров и подготовленные во внешнем редакторе.
Достоинства:
Возможность достичь любой сложности эффектов
Недостатки:
Большое потребление ресурсов и, как следствие, довольно затратный импорт в приложение с возможностью получить OutOfMemory. Если по каким-то причинам вам нужно показывать большое количество кадров, то придётся писать свою реализацию с постепенной подгрузкой изображений в память. Но если так пришлось делать, возможно проще прибегнуть к видео?
Если нам нужно всего-лишь передвинуть что-нибудь на несколько пикселей в сторону или изменить прозрачность, чтобы не плодить миллион очень похожих друг на друга кадров на помощь приходит Animator. Фактически с помощью него можно анимировать любое свойство любых объектов.
Базовый абстрактный класс называется Animator, у него есть несколько наследников, нам важны:
Анимацию выше можно описать как в коде:
val animationX = ObjectAnimator.ofFloat(card, "scaleX", 1F)
val animationY = ObjectAnimator.ofFloat(card, "scaleY", 1F)
val set = AnimatorSet()
set.play(animationX)
.with(animationY)
set.duration = DURATION
set.interpolator = DecelerateInterpolator()
set.start()
так и в XML (animator/open_animator.xml):
<?xml version="1.0" encoding="utf-8"?>
<set
xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering="together"
>
<objectAnimator
android:duration="250"
android:propertyName="scaleX"
android:valueTo="1"
android:valueType="floatType"
android:interpolator="@android:anim/decelerate_interpolator"/>
<objectAnimator
android:duration="250"
android:propertyName="scaleY"
android:valueTo="1"
android:valueType="floatType"
android:interpolator="@android:anim/decelerate_interpolator"/>
</set>
val set = AnimatorInflater.loadAnimator(context, R.animator.open_animator) as AnimatorSet
set.setTarget(card)
set.start()
Так-же есть возможность описать нашу анимацию переходов между стейтами View, что соответсвенно, с лёгкостью позволит создать анимированные переходы между стейтами у любых View. Описанная в XML анимация будет автоматически запущена при смене состояния View.
animator/state_animator.xml
<?xml version="1.0" encoding="utf-8"?>
<selector
xmlns:android="http://schemas.android.com/apk/res/android"
>
<item android:state_enabled="true">
<set android:ordering="together">
<objectAnimator
android:duration="250"
android:interpolator="@android:anim/decelerate_interpolator"
android:propertyName="scaleX"
android:valueTo="1"
android:valueType="floatType"/>
<objectAnimator
android:duration="250"
android:interpolator="@android:anim/decelerate_interpolator"
android:propertyName="scaleY"
android:valueTo="1"
android:valueType="floatType"/>
</set>
</item>
<item
android:state_enabled="false">
<set android:ordering="together">
<objectAnimator
android:duration="250"
android:interpolator="@android:anim/accelerate_interpolator"
android:propertyName="scaleX"
android:valueTo="0"
android:valueType="floatType"/>
<objectAnimator
android:duration="250"
android:interpolator="@android:anim/accelerate_interpolator"
android:propertyName="scaleY"
android:valueTo="0"
android:valueType="floatType"/>
</set>
</item>
</selector>
<View
...
android:stateListAnimator="@animator/state_animator">
</View>
Применение:
Анимация View объектов и любых их параметров
Анимация любых других параметров
Достоинства:
Абсолютно универсален
Недостатки:
В некоторой степени требовательны к ресурсам
До появления Animator в Android были только Animations. Основной недостаток которых был в том что они анимировали только представление вида и никак на самом деле не изменяли его свойства. Поэтому если хочется анимировать перемещение какого-либо элемента, то дополнительно по окончанию анимации нужно изменить ещё его свойства. Такой подход так или иначе не очень удобен, если вам нужна чуть более сложная анимация или нужно отлавливать нажатия в момент анимации.
Анимацию можно запустить как в коде:
val anim = ScaleAnimation(0F, 1F, 0F, 1F,
0F, card.measuredHeight.toFloat())
anim.duration = DURATION
anim.interpolator = DecelerateInterpolator()
anim.fillAfter = true
card.startAnimation(anim)
так и в XML (обратите внимание, что синтаксис отличается от xml для Animator):
anim/open_animation.xml
<?xml version="1.0" encoding="utf-8"?>
<scale
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="250"
android:fillAfter="true"
android:fromXScale="0"
android:fromYScale="0"
android:interpolator="@android:anim/decelerate_interpolator"
android:pivotX="0"
android:pivotY="100%"
android:toXScale="1"
android:toYScale="1"/>
val animation = AnimationUtils.loadAnimation(context, R.anim.open_animation)
card.startAnimation(animation)
Применение:
Там, где API не позволяет использовать Animator.
Достоинства:
Отсутсвуют
Недостатки:
Устаревший API, меняет только представление вида.
На мой взгляд самая интересная часть в Android анимациях. Можно относительно малыми силами добиваться сложных и интересных эффектов. Трансформации иконок в Android сделаны именно так.
VectorDrawable состоит из Path и Group элементов. Создание анимации сводится к тому, чтобы прописать движение к этим элементам. Андроид на картинке выше, в коде будет выглядеть так:
Чтобы не писать XML вручную можно воспользоваться онлайн инструментом.
Начиная с API 25 векторные анимации отрисовываются в RenderThread, поэтому, даже если мы загрузим чем-то наш UI Thread (но мы же никогда так не делаем, да?), анимации всё равно будут проигрываться плавно.
Применение:
Иконки
Анимационные эффекты
Достоинства:
Производительность
Недостатки:
Нет возможности вручную управлять точкой анимации во времени (т.е. фактически отсутствует какой-либо метод, вроде setCurrentTime)
Все примеры можно посмотреть и изучить здесь.
Используя способ анимации свойств объекта сделаем, чтобы наше окно с детальной информацией всплывало при клике на маркер и скрывалось при клике на карту:
Создайте в каталоге res каталог animator (New -> Android Resource Directory)
В этом каталоге создайте два файла анимации, для открытия и закрытия окна:
Моё окно высотой
100dpсо скруглёнными углами16dp. Чтобы его не было видно в неактивном состоянии в разметке задана нижняя граница-100dp(по размеру элемента)Правильнее было бы нижнюю границу и менять при анимации, но похоже класс Animator понимает не все свойства
open_animator.xml:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="300"
android:propertyName="translationY"
android:valueTo="-84dp"
android:valueType="floatType"
android:interpolator="@android:anim/decelerate_interpolator"/>
</set>
Обратите внимание, окно поднимается не на всю высоту, а за вычетом размера радиуса, чтобы нижнее скругление не было видно. Меняется свойство translationY
и close_animator.xml:
<?xml version="1.0" encoding="utf-8"?>
<set
xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="300"
android:propertyName="translationY"
android:valueTo="84dp"
android:valueType="floatType"
android:interpolator="@android:anim/decelerate_interpolator"/>
</set>
В классе окна включить анимацию открытия при тапе по маркеру и анимацию закрытия при тапе по карте или по кнопке "закрыть" в окне:
Меняем обработчик тапа для маркера:
setTapListener { mapObject, point ->
// тут предыдущий код с заполнением окна данными выбранного автомобиля
// запоминаем состояние окна в переменной класса
isOpen = true
// извлекаем нужный аниматор
val set = AnimatorInflater.loadAnimator(this, R.animator.open_animator) as AnimatorSet
// назначаем целевой визуальный элемент
set.setTarget(detailInfo)
// и запускаем анимацию
set.start()
true
}
Обработчика тапа по карте у нас ещё не было, создадим:
private val inputListener: InputListener = object : InputListener {
override fun onMapTap(
p0: com.yandex.mapkit.map.Map,
p1: Point)
{
closePopup()
}
override fun onMapLongTap(
p0: com.yandex.mapkit.map.Map,
p1: Point)
{
// обработка длинного тапа нам пока не нужна
}
}
// в методе onCreate цепляем обработчик к карте
mapView.map.addInputListener(inputListener)
Метод closePopup вынесен отдельно, т.к. в контексте объекта мы не видим экземпляр активности (this)
private fun closePopup(){
if(isOpen) {
isOpen = false
val set = AnimatorInflater.loadAnimator(
this,
R.animator.close_animator
) as AnimatorSet
set.setTarget(detailInfo)
set.start()
}
}