preface

When we use various apps in daily life, we will find that the following type of vector icon animation more. Icon animation is an icon effect recommended by Material Design. Of course for me, cool effects are a good reason to learn vector icon animation.

VectorDrawable

SVG and VectorDrawable
  • Vector image: Vector image and traditional PNG, JPG and other image formats, is the typical difference between fish and fish. Vector map stores the method of drawing pictures, rather than the arrangement of pixels, so no matter how many times the vector map is enlarged, as long as the icon is drawn according to the path of equal scale scaling, there is no problem of Mosaic. The text displayed on our computers does just that.
  • SVG is the most common vector format, whereas in Android we use a VectorDrawable.
  • Generally speaking, the generation of vector images is not necessary for us to care about, if you need to find their vector images, you can go to iconfont to find.
  • SVG2VectorDrawable is a useful plugin for converting SVG to vectorDrawable from AndroidStudio.
VectorDrawable files and SVG directives

Knowing some SVG instructions and how vectors are drawn will help us in future development. We can start with a simple VecotrDrawable file.

<? xml version="1.0" encoding="utf-8"? > <! --res/drawable/vd_check.xml--> <vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="100dp"
    android:height="100dp"
    android:viewportHeight="24"
    android:viewportWidth="24">
    <path
        android:name="check"
        android:pathData="The M4, 10 L9, 16 L20, 4"
        android:strokeColor="#35931d"
        android:strokeWidth="3" />
</vector>
Copy the code

The green and harmonious little check box I drew with the vd_check file above. Let’s interpret this file:

  1. Vector tag: Indicates that this is a vector graph.
    • ViewportHeight /viewWidth: The length and width of the vector image, which is also used for drawing later. The top left corner of the icon is (0,0) and the bottom right corner is (viewWidth,viewHeight).
  2. Group tags: Groups have properties that path does not have. If you want to animate these properties, you have to nest a layer of group tags around the path.
    • Name: The animation will find this object by name.
    • rotation|scaleX|pivotX.. : These attributes are familiar
  3. Path label: A continuous line or plane. A vector graph consists of one or more paths.
    • Name: The animation will find this object by name.
    • StorkeColor: Color of a line segment.
    • StrokeWidth: width of a line segment.
    • StrokeAlpha: Transparency of a line segment.
    • StrokeLineCap: the end of the line style butt (off) | round (rounded corners) | square (rectangular)
    • FillColor: indicates the filling color.
    • FillAlpha: Fill transparency.
  4. PathData attribute: pathData is an attribute of Path, which contains the SVG language used to describe Path. We only need to know a few key words to understand.
The keyword explain
M x,y Let me move my brush to the point from (x,y). Usually represents the beginning of a path.
L x,y Let me draw a line segment that connects to (x,y).
Q x1,y1 x,y Bessel second order curve. It goes through (x1,y1) to (x,y).
C x1,y1 x2,y2 x,y Bessel third order line. It goes through (x1,y1) and (x2,y2) to (x,y).
Z A closed path. Draw a line segment to the starting point.

Now looking back at the pathData of the harmony hook, it is very simple:

The M4, L9 10, 16 L20, 4

Starting at (4,10), draw a line segment to (9,16), and another line segment to (20,4). A meal a pull, green small hook spring on the paper.

Of course, if you are faced with something more complicated than a tick, such as a perfect heart, or an icon for Canton Tower, it is better to ask the UI to generate SVG for you.

animated-vector

Now that we have the vector picture, let’s get the vector picture moving. Speaking of animation, of course it’s property animation!

  • Groups and paths each have their own attributes, so nest groups as needed.
  • Notice the name attribute, our animation will use the name to find the corresponding object. Here is my modified vector with a group added.
<? xml version="1.0" encoding="utf-8"? > <! --vd_check.xml--> <! --vd_check.xml--> <vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="100dp"
    android:height="100dp"
    android:viewportHeight="24"
    android:viewportWidth="24">
    <group
        android:name="g_rotation"
        android:pivotX="12"
        android:pivotY="12"
        android:rotation="0">
            <path
                android:name="check"
                android:pathData="The M4, 10 L9, 16 L20, 4"
                android:strokeAlpha="1.0"
                android:strokeColor="@color/colorPrimary"
                android:strokeLineCap="round"
                android:strokeWidth="1" />
    </group>
</vector>
Copy the code

What animation are we going to add? Um, rotation, transparency, color, I want it all!

<? xml version="1.0" encoding="utf-8"? > <! --/res/animator/rotation_round.xml--> <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:propertyName="rotation"
    android:valueFrom="0"
    android:valueTo="360" />
Copy the code
<? xml version="1.0" encoding="utf-8"? > <! --/res/animator/alpha_animator.xml--> <set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="sequentially">
    <objectAnimator
        android:duration="500"
        android:propertyName="strokeAlpha"
        android:valueFrom="1f"
        android:valueTo="0f" />
    <objectAnimator
        android:duration="500"
        android:propertyName="strokeAlpha"
        android:valueFrom="0f"
        android:valueTo="1f" />
</set>
Copy the code
<? xml version="1.0" encoding="utf-8"? > <! --res/animator/stroke_color_animator.xml--> <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:propertyName="strokeColor"
    android:valueFrom="@color/colorPrimary"
    android:valueTo="@color/colorAccent"
    android:duration="1000"/>
Copy the code

AnimatedVector comes out in style, gluing the vector and the animation file together. It’s easy to use. We specify the vector with the drawable attribute, and then bind the animation to the object with the target tag.

<? xml version="1.0" encoding="utf-8"? > <! --avd_check.xml--> <animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/vd__check">
    <target
        android:name="g_rotation"
        android:animation="@animator/rotation_around" />
    <target
        android:name="check"
        android:animation="@animator/stroke_color_animator" />
    <target
        android:name="check"
        android:animation="@animator/alpha_animator" />
</animated-vector>
Copy the code

Finally, it needs to be triggered in code. Avd_check.xml is assigned to the ImageView as an image. When we need to call the animation, we get the Drawable of the ImageView. After converting to Animatable, we call the start() method.

 <ImageView
   android:id="@+id/img_check"
   android:layout_width="48dp"
   android:layout_height="48dp"
   app:srcCompat="@drawable/avd_check" />
Copy the code

··· img_check.setOnClickListener { val drawable = img_check.drawable (drawable as Animatable).start() } ···

And then there’s the effect.

Of course, if you just want to make it easier, you don’t need to write the animation in a separate file. You can also write it in the target tag.

TrimPath Path clipping

TrimPath is exactly the same as the animation above, but uses a few vector-specific properties. Let’s see what trimPath can do first.

TrimPath a total of three relevant attributes: trimPathStart, trimPathEnd, trimPathOffset, are float values, values range from 0 to 1. Indicates where the path starts, where it ends, and how far it is from the starting point. How it works is up to our imagination.

Now, let’s do an experiment with our little hook.

As always, you need to write an animation file

<? xml version="1.0" encoding="utf-8"? > <! --trim_path_animator.xml--> <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:interpolator="@android:interpolator/linear"
    android:propertyName="trimPathEnd"
    android:valueFrom="0.0"
    android:valueTo="1.0"
    android:valueType="floatType" />
Copy the code

Modify the animatedVector file

<? xml version="1.0" encoding="utf-8"? > <! --avd_check.xml--> <animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/vd__check">
    <target
        android:name="check"
        android:animation="@animator/trim_path_animator" />
</animated-vector>
Copy the code

Bang!

If your pathdata is broken, for example: “M,L,L M,L Z”. If two M’s appear, the path is split into two segments, and pathTrim only applies to the first segment.

Morphing paths

Here comes the big story, path change. If you can animate the strokeAplha, rotation properties, you can animate the pathData property. The result is the following effects.

ValueFrom and valueTo in the property animation have two different paths, and the path will automatically change. Note that the drawing instructions for both paths need to be the same in number and structure. For example, if the first path instruction (omits coordinates) is “M,L,L,C,Z”, the second path instruction should also be of the form “M,L,L,C,Z”.

Okay, we can give it a try. Because the current tick command is too little, not good to play my small universe, so I add a few instructions. And the goal is to turn that little tick into a little circle. So I created the following two paths. They all used one M instruction and four C instructions (yes, C can only draw straight lines). For easy administration, I’ve put both paths in one XML.

<? xml version="1.0" encoding="utf-8"? > <resources> <string name="check_path">M4,10 C10,16 10,16 10,16 C13,13 13,13 C16,10 16,10 16 C20,6 20,6 20,6</string> <string name="circle_path"</string> </resources> </resources> </resources>Copy the code

Then also animation and animatedVector:

<? xml version="1.0" encoding="utf-8"? > <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="500"
    android:interpolator="@android:interpolator/linear"
    android:propertyName="pathData"
    android:valueFrom="@string/check_path"
    android:valueTo="@string/circle_path"
    android:valueType="pathType" />
Copy the code
<? xml version="1.0" encoding="utf-8"? > <! --avd_check.xml--> <animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/vd__check">
    <target
        android:name="check"
        android:animation="@animator/path_animator" />
</animated-vector>
Copy the code

And then, clunk clunk clunk.

But as you can see, once my hook becomes a circle, it never comes back, and the animation can’t be done backwards. This is where we need to introduce the last concept, animatedSelecotr.

animated-selector

Animaling-selector allows you to define multiple vectors, use different vectors for different states, and animate the switches between different vectors via animaling-vector. So our next steps are:

  1. Define two vectors: tick and circle
  2. Define two animated- vectors: tick to circle and circle to tick
  3. Define allocation-selector to combine the above files.

Hands on: vector of circles. It’s almost the same as the check mark. Notice I changed the name to circle.

<? xml version="1.0" encoding="utf-8"? > <! --vd_circle.xml--> <vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="100dp"
    android:height="100dp"
    android:viewportHeight="24"
    android:viewportWidth="24">
    <path
        android:name="circle"
        android:pathData="@string/circle_path"
        android:strokeAlpha="1.0"
        android:strokeColor="@color/colorPrimary"
        android:strokeLineCap="round"
        android:strokeWidth="1" />
</vector>
Copy the code

Circle and tick conversion, need two files. Because check to circle is already written above (avd_check.xml, changed to avd_check2circl.xml for better description). This is circle to hook. As you can see, animations can be written directly into animated-vector.

<? xml version="1.0" encoding="utf-8"? > <! --huan --> <animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:drawable="@drawable/vd_circle">
    <target android:name="circle">
        <aapt:attr name="android:animation">
            <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
                android:duration="500"
                android:interpolator="@android:interpolator/fast_out_slow_in"
                android:propertyName="pathData"
                android:valueFrom="@string/circle_path"
                android:valueTo="@string/check_path"
                android:valueType="pathType" />
        </aapt:attr>
    </target>
</animated-vector>
Copy the code

And then we’re left with allocation-selector.

  • Each item specifies two vectors, and both states are represented through state_checked. In fact, there are system-defined states such as stated_checkable, state_SELECTED, etc., and new state variables can also be defined.
  • Transition is an animation that represents transitions between vectors. The attributes are clear: Fromid and toId represent the ids of the two items before and after the transformation. Drawable is antemator – vector.
<? xml version="1.0" encoding="utf-8"? > <animated-selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/check"
        android:drawable="@drawable/vd__check"
        android:state_checked="true" />
    <item
        android:id="@+id/circle"
        android:drawable="@drawable/vd_circle"
        android:state_checked="false" />

    <transition
        android:drawable="@drawable/avd_check2circle"
        android:fromId="@id/check"
        android:toId="@id/circle" />

    <transition
        android:drawable="@drawable/avd_circle2check"
        android:fromId="@id/circle"
        android:toId="@id/check" />
</animated-selector>
Copy the code

Use it in app:srcCompat.

  <ImageView
    android:id="@+id/img_check_selector"
    android:layout_width="48dp"
    android:layout_height="48dp"
    app:srcCompat="@drawable/asl_check" />
Copy the code

Then set the different states in the code using the setImageState method and the icon will change itself.

img_check_selector.setOnClickListener { isCheckSelect = ! isCheckSelect img_check_selector.setImageState(intArrayOf(if (isCheckSelect) android.R.attr.state_checked else -android.R.attr.state_checked), true)}Copy the code

app:srcCompat

SrcCompat is specifically for vector drawable, so it’s best to use srcCompat instead of Android: SRC.

After the language

Here, we can see the power of vector icon animation, ignoring the Mosaic, full of imagination, make our app more vivid, more in line with Material Design. However, there are also some problems with generating the vector Drawable, writing various animated- selectors, and animated-vector files. You win some, you lose some.

It is better to start at once than to lament the difficulty of the road.

* Last but not least, thank you for reading and leave a comment. *

The resources

  • Arroyal-details: arroyal-arroyal-details: arroyal-arroyal-details: arroyal-arroyal-Details: arroyal-Arroyal-Details: Arroyal-Arroyal-Details
  • An Introduction to Icon Animation Techniques
  • Android Advanced Animation (2)