Обзор анимации свойств

Попробуйте способ создания
Jetpack Compose — рекомендуемый набор инструментов пользовательского интерфейса для Android. Узнайте, как использовать анимацию в Compose.

Система анимации свойств — это надежная платформа, позволяющая анимировать практически все. Вы можете определить анимацию для изменения любого свойства объекта с течением времени, независимо от того, отображается он на экране или нет. Анимация свойства изменяет значение свойства (поля в объекте) в течение определенного периода времени. Чтобы анимировать что-либо, вы указываете свойство объекта, которое вы хотите анимировать, например положение объекта на экране, продолжительность его анимации и между какими значениями вы хотите анимировать.

Система анимации свойств позволяет определить следующие характеристики анимации:

  • Продолжительность: вы можете указать продолжительность анимации. Длина по умолчанию составляет 300 мс.
  • Интерполяция времени: вы можете указать, как значения свойства будут рассчитываться в зависимости от текущего прошедшего времени анимации.
  • Количество повторений и поведение: вы можете указать, будет ли повторяться анимация по достижении конца продолжительности и сколько раз следует повторять анимацию. Вы также можете указать, хотите ли вы, чтобы анимация воспроизводилась в обратном порядке. Если установить реверс, анимация воспроизводится вперед, а затем назад несколько раз, пока не будет достигнуто необходимое количество повторов.
  • Наборы аниматоров: анимацию можно группировать в логические наборы, которые воспроизводятся вместе, последовательно или после заданных задержек.
  • Задержка обновления кадра: вы можете указать, как часто следует обновлять кадры анимации. По умолчанию установлено обновление каждые 10 мс, но скорость, с которой ваше приложение может обновлять кадры, в конечном итоге зависит от того, насколько загружена система в целом и насколько быстро система может обслуживать базовый таймер.

Полный пример анимации свойств см. в классе ChangeColor в образце CustomTransition на GitHub.

Как работает анимация свойств

Сначала давайте рассмотрим, как работает анимация, на простом примере. На рисунке 1 изображен гипотетический объект, анимированный с помощью свойства x , которое представляет его горизонтальное положение на экране. Продолжительность анимации установлена ​​на 40 мс, а расстояние перемещения — 40 пикселей. Каждые 10 мс (частота обновления кадра по умолчанию) объект перемещается по горизонтали на 10 пикселей. По истечении 40 мс анимация останавливается, и объект заканчивается в горизонтальном положении 40. Это пример анимации с линейной интерполяцией, то есть объект движется с постоянной скоростью.

Рисунок 1. Пример линейной анимации

Вы также можете указать анимацию с нелинейной интерполяцией. На рисунке 2 показан гипотетический объект, который ускоряется в начале анимации и замедляется в конце анимации. Объект по-прежнему перемещается на 40 пикселей за 40 мс, но нелинейно. Вначале эта анимация ускоряется до середины, затем замедляется от середины до конца анимации. Как показано на рисунке 2, пройденное расстояние в начале и конце анимации меньше, чем в середине.

Рисунок 2. Пример нелинейной анимации

Давайте подробно рассмотрим, как важные компоненты системы анимации свойств будут рассчитывать анимацию, подобную показанной выше. На рисунке 3 показано, как основные классы работают друг с другом.

Рисунок 3. Как рассчитываются анимации

Объект ValueAnimator отслеживает время анимации, например, как долго выполняется анимация, а также текущее значение свойства, которое она анимирует.

ValueAnimator инкапсулирует TimeInterpolator , который определяет интерполяцию анимации, и TypeEvaluator , который определяет, как вычислять значения для анимируемого свойства. Например, на рисунке 2 используемый TimeInterpolator будет AccelerateDecelerateInterpolator , а TypeEvaluatorIntEvaluator .

Чтобы запустить анимацию, создайте 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 :

Таблица 1. Аниматоры

Сорт Описание
ValueAnimator Основной механизм синхронизации для анимации свойств, который также вычисляет значения анимируемого свойства. Он имеет все основные функции, которые вычисляют значения анимации и содержат сведения о времени каждой анимации, информацию о том, повторяется ли анимация, прослушиватели, которые получают события обновления, а также возможность устанавливать пользовательские типы для оценки. Анимация свойств состоит из двух частей: вычисление анимированных значений и установка этих значений для анимируемого объекта и свойства. ValueAnimator не выполняет вторую часть, поэтому вам придется прослушивать обновления значений, рассчитанных ValueAnimator , и изменять объекты, которые вы хотите анимировать, с помощью своей собственной логики. Дополнительную информацию см. в разделе « Анимация с помощью ValueAnimator» .
ObjectAnimator Подкласс ValueAnimator , который позволяет вам установить целевой объект и свойство объекта для анимации. Этот класс соответствующим образом обновляет свойство, когда вычисляет новое значение для анимации. Большую часть времени вы захотите использовать ObjectAnimator , поскольку он значительно упрощает процесс анимации значений целевых объектов. Однако иногда вам нужно использовать ValueAnimator напрямую, поскольку ObjectAnimator имеет еще несколько ограничений, например, требование наличия определенных методов доступа в целевом объекте.
AnimatorSet Предоставляет механизм группировки анимаций, чтобы они выполнялись относительно друг друга. Вы можете настроить анимацию для воспроизведения вместе, последовательно или после указанной задержки. Дополнительную информацию см. в разделе « Хореография нескольких анимаций с помощью наборов аниматоров» .

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

Таблица 2. Оценщики

Класс/Интерфейс Описание
IntEvaluator Оценщик по умолчанию для вычисления значений свойств int .
FloatEvaluator Оценщик по умолчанию для вычисления значений свойств float .
ArgbEvaluator Оценщик по умолчанию для расчета значений свойств цвета, представленных в виде шестнадцатеричных значений.
TypeEvaluator Интерфейс, позволяющий создать свой собственный оценщик. Если вы анимируете свойство объекта, которое не является int , float или color , необходимо реализовать интерфейс TypeEvaluator , чтобы указать, как вычислять анимированные значения свойства объекта. Вы также можете указать собственный TypeEvaluator для значений int , float и color, если вы хотите обрабатывать эти типы иначе, чем поведение по умолчанию. Дополнительную информацию о том, как написать собственный оценщик, см. в разделе «Использование TypeEvaluator» .

Интерполятор времени определяет, как определенные значения в анимации рассчитываются как функция времени. Например, вы можете указать, чтобы анимация происходила линейно по всей анимации, то есть анимация движется равномерно на протяжении всего времени, или вы можете указать, чтобы анимация использовала нелинейное время, например, ускоряясь в начале и замедляясь в конце. анимация. В таблице 3 описаны интерполяторы, содержащиеся в android.view.animation . Если ни один из предоставленных интерполяторов не соответствует вашим потребностям, реализуйте интерфейс TimeInterpolator и создайте свой собственный. См. Использование интерполяторов для получения дополнительной информации о том, как написать собственный интерполятор.

Таблица 3. Интерполяторы

Класс/Интерфейс Описание
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 следующим образом:

  1. Воспроизводит bounceAnim .
  2. Воспроизводит squashAnim1 , squashAnim2 , stretchAnim1 и stretchAnim2 одновременно.
  3. Воспроизводит bounceBackAnim .
  4. Воспроизводит 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 при нажатии:

res/xml/animate_scale.xml

<?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:

Чтобы найти атрибуты, которые можно использовать в объявлении 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 и отслеживая этап анимации. Дополнительные сведения см. в разделе Пошаговое руководство по рендерингу профиля с использованием графического процессора .