Векторная анимация в Android (часть 2)
Прежде чем приступить к анимации, необходимо понять, какие возможности нам предлагает Android. Типы анимации в Android можно условно разделить на базовую и комплексную.
К базовой анимации можно отнести простую манипуляцию параметрами объекта, такие как положение, масштаб, поворот и опорная точка (точка, относительно которой происходит анимация). Такую анимацию мы можем применять только к Group внутри VectorDrawable (кроме того, такие типы анимации применимы и к любым View, например: Button, TextView и т.д, но в статье мы будем рассматривать базовую анимацию только применительно к векторным объектам).
К комплексной анимации можно отнести анимацию, применяемую только к Path в VectorDrawable: манипуляция цветом заливки и обводки, альфой, толщиной обводки, тримминг обводки и изменение геометрии (морфинг). Все эти свойства и их значения описываются внутри ObjectAnimator. ObjectAnimator предназначен для анимации только одного свойства. Для анимации нескольких свойств добавляются соответствующие аниматоры, которые можно объединить в set, но об этом позже.
Схема поможет разобраться в том, какую анимацию к чему мы можем применять.
В первом ObjectAnimator перечислены свойства: fillColor, fillAlpha, strokeColor, strokeAlpha, strokeWidth, trimPathStart, trimPathEnd, trimPathOffset, pathData, которые мы можем применять только к Path и Clip-Path. Во втором ObjectAnimator свойства, применяемые только к Group: Alpha, translateX, translateY, scaleX ,scaleY, rotation, pivotX, pivotY.
Простая анимация
Начнем с базовой векторной анимации. Попробуем сделать анимацию открытия и закрытия замка. Ранее мы уже подготовили файл для такой анимации, разделив иконку на 2 группы. Подробнее про подготовку файла можно прочитать в первой части статьи.
Исходный lock.xml выглядит так:
<?xml version="1.0" encoding="utf-8"?> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportHeight="24" android:viewportWidth="24"> <path android:fillColor="#000000" android:pathData="M6,9c-1.1,0-2,0.9-2,2v10c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V11c0-1.1-0.9-2-2-2H6z M12,18c-1.1,0-2-0.9-2-2s0.9-2,2-2 s2,0.9,2,2S13.1,18,12,18z" /> <path android:fillColor="#000000" android:pathData="M12,2C9.2,2,7,4.2,7,7v5h1.9V7c0-1.7,1.4-3.1,3.1-3.1s3.1,1.4,3.1,3.1v2H17V7C17,4.2,14.8,2,12,2z" /> </vector>
Сначала переведем статичный вектор в анимированный, обернув все в тэг animated-vector
и добавив appt:attr
тэги, для того чтобы собрать xml bundle. Чтобы анимировать дужку замка, необходимо первый path
обернуть в group
и присвоить ему уникальное имя (например, shackle
).
<?xml version="1.0" encoding="utf-8"?> <animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"> <aapt:attr name="android:drawable"> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportHeight="24" android:viewportWidth="24"> <path android:fillColor="#000000" android:pathData="M6,9c-1.1,0-2,0.9-2,2v10c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V11c0-1.1-0.9-2-2-2H6z M12,18c-1.1,0-2-0.9-2-2s0.9-2,2-2 s2,0.9,2,2S13.1,18,12,18z" /> <group android:name="shackle"> <path android:fillColor="#000000" android:pathData="M12,2C9.2,2,7,4.2,7,7v5h1.9V7c0-1.7,1.4-3.1,3.1-3.1s3.1,1.4,3.1,3.1v2H17V7C17,4.2,14.8,2,12,2z" /> </group> </vector> </aapt:attr> </animated-vector>
Затем создадим target
внутри animated-vector
, в котором укажем название группы, которую будем анимировать: shakle
. Внутри него создадим ObjectAnimator, который будет отвечать за анимацию открытия дужки, используя свойство translateY
. В propertyName
указывается свойство, которое мы собираемся анимировать. В нашем случае это translateY
. Свойство duration
— время выполнения анимации в миллисекундах. В valueFrom
и valueTo
указываются начальные и конечные значения в dp соответственно. valueType
в нашем случае имеет значение floatType
, а если бы мы анимировали значения цвета, то был бы colorType
, а для морфинга использовался бы pathType
.
<?xml version="1.0" encoding="utf-8"?> <animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"> <aapt:attr name="android:drawable"> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportHeight="24" android:viewportWidth="24"> <path android:fillColor="#000000" android:pathData="M6,9c-1.1,0-2,0.9-2,2v10c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V11c0-1.1-0.9-2-2-2H6z M12,18c-1.1,0-2-0.9-2-2s0.9-2,2-2 s2,0.9,2,2S13.1,18,12,18z" /> <group android:name="shackle"> <path android:fillColor="#000000" android:pathData="M12,2C9.2,2,7,4.2,7,7v5h1.9V7c0-1.7,1.4-3.1,3.1-3.1s3.1,1.4,3.1,3.1v2H17V7C17,4.2,14.8,2,12,2z" /> </group> </vector> </aapt:attr> <target android:name="shackle"> <aapt:attr name="android:animation"> <objectAnimator android:duration="1000" android:interpolator="@android:anim/accelerate_decelerate_interpolator" android:propertyName="translateY" android:repeatCount="infinite" android:valueFrom="0" android:valueTo="-2" android:valueType="floatType" /> </aapt:attr> </target> </animated-vector>
В результате у нас получилась простая анимация открытия замка. Для того чтобы добавить анимацию закрытия, необходимо создать новый objectAnimator. Для применения нескольких анимаций к одному target лучше всего использовать set.
Пример другой анимации с использованием set.
<?xml version="1.0" encoding="utf-8"?> <animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"> <aapt:attr name="android:drawable"> <vector android:width="48dp" android:height="24dp" android:viewportHeight="24" android:viewportWidth="48"> <group android:name="gear_group"> <path android:name="gear_path" android:fillColor="#FF1744" android:pathData="M19.43 12.98c.04-.32 .07 -.64 .07 -.98s-.03-.66-.07-.98l2.11-1.65c.19-.15 .24 -.42 .12 -.64l-2-3.46c-.12-.22-.39-.3-.61-.22l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65C14.46 2.18 14.25 2 14 2h-4c-.25 0-.46 .18 -.49 .42 l-.38 2.65c-.61 .25 -1.17 .59 -1.69 .98 l-2.49-1c-.23-.09-.49 0-.61 .22 l-2 3.46c-.13 .22 -.07 .49 .12 .64 l2.11 1.65c-.04 .32 -.07 .65 -.07 .98 s.03 .66 .07 .98 l-2.11 1.65c-.19 .15 -.24 .42 -.12 .64 l2 3.46c.12 .22 .39 .3 .61 .22 l2.49-1c.52 .4 1.08 .73 1.69 .98 l.38 2.65c.03 .24 .24 .42 .49 .42 h4c.25 0 .46-.18 .49 -.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.23 .09 .49 0 .61-.22l2-3.46c.12-.22 .07 -.49-.12-.64l-2.11-1.65zM12 15.5c-1.93 0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5 3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z" /> </group> </vector> </aapt:attr> <target android:name="gear_path"> <aapt:attr name="android:animation"> <objectAnimator android:name="color" android:duration="2000" android:interpolator="@android:anim/accelerate_decelerate_interpolator" android:propertyName="fillColor" android:repeatCount="infinite" android:repeatMode="reverse" android:valueFrom="#FF1744" android:valueTo="#4527A0" android:valueType="colorType" /> </aapt:attr> </target> <target android:name="gear_group"> <aapt:attr name="android:animation"> <set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> <objectAnimator android:duration="0" android:interpolator="@android:anim/accelerate_decelerate_interpolator" android:propertyName="pivotX" android:valueFrom="12" android:valueTo="12" android:valueType="floatType" /> <objectAnimator android:duration="0" android:interpolator="@android:anim/accelerate_decelerate_interpolator" android:propertyName="pivotY" android:valueFrom="12" android:valueTo="12" android:valueType="floatType" /> <objectAnimator android:duration="2000" android:interpolator="@android:anim/accelerate_decelerate_interpolator" android:propertyName="rotation" android:repeatCount="infinite" android:repeatMode="reverse" android:valueFrom="0" android:valueTo="160" android:valueType="floatType" /> <objectAnimator android:duration="2000" android:interpolator="@android:anim/accelerate_decelerate_interpolator" android:propertyName="translateX" android:repeatCount="infinite" android:repeatMode="reverse" android:startOffset="0" android:valueFrom="0" android:valueTo="24" android:valueType="floatType" /> </set> </aapt:attr> </target> </animated-vector>
Свойство ordering
отвечает за порядок воспроизведения анимации в set. Может принимать два значения: together
(одновременное воспроизведение — значение по умолчанию), sequentially
(последовательное воспроизведение). Помимо этого, можно использовать свойство repeatMode
для указания типа повтора анимации, а repeatCount
указывает количество повторов. Стоит учитывать, что если в set используется последовательное воспроизведение, то repeatCount=infinite
будет работать только в первом objectAnimator
, так как очередь до остальных никогда не дойдёт.
Все приведенные примеры доступны для самостоятельного изучения в проекте на GitHub.
Источник: Векторная анимация в Android (часть 2)