Система анимации свойств — это надежная платформа, позволяющая анимировать практически все. Вы можете определить анимацию для изменения любого свойства объекта с течением времени, независимо от того, отображается он на экране или нет. Анимация свойства изменяет значение свойства (поля в объекте) в течение определенного периода времени. Чтобы анимировать что-либо, вы указываете свойство объекта, которое вы хотите анимировать, например положение объекта на экране, продолжительность его анимации и значения, между которыми вы хотите анимировать.
Система анимации свойств позволяет определить следующие характеристики анимации:
- Продолжительность: вы можете указать продолжительность анимации. Длина по умолчанию составляет 300 мс.
- Интерполяция времени: вы можете указать, как значения свойства будут рассчитываться в зависимости от текущего прошедшего времени анимации.
- Количество повторений и поведение: вы можете указать, будет ли повторяться анимация по достижении конца продолжительности и сколько раз следует повторять анимацию. Вы также можете указать, хотите ли вы, чтобы анимация воспроизводилась в обратном порядке. Если установить реверс, анимация воспроизводится вперед, а затем назад несколько раз, пока не будет достигнуто необходимое количество повторов.
- Наборы аниматоров: анимацию можно группировать в логические наборы, которые воспроизводятся вместе, последовательно или после заданных задержек.
- Задержка обновления кадра: вы можете указать, как часто следует обновлять кадры анимации. По умолчанию установлено обновление каждые 10 мс, но скорость, с которой ваше приложение может обновлять кадры, в конечном итоге зависит от того, насколько загружена система в целом и насколько быстро система может обслуживать базовый таймер.
Полный пример анимации свойств см. в классе ChangeColor
в образце CustomTransition на GitHub.
Как работает анимация свойств
Сначала давайте рассмотрим, как работает анимация, на простом примере. На рисунке 1 изображен гипотетический объект, анимированный с помощью свойства x
, которое представляет его горизонтальное положение на экране. Длительность анимации установлена на 40 мс, а расстояние перемещения — 40 пикселей. Каждые 10 мс (частота обновления кадра по умолчанию) объект перемещается по горизонтали на 10 пикселей. По истечении 40 мс анимация останавливается, и объект заканчивается в горизонтальном положении 40. Это пример анимации с линейной интерполяцией, то есть объект движется с постоянной скоростью.
Вы также можете указать анимацию с нелинейной интерполяцией. На рисунке 2 показан гипотетический объект, который ускоряется в начале анимации и замедляется в конце анимации. Объект по-прежнему перемещается на 40 пикселей за 40 мс, но нелинейно. Вначале эта анимация ускоряется до середины, затем замедляется от середины до конца анимации. Как показано на рисунке 2, пройденное расстояние в начале и конце анимации меньше, чем в середине.
Давайте подробно рассмотрим, как важные компоненты системы анимации свойств будут рассчитывать анимацию, подобную показанной выше. На рисунке 3 показано, как основные классы работают друг с другом.
Объект ValueAnimator
отслеживает время анимации, например, как долго выполняется анимация, а также текущее значение свойства, которое она анимирует.
ValueAnimator
инкапсулирует TimeInterpolator
, который определяет интерполяцию анимации, и TypeEvaluator
, который определяет, как вычислять значения для анимируемого свойства. Например, на рисунке 2 используемый TimeInterpolator
будет AccelerateDecelerateInterpolator
, а TypeEvaluator
— IntEvaluator
.
Чтобы запустить анимацию, создайте ValueAnimator
и задайте ему начальное и конечное значения свойства, которое вы хотите анимировать, а также продолжительность анимации. Когда вы вызываете start()
начинается анимация. В течение всей анимации ValueAnimator
вычисляет затраченную долю от 0 до 1 в зависимости от продолжительности анимации и количества прошедшего времени. Прошедшая дробь представляет собой процент времени, в течение которого анимация завершилась: 0 означает 0%, а 1 означает 100%. Например, на рисунке 1 затраченная доля при t = 10 мс будет равна 0,25, поскольку общая продолжительность равна t = 40 мс.
Когда ValueAnimator
завершает вычисление прошедшей дроби, он вызывает TimeInterpolator
, который установлен в данный момент, для вычисления интерполированной дроби . Интерполированная дробь отображает прошедшую дробь в новую дробь, которая учитывает установленную временную интерполяцию. Например, на рисунке 2, поскольку анимация медленно ускоряется, интерполированная доля, около 0,15, меньше затраченной доли, 0,25, при t = 10 мс. На рисунке 1 интерполированная дробь всегда совпадает с прошедшей дробью.
При вычислении интерполированной дроби ValueAnimator
вызывает соответствующий TypeEvaluator
для вычисления значения свойства, которое вы анимируете, на основе интерполированной дроби, начального значения и конечного значения анимации. Например, на рисунке 2 интерполированная дробь составляла 0,15 при t = 10 мс, поэтому значение свойства в этот момент будет 0,15 × (40 – 0) или 6.
Чем анимация свойств отличается от анимации просмотра
Система анимации представления предоставляет возможность анимировать только объекты View
, поэтому, если вы хотите анимировать объекты, не относящиеся View
, вам придется реализовать для этого собственный код. Система анимации представления также ограничена тем фактом, что она предоставляет для анимации только несколько аспектов объекта View
, например, масштабирование и вращение представления, но не цвет фона.
Еще одним недостатком системы анимации представления является то, что она изменяет только место отрисовки представления, а не само представление. Например, если вы анимировали кнопку для перемещения по экрану, кнопка отрисовывается правильно, но фактическое место, где вы можете нажать кнопку, не меняется, поэтому вам придется реализовать свою собственную логику, чтобы справиться с этим.
С помощью системы анимации свойств эти ограничения полностью устраняются, и вы можете анимировать любое свойство любого объекта (представления и не-представления), при этом сам объект фактически изменяется. Система анимации свойств также более надежна в плане выполнения анимации. На высоком уровне вы назначаете аниматоров свойствам, которые хотите анимировать, например цвету, положению или размеру, и можете определять такие аспекты анимации, как интерполяция и синхронизация нескольких аниматоров.
Однако система анимации просмотра требует меньше времени для настройки и написания меньшего количества кода. Если анимация просмотра выполняет все, что вам нужно, или если ваш существующий код уже работает так, как вы хотите, нет необходимости использовать систему анимации свойств. Также может иметь смысл использовать обе системы анимации для разных ситуаций, если возникнет такой вариант использования.
Обзор API
Вы можете найти большинство API-интерфейсов системы анимации свойств в android.animation
. Поскольку система анимации представления уже определяет множество интерполяторов в android.view.animation
, вы также можете использовать эти интерполяторы в системе анимации свойств. В следующих таблицах описаны основные компоненты системы анимации свойств.
Класс Animator
предоставляет базовую структуру для создания анимации. Обычно вы не используете этот класс напрямую, поскольку он предоставляет лишь минимальную функциональность, которую необходимо расширить для полной поддержки значений анимации. Следующие подклассы расширяют Animator
:
Сорт | Описание |
---|---|
ValueAnimator | Основной механизм синхронизации для анимации свойств, который также вычисляет значения анимируемого свойства. Он имеет все основные функции, которые вычисляют значения анимации и содержат сведения о времени каждой анимации, информацию о том, повторяется ли анимация, прослушиватели, которые получают события обновления, а также возможность устанавливать пользовательские типы для оценки. Анимация свойств состоит из двух частей: вычисление анимированных значений и установка этих значений для анимируемого объекта и свойства. ValueAnimator не выполняет вторую часть, поэтому вам придется прослушивать обновления значений, рассчитанных ValueAnimator , и изменять объекты, которые вы хотите анимировать, с помощью своей собственной логики. Дополнительную информацию см. в разделе « Анимация с помощью ValueAnimator» . |
ObjectAnimator | Подкласс ValueAnimator , который позволяет вам установить целевой объект и свойство объекта для анимации. Этот класс соответствующим образом обновляет свойство, когда вычисляет новое значение для анимации. Большую часть времени вы захотите использовать ObjectAnimator , поскольку он значительно упрощает процесс анимации значений целевых объектов. Однако иногда вам нужно использовать ValueAnimator напрямую, поскольку ObjectAnimator имеет еще несколько ограничений, например, требование присутствия определенных методов доступа в целевом объекте. |
AnimatorSet | Предоставляет механизм группировки анимаций, чтобы они выполнялись относительно друг друга. Вы можете настроить анимацию для воспроизведения вместе, последовательно или после указанной задержки. Дополнительную информацию см. в разделе « Хореография нескольких анимаций с помощью наборов аниматоров» . |
Оценщики сообщают системе анимации свойств, как рассчитать значения для данного свойства. Они берут данные о времени, предоставляемые классом Animator
, начальное и конечное значения анимации, и на основе этих данных вычисляют анимированные значения свойства. Система анимации недвижимости предоставляет следующие оценщики:
Класс/Интерфейс | Описание |
---|---|
IntEvaluator | Оценщик по умолчанию для вычисления значений свойств int . |
FloatEvaluator | Оценщик по умолчанию для вычисления значений свойств float . |
ArgbEvaluator | Оценщик по умолчанию для вычисления значений свойств цвета, представленных в виде шестнадцатеричных значений. |
TypeEvaluator | Интерфейс, позволяющий создать свой собственный оценщик. Если вы анимируете свойство объекта, которое не является int , float или color , необходимо реализовать интерфейс TypeEvaluator , чтобы указать, как вычислять анимированные значения свойства объекта. Вы также можете указать собственный TypeEvaluator для значений int , float и color, если вы хотите обрабатывать эти типы иначе, чем поведение по умолчанию. Дополнительную информацию о том, как написать собственный оценщик, см. в разделе «Использование TypeEvaluator» . |
Интерполятор времени определяет, как определенные значения в анимации рассчитываются как функция времени. Например, вы можете указать, чтобы анимация происходила линейно по всей анимации, то есть анимация движется равномерно на протяжении всего времени, или вы можете указать, чтобы анимация использовала нелинейное время, например, ускоряясь в начале и замедляясь в конце. анимация. В таблице 3 описаны интерполяторы, содержащиеся в android.view.animation
. Если ни один из предоставленных интерполяторов не соответствует вашим потребностям, реализуйте интерфейс TimeInterpolator
и создайте свой собственный. См. Использование интерполяторов для получения дополнительной информации о том, как написать собственный интерполятор.
Класс/Интерфейс | Описание |
---|---|
AccelerateDecelerateInterpolator | Интерполятор, скорость изменения которого начинается и заканчивается медленно, но ускоряется к середине. |
AccelerateInterpolator | Интерполятор, скорость изменения которого начинается медленно, а затем ускоряется. |
AnticipateInterpolator | Интерполятор, изменение которого начинается назад, а затем перемещается вперед. |
AnticipateOvershootInterpolator | Интерполятор, изменение которого начинается назад, перемещается вперед и превышает целевое значение, а затем, наконец, возвращается к конечному значению. |
BounceInterpolator | Интерполятор, изменение которого отскакивает в конце. |
CycleInterpolator | Интерполятор, анимация которого повторяется определенное количество циклов. |
DecelerateInterpolator | Интерполятор, скорость изменения которого начинается быстро, а затем замедляется. |
LinearInterpolator | Интерполятор, скорость изменения которого постоянна. |
OvershootInterpolator | Интерполятор, изменение которого перемещается вперед и превышает последнее значение, затем возвращается назад. |
TimeInterpolator | Интерфейс, позволяющий реализовать собственный интерполятор. |
Анимация с помощью ValueAnimator
Класс ValueAnimator
позволяет анимировать значения определенного типа на протяжении всей анимации, указав набор значений int
, float
или color для анимации. Вы получаете ValueAnimator
, вызывая один из его фабричных методов: ofInt()
, ofFloat()
или ofObject()
. Например:
Котлин
ValueAnimator.ofFloat(0f, 100f).apply { duration = 1000 start() }
Ява
ValueAnimator animation = ValueAnimator.ofFloat(0f, 100f); animation.setDuration(1000); animation.start();
В этом коде ValueAnimator
начинает вычисление значений анимации от 0 до 100 в течение 1000 мс при запуске метода start()
.
Вы также можете указать собственный тип анимации, выполнив следующие действия:
Котлин
ValueAnimator.ofObject(MyTypeEvaluator(), startPropertyValue, endPropertyValue).apply { duration = 1000 start() }
Ява
ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue); animation.setDuration(1000); animation.start();
В этом коде ValueAnimator
начинает вычисление значений анимации между startPropertyValue
и endPropertyValue
, используя логику, предоставленную MyTypeEvaluator
, в течение 1000 мс при запуске метода start()
.
Вы можете использовать значения анимации, добавив AnimatorUpdateListener
к объекту ValueAnimator
, как показано в следующем коде:
Котлин
ValueAnimator.ofObject(...).apply { ... addUpdateListener { updatedAnimation -> // You can use the animated value in a property that uses the // same type as the animation. In this case, you can use the // float value in the translationX property. textView.translationX = updatedAnimation.animatedValue as Float } ... }
Ява
animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator updatedAnimation) { // You can use the animated value in a property that uses the // same type as the animation. In this case, you can use the // float value in the translationX property. float animatedValue = (float)updatedAnimation.getAnimatedValue(); textView.setTranslationX(animatedValue); } });
В методе onAnimationUpdate()
вы можете получить доступ к обновленному значению анимации и использовать его в свойстве одного из ваших представлений. Дополнительную информацию о прослушивателях см. в разделе про прослушиватели анимации .
Анимация с помощью ObjectAnimator
ObjectAnimator
является подклассом ValueAnimator
(обсуждаемого в предыдущем разделе) и сочетает в себе механизм синхронизации и вычисления значений ValueAnimator
с возможностью анимации именованного свойства целевого объекта. Это значительно упрощает анимацию любого объекта, поскольку вам больше не нужно реализовывать ValueAnimator.AnimatorUpdateListener
, поскольку анимированное свойство обновляется автоматически.
Создание ObjectAnimator
похоже на ValueAnimator
, но вы также указываете объект и имя свойства этого объекта (в виде строки) вместе со значениями для анимации между ними:
Котлин
ObjectAnimator.ofFloat(textView, "translationX", 100f).apply { duration = 1000 start() }
Ява
ObjectAnimator animation = ObjectAnimator.ofFloat(textView, "translationX", 100f); animation.setDuration(1000); animation.start();
Чтобы свойства ObjectAnimator
обновлялись правильно, необходимо сделать следующее:
- Свойство объекта, которое вы анимируете, должно иметь функцию установки (в случае Camel) в форме
set<PropertyName>()
. ПосколькуObjectAnimator
автоматически обновляет свойство во время анимации, у него должна быть возможность доступа к свойству с помощью этого метода установки. Например, если имя свойства —foo
, вам понадобится методsetFoo()
. Если этот метод установки не существует, у вас есть три варианта:- Добавьте метод установки в класс, если у вас есть на это права.
- Используйте класс-оболочку, на изменение которого у вас есть права, и пусть эта оболочка получит значение с помощью допустимого метода установки и передаст его исходному объекту.
- Вместо этого используйте
ValueAnimator
.
- Если вы укажете только одно значение для
values...
в одном из фабричных методовObjectAnimator
, предполагается, что оно является конечным значением анимации. Таким образом, свойство объекта, который вы анимируете, должно иметь функцию получения, которая используется для получения начального значения анимации. Функция получения должна иметь формуget<PropertyName>()
. Например, если имя свойства —foo
, вам понадобится методgetFoo()
. - Методы получения (при необходимости) и установки свойства, которое вы анимируете, должны работать с тем же типом, что и начальное и конечное значения, которые вы указываете для
ObjectAnimator
. Например, у вас должны бытьtargetObject.setPropName(float)
иtargetObject.getPropName()
если вы создаете следующийObjectAnimator
:ObjectAnimator.ofFloat(targetObject, "propName", 1f)
- В зависимости от того, какое свойство или объект вы анимируете, вам может потребоваться вызвать метод
invalidate()
в представлении, чтобы заставить экран перерисоваться с обновленными анимированными значениями. Вы делаете это в обратном вызовеonAnimationUpdate()
. Например, анимация свойства цвета объекта Drawable приводит к обновлению экрана только тогда, когда этот объект перерисовывается. Все установщики свойств в представлении, такие какsetAlpha()
иsetTranslationX()
правильно делают представление недействительным, поэтому вам не нужно делать представление недействительным при вызове этих методов с новыми значениями. Дополнительную информацию о прослушивателях см. в разделе про прослушиватели анимации .
Хореографируйте несколько анимаций с помощью AnimatorSet.
Во многих случаях требуется воспроизвести анимацию, которая зависит от того, когда начинается или заканчивается другая анимация. Система Android позволяет объединять анимации в AnimatorSet
, чтобы вы могли указать, следует ли запускать анимацию одновременно, последовательно или после указанной задержки. Вы также можете вкладывать объекты AnimatorSet
друг в друга.
Следующий фрагмент кода воспроизводит следующие объекты Animator
следующим образом:
- Воспроизводит
bounceAnim
. - Воспроизводит
squashAnim1
,squashAnim2
,stretchAnim1
иstretchAnim2
одновременно. - Воспроизводит
bounceBackAnim
. - Воспроизводит
fadeAnim
.
Котлин
val bouncer = AnimatorSet().apply { play(bounceAnim).before(squashAnim1) play(squashAnim1).with(squashAnim2) play(squashAnim1).with(stretchAnim1) play(squashAnim1).with(stretchAnim2) play(bounceBackAnim).after(stretchAnim2) } val fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f).apply { duration = 250 } AnimatorSet().apply { play(bouncer).before(fadeAnim) start() }
Ява
AnimatorSet bouncer = new AnimatorSet(); bouncer.play(bounceAnim).before(squashAnim1); bouncer.play(squashAnim1).with(squashAnim2); bouncer.play(squashAnim1).with(stretchAnim1); bouncer.play(squashAnim1).with(stretchAnim2); bouncer.play(bounceBackAnim).after(stretchAnim2); ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f); fadeAnim.setDuration(250); AnimatorSet animatorSet = new AnimatorSet(); animatorSet.play(bouncer).before(fadeAnim); animatorSet.start();
Слушатели анимации
Вы можете прослушивать важные события во время анимации с помощью прослушивателей, описанных ниже.
-
Animator.AnimatorListener
-
onAnimationStart()
— вызывается при запуске анимации. -
onAnimationEnd()
— вызывается, когда анимация заканчивается. -
onAnimationRepeat()
— вызывается, когда анимация повторяется. -
onAnimationCancel()
— вызывается при отмене анимации. Отмененная анимация также вызываетonAnimationEnd()
независимо от того, как она была завершена.
-
-
ValueAnimator.AnimatorUpdateListener
onAnimationUpdate()
— вызывается в каждом кадре анимации. Прослушайте это событие, чтобы использовать вычисленные значения, сгенерированныеValueAnimator
, во время анимации. Чтобы использовать значение, запросите объектValueAnimator
, переданный в событие, чтобы получить текущее анимированное значение с помощью методаgetAnimatedValue()
. Реализация этого прослушивателя необходима, если вы используетеValueAnimator
.В зависимости от того, какое свойство или объект вы анимируете, вам может потребоваться вызвать
invalidate()
в представлении, чтобы заставить эту область экрана перерисоваться с новыми анимированными значениями. Например, анимация свойства цвета объекта Drawable приводит к обновлению экрана только тогда, когда этот объект перерисовывается. Все установщики свойств в представлении, такие какsetAlpha()
иsetTranslationX()
правильно делают представление недействительным, поэтому вам не нужно делать представление недействительным при вызове этих методов с новыми значениями.
Вы можете расширить класс AnimatorListenerAdapter
вместо реализации интерфейса Animator.AnimatorListener
, если вы не хотите реализовывать все методы интерфейса Animator.AnimatorListener
. Класс AnimatorListenerAdapter
предоставляет пустые реализации методов, которые вы можете переопределить.
Например, следующий фрагмент кода создает AnimatorListenerAdapter
только для обратного вызова onAnimationEnd()
:
Котлин
ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f).apply { duration = 250 addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { balls.remove((animation as ObjectAnimator).target) } }) }
Ява
ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f); fadeAnim.setDuration(250); fadeAnim.addListener(new AnimatorListenerAdapter() { public void onAnimationEnd(Animator animation) { balls.remove(((ObjectAnimator)animation).getTarget()); }
Анимация изменений макета объектов ViewGroup
Система анимации свойств предоставляет возможность анимировать изменения объектов ViewGroup, а также предоставляет простой способ анимации самих объектов View.
Вы можете анимировать изменения макета внутри ViewGroup с помощью класса LayoutTransition
. Представления внутри ViewGroup могут проявлять анимацию появления и исчезновения, когда вы добавляете их в ViewGroup или удаляете из них, или когда вы вызываете метод setVisibility()
представления с помощью VISIBLE
, INVISIBLE
или GONE
. Остальные виды в ViewGroup также могут анимироваться в своих новых положениях при добавлении или удалении представлений. Вы можете определить следующие анимации в объекте LayoutTransition
, вызвав setAnimator()
и передав объект Animator
с одной из следующих констант LayoutTransition
:
-
APPEARING
— флаг, указывающий анимацию, которая запускается для элементов, появляющихся в контейнере. -
CHANGE_APPEARING
— флаг, указывающий анимацию, которая запускается для элементов, которые изменяются из-за появления нового элемента в контейнере. -
DISAPPEARING
— флаг, обозначающий анимацию, которая запускается для элементов, исчезающих из контейнера. -
CHANGE_DISAPPEARING
— флаг, указывающий анимацию, которая запускается для элементов, которые изменяются из-за исчезновения элемента из контейнера.
Вы можете определить свои собственные анимации для этих четырех типов событий, чтобы настроить внешний вид переходов макета, или просто указать системе анимации использовать анимации по умолчанию.
Чтобы установить для android:animateLayoutchanges
значение true
для ViewGroup, выполните следующие действия:
<LinearLayout android:orientation="vertical" android:layout_width="wrap_content" android:layout_height="match_parent" android:id="@+id/verticalContainer" android:animateLayoutChanges="true" />
Установка для этого атрибута значения true автоматически анимирует представления, которые добавляются или удаляются из ViewGroup, а также остальные представления в ViewGroup.
Анимация изменений состояния представления с помощью StateListAnimator
Класс StateListAnimator
позволяет определять аниматоры, которые запускаются при изменении состояния представления. Этот объект ведет себя как оболочка для объекта Animator
, вызывая эту анимацию всякий раз, когда изменяется указанное состояние просмотра (например, «нажатие» или «фокусировка»).
StateListAnimator
можно определить в XML-ресурсе с корневым элементом <selector>
и дочерними элементами <item>
, каждый из которых определяет различное состояние просмотра, определенное классом StateListAnimator
. Каждый <item>
содержит определение набора анимации свойств .
Например, следующий файл создает аниматор списка состояний, который изменяет масштаб представления по осям X и Y при нажатии:
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <!-- the pressed state; increase x and y size to 150% --> <item android:state_pressed="true"> <set> <objectAnimator android:propertyName="scaleX" android:duration="@android:integer/config_shortAnimTime" android:valueTo="1.5" android:valueType="floatType"/> <objectAnimator android:propertyName="scaleY" android:duration="@android:integer/config_shortAnimTime" android:valueTo="1.5" android:valueType="floatType"/> </set> </item> <!-- the default, non-pressed state; set x and y size to 100% --> <item android:state_pressed="false"> <set> <objectAnimator android:propertyName="scaleX" android:duration="@android:integer/config_shortAnimTime" android:valueTo="1" android:valueType="floatType"/> <objectAnimator android:propertyName="scaleY" android:duration="@android:integer/config_shortAnimTime" android:valueTo="1" android:valueType="floatType"/> </set> </item> </selector>
Чтобы прикрепить аниматор списка состояний к представлению, добавьте атрибут android:stateListAnimator
следующим образом:
<Button android:stateListAnimator="@xml/animate_scale" ... />
Теперь анимации, определенные в animate_scale.xml
используются при изменении состояния этой кнопки.
Или вместо этого назначьте аниматор списка состояний представлению в вашем коде, используйте метод AnimatorInflater.loadStateListAnimator()
и назначьте аниматор вашему представлению с помощью метода View.setStateListAnimator()
.
Или вместо анимации свойств представления вы можете воспроизводить рисуемую анимацию между изменениями состояния, используя AnimatedStateListDrawable
. Некоторые системные виджеты Android 5.0 по умолчанию используют эту анимацию. В следующем примере показано, как определить AnimatedStateListDrawable
как ресурс XML:
<!-- res/drawable/myanimstatedrawable.xml --> <animated-selector xmlns:android="http://schemas.android.com/apk/res/android"> <!-- provide a different drawable for each state--> <item android:id="@+id/pressed" android:drawable="@drawable/drawableP" android:state_pressed="true"/> <item android:id="@+id/focused" android:drawable="@drawable/drawableF" android:state_focused="true"/> <item android:id="@id/default" android:drawable="@drawable/drawableD"/> <!-- specify a transition --> <transition android:fromId="@+id/default" android:toId="@+id/pressed"> <animation-list> <item android:duration="15" android:drawable="@drawable/dt1"/> <item android:duration="15" android:drawable="@drawable/dt2"/> ... </animation-list> </transition> ... </animated-selector>
Используйте TypeEvaluator
Если вы хотите анимировать тип, который неизвестен системе Android, вы можете создать свой собственный оценщик, реализовав интерфейс TypeEvaluator
. Типы, известные системе Android, — это int
, float
или a color, которые поддерживаются оценщиками типов IntEvaluator
, FloatEvaluator
и ArgbEvaluator
.
В интерфейсе TypeEvaluator
можно реализовать только один метод — метод evaluate()
. Это позволяет аниматору, который вы используете, возвращать подходящее значение для вашего анимированного свойства в текущей точке анимации. Класс FloatEvaluator
демонстрирует, как это сделать:
Котлин
private class FloatEvaluator : TypeEvaluator<Any> { override fun evaluate(fraction: Float, startValue: Any, endValue: Any): Any { return (startValue as Number).toFloat().let { startFloat -> startFloat + fraction * ((endValue as Number).toFloat() - startFloat) } } }
Ява
public class FloatEvaluator implements TypeEvaluator { public Object evaluate(float fraction, Object startValue, Object endValue) { float startFloat = ((Number) startValue).floatValue(); return startFloat + fraction * (((Number) endValue).floatValue() - startFloat); } }
Примечание. При запуске ValueAnimator
(или ObjectAnimator
) он вычисляет текущую прошедшую часть анимации (значение от 0 до 1), а затем вычисляет интерполированную версию этого значения в зависимости от того, какой интерполятор вы используете. Интерполированная дробь — это то, что ваш TypeEvaluator
получает через параметр fraction
, поэтому вам не нужно учитывать интерполятор при вычислении анимированных значений.
Используйте интерполяторы
Интерполятор определяет, как определенные значения в анимации рассчитываются как функция времени. Например, вы можете указать, чтобы анимация происходила линейно по всей анимации, то есть анимация движется равномерно на протяжении всего времени, или вы можете указать, чтобы анимация использовала нелинейное время, например, используя ускорение или замедление в начале или конце анимации. анимация.
Интерполяторы в системе анимации получают от аниматоров часть, которая представляет собой прошедшее время анимации. Интерполяторы изменяют эту дробь, чтобы она соответствовала типу анимации, которую она призвана обеспечить. Система Android предоставляет набор общих интерполяторов в android.view.animation package
. Если ни один из них не соответствует вашим потребностям, вы можете реализовать интерфейс TimeInterpolator
и создать свой собственный.
В качестве примера ниже сравнивается то, как интерполятор по умолчанию AccelerateDecelerateInterpolator
и LinearInterpolator
вычисляют интерполированные дроби. LinearInterpolator
не влияет на прошедшую дробь. AccelerateDecelerateInterpolator
ускоряет анимацию и замедляет ее. Следующие методы определяют логику этих интерполяторов:
УскорениеЗамедлениеИнтерполятор
Котлин
override fun getInterpolation(input: Float): Float = (Math.cos((input + 1) * Math.PI) / 2.0f).toFloat() + 0.5f
Ява
@Override public float getInterpolation(float input) { return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f; }
ЛинейныйИнтерполятор
Котлин
override fun getInterpolation(input: Float): Float = input
Ява
@Override public float getInterpolation(float input) { return input; }
В следующей таблице представлены приблизительные значения, рассчитанные этими интерполяторами для анимации длительностью 1000 мс:
мс прошло | Прошедшая дробь/Интерполированная дробь (Линейная) | Интерполированная дробь (Ускорение/Замедление) |
---|---|---|
0 | 0 | 0 |
200 | .2 | .1 |
400 | .4 | .345 |
600 | .6 | .8 |
800 | .8 | .9 |
1000 | 1 | 1 |
Как видно из таблицы, LinearInterpolator
меняет значения с одинаковой скоростью: 0,2 за каждые 200 мс. AccelerateDecelerateInterpolator
изменяет значения быстрее, чем LinearInterpolator
в диапазоне от 200 мс до 600 мс и медленнее, в диапазоне от 600 мс до 1000 мс.
Укажите ключевые кадры
Объект Keyframe
состоит из пары время/значение, которая позволяет вам определить определенное состояние в определенный момент анимации. Каждый ключевой кадр также может иметь собственный интерполятор для управления поведением анимации в интервале между временем предыдущего ключевого кадра и временем этого ключевого кадра.
Чтобы создать экземпляр объекта Keyframe
, вы должны использовать один из фабричных методов ofInt()
, ofFloat()
или ofObject()
, чтобы получить соответствующий тип Keyframe
. Затем вы вызываете фабричный метод ofKeyframe()
, чтобы получить объект PropertyValuesHolder
. Получив объект, вы можете получить аниматор, передав объект PropertyValuesHolder
и объект для анимации. Следующий фрагмент кода демонстрирует, как это сделать:
Котлин
val kf0 = Keyframe.ofFloat(0f, 0f) val kf1 = Keyframe.ofFloat(.5f, 360f) val kf2 = Keyframe.ofFloat(1f, 0f) val pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2) ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation).apply { duration = 5000 }
Ява
Keyframe kf0 = Keyframe.ofFloat(0f, 0f); Keyframe kf1 = Keyframe.ofFloat(.5f, 360f); Keyframe kf2 = Keyframe.ofFloat(1f, 0f); PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2); ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation); rotationAnim.setDuration(5000);
Анимация представлений
Система анимации свойств позволяет упростить анимацию объектов просмотра и предлагает несколько преимуществ по сравнению с системой анимации просмотра. Система анимации представления преобразовала объекты View, изменив способ их рисования. Это обрабатывалось в контейнере каждого представления, поскольку у самого представления не было свойств, которыми можно было бы манипулировать. Это привело к анимации представления, но не вызвало никаких изменений в самом объекте представления. Это приводило к такому поведению, как будто объект все еще существовал в своем исходном месте, даже если он был нарисован в другом месте на экране. В Android 3.0 для устранения этого недостатка были добавлены новые свойства и соответствующие методы получения и установки.
Система анимации свойств может анимировать представления на экране, изменяя фактические свойства объектов представления. Кроме того, представления также автоматически вызывают метод invalidate()
для обновления экрана при каждом изменении его свойств. Новые свойства класса View
, которые облегчают анимацию свойств:
-
translationX
иtranslationY
: эти свойства управляют расположением представления в виде отклонения от его левой и верхней координат, которые задаются его контейнером макета. -
rotation
,rotationX
иrotationY
: Эти свойства управляют вращением в 2D (свойствоrotation
) и 3D вокруг точки поворота. -
scaleX
иscaleY
: эти свойства управляют 2D-масштабированием представления вокруг его точки поворота. -
pivotX
иpivotY
: эти свойства управляют расположением точки поворота, вокруг которой происходят преобразования вращения и масштабирования. По умолчанию точка поворота расположена в центре объекта. -
x
иy
: это простые служебные свойства, описывающие окончательное расположение представления в его контейнере как сумму левого и верхнего значений, а также значений TranslationX и TranslationY. -
alpha
: представляет альфа-прозрачность в представлении. По умолчанию это значение равно 1 (непрозрачный), а значение 0 соответствует полной прозрачности (невидимому).
Чтобы анимировать свойство объекта View, например его цвет или значение поворота, все, что вам нужно сделать, — это создать аниматор свойств и указать свойство View, которое вы хотите анимировать. Например:
Котлин
ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f)
Ява
ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f);
Дополнительные сведения о создании аниматоров см. в разделах, посвященных анимации с помощью ValueAnimator и ObjectAnimator .
Анимация с помощью ViewPropertyAnimator
ViewPropertyAnimator
предоставляет простой способ параллельной анимации нескольких свойств View
, используя один базовый объект Animator
. Он ведет себя так же, как ObjectAnimator
, поскольку изменяет фактические значения свойств представления, но более эффективен при анимации нескольких свойств одновременно. Кроме того, код использования ViewPropertyAnimator
намного лаконичнее и легче читается. В следующих фрагментах кода показаны различия в использовании нескольких объектов ObjectAnimator
, одного ObjectAnimator
и ViewPropertyAnimator
при одновременной анимации свойств x
и y
представления.
Несколько объектов ObjectAnimator
Котлин
val animX = ObjectAnimator.ofFloat(myView, "x", 50f) val animY = ObjectAnimator.ofFloat(myView, "y", 100f) AnimatorSet().apply { playTogether(animX, animY) start() }
Ява
ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f); ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f); AnimatorSet animSetXY = new AnimatorSet(); animSetXY.playTogether(animX, animY); animSetXY.start();
Один ОбъектАниматор
Котлин
val pvhX = PropertyValuesHolder.ofFloat("x", 50f) val pvhY = PropertyValuesHolder.ofFloat("y", 100f) ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvhY).start()
Ява
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f); PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f); ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvhY).start();
ViewPropertyAnimator
Котлин
myView.animate().x(50f).y(100f)
Ява
myView.animate().x(50f).y(100f);
Более подробную информацию о ViewPropertyAnimator
можно найти в соответствующей записи блога разработчиков Android.
Объявить анимацию в XML
Система анимации свойств позволяет объявлять анимацию свойств с помощью XML, а не делать это программно. Определив анимацию в XML, вы можете легко повторно использовать анимацию в нескольких действиях и упростить редактирование последовательности анимации.
Чтобы отличить файлы анимации, использующие новые API-интерфейсы анимации свойств, от тех, которые используют устаревшую структуру анимации представлений , начиная с Android 3.1, вам следует сохранить XML-файлы для анимации свойств в каталоге res/animator/
.
Следующие классы анимации свойств поддерживают декларации XML со следующими тегами XML:
-
ValueAnimator
-<animator>
-
ObjectAnimator
—<objectAnimator>
-
AnimatorSet
—<set>
Чтобы найти атрибуты, которые можно использовать в объявлении XML, см. раздел Ресурсы анимации . В следующем примере последовательно воспроизводятся два набора анимаций объектов, при этом первый вложенный набор воспроизводит две анимации объектов вместе:
<set android:ordering="sequentially"> <set> <objectAnimator android:propertyName="x" android:duration="500" android:valueTo="400" android:valueType="intType"/> <objectAnimator android:propertyName="y" android:duration="500" android:valueTo="300" android:valueType="intType"/> </set> <objectAnimator android:propertyName="alpha" android:duration="500" android:valueTo="1f"/> </set>
Чтобы запустить эту анимацию, вы должны преобразовать ресурсы XML в своем коде в объект AnimatorSet
, а затем установить целевые объекты для всех анимаций перед запуском набора анимаций. Вызов setTarget()
для удобства устанавливает один целевой объект для всех дочерних элементов AnimatorSet
. Следующий код показывает, как это сделать:
Котлин
(AnimatorInflater.loadAnimator(myContext, R.animator.property_animator) as AnimatorSet).apply { setTarget(myObject) start() }
Ява
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext, R.animator.property_animator); set.setTarget(myObject); set.start();
Вы также можете объявить ValueAnimator
в XML, как показано в следующем примере:
<animator xmlns:android="http://schemas.android.com/apk/res/android" android:duration="1000" android:valueType="floatType" android:valueFrom="0f" android:valueTo="-100f" />
Чтобы использовать предыдущий ValueAnimator
в своем коде, вы должны раздуть объект, добавить AnimatorUpdateListener
, получить обновленное значение анимации и использовать его в свойстве одного из ваших представлений, как показано в следующем коде:
Котлин
(AnimatorInflater.loadAnimator(this, R.animator.animator) as ValueAnimator).apply { addUpdateListener { updatedAnimation -> textView.translationX = updatedAnimation.animatedValue as Float } start() }
Ява
ValueAnimator xmlAnimator = (ValueAnimator) AnimatorInflater.loadAnimator(this, R.animator.animator); xmlAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator updatedAnimation) { float animatedValue = (float)updatedAnimation.getAnimatedValue(); textView.setTranslationX(animatedValue); } }); xmlAnimator.start();
Сведения о синтаксисе XML для определения анимации свойств см. в разделе Ресурсы анимации .
Потенциальное влияние на производительность пользовательского интерфейса
Аниматоры, обновляющие пользовательский интерфейс, вызывают дополнительную работу по рендерингу для каждого кадра, в котором выполняется анимация. По этой причине использование ресурсоемкой анимации может отрицательно повлиять на производительность вашего приложения.
Работа, необходимая для анимации пользовательского интерфейса, добавляется на этап анимации конвейера рендеринга. Вы можете узнать, влияет ли ваша анимация на производительность вашего приложения, включив рендеринг профиля GPU и отслеживая этап анимации. Дополнительные сведения см. в разделе Пошаговое руководство по рендерингу профиля с использованием графического процессора .