- Animator — что это? Зачем он нужен? Почему его стоит использовать вместо Animation?
- Что такое Animator?
- Классы, унаследованные от Animator
- Как мы начали использовать Animator
- Глубже в Animator
- Android properties that can be animated with ObjectAnimator
- 4 Answers 4
- Анимации в Android на базе Kotlin и RxJava
- Анимация
- ValueAnimator
- ObjectAnimator
- AnimatorSet
- ViewPropertyAnimator
- Почему мы выбрали RxJava?
- Запуск двух анимаций
- VanGogh
- Видеозапись доклада
- Спикеры MBLT DEV 2018
Animator — что это? Зачем он нужен? Почему его стоит использовать вместо Animation?
Привет! Меня зовут Данил Перевалов, я работаю Android-разработчиком в компании «Лайв Тайпинг», и сегодня мне выпала честь открыть наш блог на Хабре. Дебютный материал посвящён типу классов Animator. Статья будет полезна и тем, кто только столкнулся с созданием анимаций, и тем, кому недостаточно уже имеющихся знаний по предмету. Тех же, кто давно знаком с темой, мы настойчиво просим делиться опытом в комментариях.
Что такое Animator?
Немного истории. С момента запуска платформы Android существовал фреймворк View Animation. Предназначался он, как следует из названия, для анимаций. Но производительность устройств в конце нулевых была настолько низкой, что о красивых анимациях никто особо не думал, поэтому фреймворк не был удобным и гибким. Он имел только четыре типа анимации (TranslateAnimation, AlphaAnimation, ScaleAnimation, RotateAnimation), класс, позволяющий их комбинировать (AnimationSet), а также способность работать только с классами, унаследованными от View.
В Android 3.0 появился куда более гибкий фреймворк Property Animation. Он умеет изменять любое доступное свойство, а также может работать с любыми классами. Его основным инструментом является Animator.
Animator — это тип классов, предназначенных для изменения значений выбранного объекта относительно времени. Грубо говоря, это инструмент для управления потоком заданной длительности, который изменяет определённое свойство от начального значения к конечному. Таким плавно меняющимся свойством в анимации может быть, например, прозрачность.
Идеологическое отличие классов Animator от View Animation в том, что View Animation изменяет «представление» объекта, не изменяя сам объект (исключением является использование setFillAfter(true), но с этим флагом объект изменяется в конце анимации). Animator же призван изменять свойства самого объекта.
Классы, унаследованные от Animator
ValueAnimator (наследуется от Animator)
В самом простом варианте мы задаём этому классу тип изменяемого значения, начальное значение и конечное значение, и запускаем. В ответ нам будут приходить события на начало, конец, повторение и отмену анимации и ещё на два события, которые задаются отдельно для паузы и изменения значения. Событие изменения, пожалуй, самое важное: в него будет приходить изменённое значение, с помощью которого мы и будем менять свойства объектов.
Посмотрите на изменение alpha с его помощью:
ObjectAnimator, наследуется от ValueAnimator
Это класс, призванный упростить работу с ValueAnimator. С ним вам не нужно вручную изменять какое-либо значение по событию изменения — вы просто даёте Animator’у объект и указываете поле, которое вы хотите изменить, например scaleX. С помощью Java Refliction ищется setter для этого поля (в данном случае — setScaleX. Далее Animator самостоятельно будет менять значение этого поля.
С помощью ObjectAnimator изменение alpha будет выглядеть так:
У класса View есть несколько свойств специально предназначенных для анимирования с помощью Animator:
- прозрачность (View.ALPHA)
- масштаб (View.SCALE_X, View.SCALE_Y)
- вращение (View.ROTATION, View.ROTATION_X, View.ROTATION_Y)
- положение (View.X, View.Y, View.Z)
- положение отображаемой части (View.TRANSLATION_X, View.TRANSLATION_Y, View.TRANSLATION_Z)
AnimatorSet (наследуется от Animator)
Это класс, позволяющий комбинировать анимации различными способами: запускать одновременно или последовательно, добавлять задержки и т.д.
Это отдельный класс. Он не наследуется от Animator, но обладает той же логикой, что и ObjectAnimator для View, и предназначен для лёгкого анимирования какой-либо View без лишних заморочек.
Вот так с его помощью можно изменить alpha:
Как мы начали использовать Animator
Около года назад передо мной встала задача сделать анимацию при клике на элемент. Вот такую:
Не то чтобы я не делал анимаций прежде, но на аутсорсе они редко нужны. Поэтому я загуглил Animation Android. Первые пять ссылок довольно подробно описывали, как делаются анимации, и я приступил. Вот первый результат:
Код получился малопонятным, что подтолкнуло меня к поискам иного подхода в составлении последовательности анимаций. Решение было найдено на StackOveflow. Идея такая: помещать в последовательности анимаций каждую последующую анимацию в AnimationSet со сдвигом, равным сумме длительностей предыдущих анимаций. Получилось гораздо лучше, чем было:
Код стал понятнее и читабельнее, но есть одно «но»: следить за сдвигом у каждой анимации довольно неудобно даже в такой простой последовательности. Если добавить ещё несколько шагов, то это станет почти невыполнимой задачей. Также важным минусом такого подхода стало странное поведение анимации: размер анимированного объекта, по непонятным для меня причинам, был больше, чем при обычной последовательности анимаций. Попытки разобраться ни к чему не привели, а вникать глубже я уже не стал — подход мне всё равно не нравился. Но я решил развить эту идею и разбить каждый шаг на отдельный AnimatorSet. Вот что вышло:
Некорректная работа анимации, плохой подход, всё плохо. Вновь я обратился к Google, и наткнулся на то, что Animation уже является Legacy code, то есть устарел и не поддерживается, хотя и используется.
Я понял, что нужно делать анимации совершенно иначе. И вот на просторах Android Developers я наткнулся на Animator. Попытка сделать анимацию с его помощью выглядела так:
Анимация работала безупречно, а значит, поиски можно считать оконченными. Единственное, что при работе с Animator нужно помнить, не запущен ли уже какой-то Animator для конкретной view, потому что в противном случае старый продолжит выполнятся, как ни в чем не бывало.
Глубже в Animator
Я начал поиски того, что ещё интересного можно сделать с помощью Animator. Полёт мысли привёл меня к следующему:
При нажатии на кнопку одновременно выполняется четыре Animator’a:
Источник
Android properties that can be animated with ObjectAnimator
I am starting to play around with Property Animations over view animations as I have a view that needs to scale and push others out of the way as it does. I’ve seen some examples but I’m just wondering if there is anywhere that provides a list of the properties that can be altered using these classes. For example, I saw one tutorial that did a quick rotation using:
Which is quite cool, but I wouldn’t have known the rotation property if not for that exact tutorial, is there any comprehensive list of what can be done? The particular property I want to animate is the weight of a view within a LinearLayout, if anyone has any advice on that specifically.
4 Answers 4
Better late than never, so here is the comprehensive list of the properties that can be animated with ObjectAnimator.
The Docs imply that any value can be used with ObjectAnimator as long as you follow a naming convention:
The object property that you are animating must have a setter function (in camel case) in the form of set
() . Because the ObjectAnimator automatically updates the property during animation, it must be able to access the property with this setter method. For example, if the property name is foo , you need to have a setFoo() method. If this setter method does not exist, you have three options:
- Add the setter method to the class if you have the rights to do so.
- Use a wrapper class that you have rights to change and have that wrapper receive the value with a valid setter method and forward it to the original object.
- Use ValueAnimator instead.
With respect to your question, View has the method setRotation(float) — that gives you a hint it can be used. In particular here’s how you would do it with a particular TimeInterpolator :
You can read the docs for more details on the expectations of ObjectAnimator .
Источник
Анимации в Android на базе Kotlin и RxJava
Привет, Хабр! В прошлом году на MBLT DEV выступал Ivan Škorić из PSPDFKit c докладом о создании анимаций в Android на базе Kotlin и библиотеки RxJava.
Приёмы из доклада я сейчас использую в работе над своим проектом, они здорово помогают. Под катом — расшифровка доклада и видеозапись, теперь этими приёмами можете воспользоваться и вы.
Анимация
В Android есть 4 класса, которые применяются как бы по умолчанию:
- ValueAnimator — этот класс предоставляет простой механизм синхронизации для запуска анимаций, которые вычисляют анимированные значения и устанавливают их для View.
- ObjectAnimator — это подкласс ValueAnimator, который позволяет поддерживать анимацию для свойств объекта.
- AnimatorSet применяется для создания последовательности анимаций. Например, у вас есть последовательность из анимаций:
- View выезжает слева на экране.
- После завершения первой анимации мы хотим выполнить анимацию появления для другой View и т.д.
ValueAnimator
Разберём фреймворк ValueAnimator. Он применяется для изменения значения. Вы задаёте диапазон значений через ValueAnimator.ofFloat для примитивного типа float от 0 до 100. Указываете значение длительности Duration и запускаете анимацию.
Рассмотрим на примере:
Здесь добавляем UpdateListener и при каждом обновлении будем двигать нашу View по горизонтали и менять её положение от 0 до 100, хотя это не очень хороший способ выполнения этой операции.
ObjectAnimator
Ещё один пример реализации анимации — ObjectAnimator:
Даём ему команду изменить у нужной View конкретный параметр до определённого значения и выставляем время методом setDuration. Суть в том, что в вашем классе должен находиться метод setTranslationX, потом система через рефлексию найдёт этот метод, а затем будет происходить анимирование View. Проблема в том, что здесь используется рефлексия.
AnimatorSet
Теперь рассмотрим класс AnimatorSet:
На самом деле, он не очень удобен в применении, особенно для большого количества объектов. Если вы хотите делать более сложные анимации — например устанавливать задержку между появлением анимаций, и чем больше анимаций вы хотите выполнять, тем сложнее всё это контролировать.
ViewPropertyAnimator
Последний класс — ViewPropertyAnimator. Он является одним из лучших классов для анимирования View. Это отличный API для введения последовательности запускаемых вами анимаций:
Запускаем метод ViewCompat.animate, который возвращает ViewPropertyAnimator, и для анимирования translationX задаём значение 50, для параметра translatonY — 100. Затем указываем длительность анимации, а также интерполятор. Интерполятор определяет последовательность, в которой будут появляться анимации. В данном примере используется интерполятор, который ускоряет начало анимации и добавляет замедление в конце. Также добавляем задержку для старта анимации. Кроме этого, у нас есть AnimatorListener. С его помощью можно подписываться на определённые события, которые возникают во время выполнения анимации. У данного интерфейса есть 4 метода: onAnimationStart, onAnimationCancel, onAnimationEnd, onAnimationRepeat.
Как правило, нас интересует только завершение анимации. В API Level 16
добавили withEndAction:
В ней можно определить интерфейс Runnable, и после завершения показа конкретной анимации выполнится действие.
Теперь несколько замечаний по поводу процесса создания анимаций в целом:
- Метод start() не обязателен: как только вы вызываете метод animate(), вводится последовательность анимаций. Когда ViewPropertyAnimator будет настроен, система запустит анимацию сразу, как будет готова это сделать.
- Только один класс ViewPropertyAnimator может анимировать только конкретный View. Поэтому если вы хотите выполнять несколько анимаций, например, вам хочется, чтобы что-то двигалось, и при этом увеличивалось в размерах, то нужно указать это в одном аниматоре.
Почему мы выбрали RxJava?
Начнём с простого примера. Предположим, мы создаем метод fadeIn:
Это достаточно примитивное решение, и чтобы применить его для своего проекта, вам нужно будет учесть некоторые нюансы.
Мы собираемся создать CompletableSubject, который будем использовать, чтобы дожидаться завершения анимаций, а затем при помощи метода onComplete отправлять сообщения подписчикам. Для последовательного запуска анимаций необходимо стартовать анимацию не сразу, а как только на неё кто-то подпишется. Таким образом можно последовательно запускать несколько анимации в реактивном стиле.
Рассмотрим саму анимацию. В ней передаём View, над которой будет совершаться анимация, а также указываем длительность анимации. И поскольку это анимация — появление, то мы должны указать прозрачность 1.
Попробуем использовать наш метод и создадим простую анимацию. Предположим, у нас на экране есть 4 кнопки, и мы хотим добавить для них анимацию появления длительностью 1 секунду:
В результате получается вот такой лаконичный код. С помощью оператора andThen можно запускать анимации последовательно. Когда мы подпишемся на него, он отправит событие doOnSubscribe к Completable, который стоит первым в очереди на исполнение. После его завершения он подпишется ко второму, третьему, и так по цепочке. Поэтому если на каком-то этапе появляется ошибка, то вся последовательность выдаёт ошибку. Нужно также указать значение alpha 0 до начало анимации, чтобы кнопки были невидимыми. И вот как это будет выглядеть:
Используя Kotlin, то мы можем использовать расширения:
Для класса View добавили функцию-расширение. В дальнейшем нет необходимости передавать в метод fadeIn аргумент View. Теперь можно внутри метода заменить все обращения к View на ключевое слово this. Это то, на что способен язык Kotlin.
Посмотрим, как изменился вызов этой функции в нашей цепочке анимаций:
Теперь код выглядит более понятным. В нём явно указано, что мы хотим применить к нужному отображению анимацию с определённой продолжительностью. При помощи оператора andThen создаём последовательную цепочку из анимаций ко второй, третьей кнопке и так далее.
Всегда указываем длительность анимаций, это значение одинаково для всех отображений — 1000 миллисекунд. На помощь снова приходит Kotlin. Мы можем сделать значение времени по умолчанию.
Если не указывать параметр duration, то время автоматически будет установлено как 1 секунда. Но если мы захотим для кнопки под номером 2 увеличить это время до 2 секунд, мы просто указываем это значение в методе:
Запуск двух анимаций
Мы смогли запустить последовательность из анимаций с помощью оператора andThen. Что делать, если понадобится запустить одновременно 2 анимации? Для этого в RxJava существует оператор mergeWith, который позволяет объединять элементы Completable таким образом, что они будут запускаться одновременно. Этот оператор запускает все элементы и заканчивает работу после того, как будет показан последний элемент. Если поменять andThen на mergeWith, то получим анимацию, в которой все кнопки появляются одновременно, но кнопка 2 будет появляться немного дольше остальных:
Теперь мы можем группировать анимации. Попробуем усложнить задачу: например, мы хотим, чтобы сначала одновременно появились кнопка 1 и кнопка 2, а затем — кнопка 3 и кнопка 4:
Объединяем первую и вторую кнопки оператором mergeWith, повторяем действие для третьей и четвертой, и запускаем эти группы последовательно с помощью оператора andThen. Теперь улучшим код, добавив метод fadeInTogether:
Он позволит запускать анимацию fadeIn для двух View одновременно. Как изменилась цепочка анимаций:
В итоге получится следующая анимация:
Рассмотрим более сложный пример. Предположим, нам нужно показывать анимацию с некоторой заданной задержкой. В этом поможет оператор interval:
Он будет генерировать значения каждые 100 миллисекунд. Каждая кнопка будет появляться спустя 100 миллисекунд. Далее указываем ещё один Observable, который будет эмитить кнопки. В данном случае у нас 4 кнопки. Воспользуемся оператором zip.
Перед нами потоки событий:
Первый соответствует timeObservable. Этот Observable будет генерировать цифры с определённой периодичностью. Предположим, она будет составлять 100 миллисекунд.
Второй Observable будет генерировать view. Оператор zip ждёт, пока первый объект появится в первом потоке, и соединяет его с первым объектом из второго потока. Несмотря на то, что все эти 4 объекта во втором потоке появятся сразу, он будет ждать, пока объекты начнут появляться на первом потоке. Таким образом, первый объект из первого потока будет соединяться с первым объектом из второго в виде нашего view, а 100 миллисекунд спустя, когда появится новый объект, оператор объединит его со вторым объектом. Поэтому view будут появляться с опеределённой задержкой.
Разберёмся с BiFinction в RxJava. Эта функция получает на вход два объекта, делает какие-то операции над ними и возвращает третий объект. Мы хотим взять объекты time и view и получить Disposable за счёт того, что мы вызываем анимацию fadeIn и подписываемся subscribe. Значение time нам не важно. В итоге получаем вот такую анимацию:
VanGogh
Расскажу про проект, который Иван начал разрабатывать для MBLT DEV 2017.
В библиотеке, которую разработал Иван, представлены различные оболочки для анимаций. Мы уже рассматривали это выше. В ней также содержатся готовые анимации, которые можно использовать. Вы получаете обобщённый набор инструментов для создания собственных анимаций. Эта библиотека предоставит вам более мощные компоненты для реактивного программирования.
Рассмотрим библиотеку на примере:
Предположим, вы хотите создать появляющуюся анимацию, но в этот раз вместо объекта Completable появляется AnimationCompletable. Данный класс наследуется от Completable, так что теперь появляется больше функций. Одной важной особенностью предыдущего кода было то, что отменять анимации было нельзя. Теперь можно создать объект AnimationCompletable, который заставляет анимацию остановиться, как только мы отпишемся от неё.
Создаём появляющуюся анимацию с помощью AnimationBuilder — один из классов библиотеки. Указываем, к какой View будет применена анимация. По сути этот класс копирует поведение ViewPropertyAnimator, но с разницей в том, что на выходе получаем поток.
Далее выставляем alpha 1f и длительность 2 секунды. Затем cобираем анимацию. Как только вызываем оператор build, появляется анимация. Присваиваем анимации свойство не изменяемого объекта, поэтому он сохранит эти характеристики для её запуска. Но сама анимация не запустится.
Вызываем toCompletable, который создаст AnimationCompletable. Обернёт параметры этой анимации в своеобразную оболочку для реактивного программирования, и как только вы подпишетесь на него — запустит анимацию. Если отключить его до завершения процесса, анимация закончится. Также теперь можно добавить функцию обратного вызова. Можно прописать операторы doOnAnimationReady, doOnAnimationStart, doOnAnimationEnd и тому подобное:
В этом примере мы показали, как удобно использовать AnimationBuilder, и изменили состояние нашей View перед запуском анимации.
Видеозапись доклада
Мы рассмотрели один из вариантов создания, компоновки и настройки анимации с помощью Kotlin и RxJava. Вот ссылка на проект, в котором описаны базовые анимации и примеры для них, а также основные оболочки для работы с анимацией.
Помимо расшифровки делюсь видеозаписью доклада:
Спикеры MBLT DEV 2018
До MBLT DEV 2018 осталось чуть больше двух месяцев. У нас выступят:
- Laura Morinigo, Google Developer Expert
- Kaushik Gopal, автор подкаста Fragmented
- Артём Рудой, Badoo
- Дина Сидорова, Google, и другие.
Уже завтра стоимость билета изменится. Регистрируйся сегодня.
Источник