svg Vector 章节(2):绘制图形并改变groups paths

1. 绘制并改变groups paths

在上一节中,我们学习了如何path通过直接修改其属性(如不透明度和颜色)来更改的外观。除此之外,VectorDrawables还支持使用标记进行组转换<group>,这允许我们path使用以下可设置动画的属性一次将转换应用于多个:

名称 元素类型 值类型 解释
android:pivotX <group> float 定义缩放和旋转等动作时候,会以该 group 的 pivotX的值作为参考点。该值相对于 vector 的 viewport 值来指定的。
android:pivotY <group> float 定义缩放和旋转等动作时候,会以该 group 的 pivotY的值作为参考点。该值相对于 vector 的 viewport 值来指定的。
android:rotation <group> float 定义该 group 的路径旋转多少度,这样图片就被旋转了
android:scaleX <group> float 定义 X 轴的缩放倍数
android:scaleY <group> float 定义 Y 轴的缩放倍数
android:translateX <group> float 定义移动 X 轴的位移。相对于 vector 的 viewport 值来指定的。
android:translateY <group> float 定义移动 Y 轴的位移。相对于 vector 的 viewport 值来指定的。

了解嵌套group转换的应用顺序很重要。要记住的两个规则是:

  1. 子group继承其父组所应用的转换,意思是当前每次的转变都是以上一个为准
  2. group按比例,旋转和平移的顺序对其进行的转换。



那么让我们通过一些简单的例子来学习,以下是group应用于集中图标的转换相关例子:

1.1 放大转换相关例子:

放大转换.png
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="48dp"
    android:height="48dp"
    android:viewportHeight="12"
    android:viewportWidth="12">

    <!-- 1.以pivotX和pivotY都是坐标为6点作为中心,以scaleX也即是横向放大1.5倍 -->
    <group
        android:pivotX="6"
        android:pivotY="6"
        android:scaleX="1.5">
        <path
            android:name="iconPath"
            android:fillColor="@android:color/white"
            android:pathData="M 4,2.5 L 4,9.5 L 9.5,6 Z" />
    </group>

</vector>

1.2 旋转90度相关例子:

旋转90度
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="48dp"
    android:height="48dp"
    android:viewportWidth="12"
    android:viewportHeight="12">

    <!-- 2.以pivotX和pivotY都是坐标为6点作为中心,而rotation顺时针旋转90度 -->
    <group
        android:pivotX="6"
        android:pivotY="6"
        android:rotation="90">
        <path
            android:name="iconPath"
            android:fillColor="@android:color/white"
            android:pathData="M 4,2.5 L 4,9.5 L 9.5,6 Z" />
    </group>

</vector>

1.3 横向移动2格相关例子:

横向移动2格
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="48dp"
    android:height="48dp"
    android:viewportWidth="12"
    android:viewportHeight="12">

    <!-- 3.这个是平移X角度即使横向移动2格 -->
    <group android:translateX="2" >
        <path
            android:name="iconPath"
            android:fillColor="@android:color/white"
            android:pathData="M 4,2.5 L 4,9.5 L 9.5,6 Z" />
    </group>

</vector>

1.4 3个group结合后的动作相关例子:

3个group结合后的动作

请记住,group是根据顺序而变化,您可以尝试不同的顺序会发现最终显示的结果不一样

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="48dp"
    android:height="48dp"
    android:viewportWidth="12"
    android:viewportHeight="12">

    <!-- 1.以pivotX和pivotY都是坐标为6点作为中心,而scaleX也即是横向放大1.5倍 -->
    <group android:scaleX="1.5" android:pivotX="6" android:pivotY="6">
        <!-- 2.以pivotX和pivotY都是坐标为6点作为中心,而rotation顺时针旋转90度 -->
        <group android:rotation="90" android:pivotX="6" android:pivotY="6">
            <!-- 3.这个是平移X角度即使横向移动2格 -->
            <group android:translateX="2">
                <path
                    android:name="iconPath"
                    android:fillColor="@android:color/white"
                    android:pathData="M 4,2.5 L 4,9.5 L 9.5,6 Z" />
            </group>
        </group>
    </group>

</vector>

将 <group> 组合一起可以实现各种炫酷效果。

在上面每个图中,每个图标的源代码都可以在demo2地址上找到

2.实现简单的动画

在讲解动画之前,我们先了解动画是基于xml设置,而xml的标签意思设置如下:

<set android:ordering=["together" | "sequentially"]>

    <objectAnimator
        android:propertyName="string"
        android:duration="int"
        android:valueFrom="float | int | color"
        android:valueTo="float | int | color"
        android:startOffset="int"
        android:repeatCount="int"
        android:repeatMode=["repeat" | "reverse"]
        android:valueType=["intType" | "floatType"] />

    <animator
        android:duration="int"
        android:valueFrom="float | int | color"
        android:valueTo="float | int | color"
        android:startOffset="int"
        android:repeatCount="int"
        android:repeatMode=["repeat" | "reverse"]
        android:valueType=["intType" | "floatType"] />

    <set>
        ...
    </set>
</set>

名称 值类型 详细解释
<set> 它是一个集合,包含了其他的元素,包括, 和其他的元素,它代表的就是一个AnimatorSet对象
android:ordering "together" / "sequentially" 指定集合中动画播放的顺序。它具有两种类型的值:sequentially和together。第一个表示顺序执行,第二个表示同时执行。默认是together。
<objectAnimator> 它指定一个对象的属性动画,表示一个ObjectAnimator对象。
android:propertyName string 属性名称,例如一个view对象的”alpha”和”backgroundColor”。
android:valueFrom float/int /color 变化开始值
android:valueTo float 变化结束值
android:valueType "intType"/"floatType" 变化值类型,它有两种值:intType和floatType,第二种为默认值
android:duration "int" 持续时间
android:startOffset "int" 动画开始延迟时间
android:repeatCount "int" 重复次数,-1表示无限重复
android:repeatMode "repeat" / "reverse" 重复模式,前提是android:repeatCount为-1,它有两种值:”reverse”和”repeat”,第一个表示反向重复,第二个为顺序重复。
<animator> 它对应的就是ValueAnimator对象。拥有的属性跟<objectAnimator>一样

下面将学习三个不同动画的例子让我们更加深入了解!

2.1 展开折叠效果

展开折叠效果.gif

展开/折叠图标是使用两个矩形路径绘制。单击时,两个路径同时旋转90°并垂直平移以创建过渡。

首先,让我们看看形成这个绘制和动画的代码:

<?xml version="1.0" encoding="utf-8"?>
<animated-selector xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- 展开的图片 -->
    <item
        android:id="@+id/expanded"
        android:drawable="@drawable/ic_expanded"
        android:state_checked="true"/>

    <!-- 收缩的图片 -->
    <item
        android:id="@+id/collapsed"
        android:drawable="@drawable/ic_collapsed" />

    <!-- 从收缩过渡到展开的动画 -->
    <transition
        android:drawable="@drawable/avd_collapsed_to_expanded"
        android:fromId="@id/collapsed"
        android:toId="@id/expanded" />

    <!-- 从展开过渡到收缩的动画 -->
    <transition
        android:drawable="@drawable/avd_expanded_to_collapsed"
        android:fromId="@id/expanded"
        android:toId="@id/collapsed" />

</animated-selector>

接下来让我们一步一步解析这4块注释,首先是看看展开图片怎么组成的,上面说到,这个图形是由两个矩形绘制而成的

2.1.1 首先让我们看看展开的图片

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:tint="?attr/colorControlNormal"
    android:viewportWidth="24"
    android:viewportHeight="24">

    <!-- 整个画布移动到X12即是X中心点,而Y移动到9 -->
    <group
        android:name="chevron"
        android:translateX="12"
        android:translateY="9">
        <!-- 左边的矩形,向右正旋转45度,而Y移动+3=12,即是中心 -->
        <group
            android:name="leftBar"
            android:rotation="45">
            <group android:translateY="3">
                <path
                    android:fillColor="@android:color/white"
                    android:pathData="M 1,-4 L 1,4 L -1,4 L -1,-4 Z" />
            </group>
        </group>
        <!-- 右边的矩形,向右正旋转135度,这个135度会导致矩形往Y轴下面延伸,所以后面一个group需要Y移动-3=6 -->
        <group
            android:name="rightBar"
            android:rotation="135">
            <group android:translateY="-3">
                <path
                    android:fillColor="@android:color/white"
                    android:pathData="M 1,-4 L 1,4 L -1,4 L -1,-4 Z" />
            </group>
        </group>
    </group>

</vector>

https://shapeshifter.design/可以模仿出上图效果,基本结构:

image.png

2.1.2 然后是收缩的图片

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:tint="?attr/colorControlNormal"
    android:viewportWidth="24"
    android:viewportHeight="24">

    <!-- 整个画布移动到X12即是X中心点,而Y移动到15偏下 -->
    <group
        android:name="chevron"
        android:translateX="12"
        android:translateY="15">
        <!-- 左边的矩形,向右正旋转135度后会比较偏上,而旋转后,《Y轴方向偏向左上角》,所以Y移动+3=18会往左上角继续偏移(即是往上),这样就比较居中了 -->
        <group
            android:name="leftBar"
            android:rotation="135">
            <group android:translateY="3">
                <path
                    android:fillColor="@android:color/white"
                    android:pathData="M 1,-4 L 1,4 L -1,4 L -1,-4 Z" />
            </group>
        </group>
        <!-- 右边的矩形,向右正旋转45度,Y移动-3 -->
        <group
            android:name="rightBar"
            android:rotation="45">
            <group android:translateY="-3">
                <path
                    android:fillColor="@android:color/white"
                    android:pathData="M 1,-4 L 1,4 L -1,4 L -1,-4 Z"/>
            </group>
        </group>
    </group>

</vector>

2.1.3 从收缩过渡到展开的动画

<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:drawable="@drawable/ic_collapsed">

    <!-- 这是整个画布的移动动画,收缩过渡到展开,偏下移到偏上,可以发现translateY的valueFrom和valueTo值都是对应收缩和展开图的相应位置的 -->
    <target android:name="chevron">
        <aapt:attr name="android:animation">
            <objectAnimator
                android:duration="250"
                android:interpolator="@android:interpolator/fast_out_slow_in"
                android:propertyName="translateY"
                android:valueFrom="15"
                android:valueTo="9" />
        </aapt:attr>
    </target>

    <!-- 左边矩形旋转,用到了自定义动画差值器,同样的,旋转角度的值都是对应收缩和展开图的相应值的 -->
    <target android:name="leftBar">
        <aapt:attr name="android:animation">
            <objectAnimator
                android:duration="200"
                android:interpolator="@anim/pathmorph_expandcollapse"
                android:propertyName="rotation"
                android:valueFrom="135"
                android:valueTo="45"
                android:valueType="floatType" />
        </aapt:attr>
    </target>

    <!-- 左边矩形旋转,用到了自定义动画差值器,同样的,旋转角度的值都是对应收缩和展开图的相应值的 -->
    <target android:name="rightBar">
        <aapt:attr name="android:animation">
            <objectAnimator
                android:duration="200"
                android:interpolator="@anim/pathmorph_expandcollapse"
                android:propertyName="rotation"
                android:valueFrom="45"
                android:valueTo="135"
                android:valueType="floatType" />
        </aapt:attr>
    </target>

</animated-vector>

从展开过渡到收缩的动画差不多如此。

2.2 闹钟“振铃”效果

闹钟“振铃”效果.gif

在闹钟图标绘制使用两个矩形路径其钟声。单击后,两个矩形路径围绕中心前后旋转,以产生“振铃”效果。

首先我们看看闹钟这个图片如何“画”出来。
老样子,打开https://material.io/resources/icons/?search=access_alarm&icon=access_alarm&style=baseline下载闹钟svg路径,根据上面动画效果显示,是上面两个矩形左右摆动,那我们分割出这两个矩形出来,到时候动画只针对这两个矩形做相应的动作即可。这里有个小诀窍就是以M(根据文章第一章M意思是移动到某点)为起点,Z为结点(根据文章第一章Z意思是闭合该画笔),所以最终闹钟的代码如下:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:tint="?attr/colorControlNormal"
    android:viewportWidth="24"
    android:viewportHeight="24">

    <!-- 顶部需要动画的两个矩形,可以看到button和button_pivot两个group分别translate加12,又translate减12,这样做目的是为了让后面的动画可以让这两个矩形围绕这12中心点抖动。
            至于具体原理我还查不到相关资料,如果有人知道麻烦留言,非常感谢!
            我猜测+12是扩大画布,-12又回到原位置,这样可以定点中心点在12利用抖动。-->
    <group
        android:name="button"
        android:translateX="12"
        android:translateY="12">
        <group
            android:name="button_pivot"
            android:translateX="-12"
            android:translateY="-12">
            <!-- 左边的矩形 -->
            <group
                android:name="right_button">
                <path
                    android:name="path_1"
                    android:fillAlpha="1"
                    android:fillColor="@android:color/white"
                    android:pathData="M 22 5.72 L 17.4 1.86 L 16.11 3.39 L 20.71 7.25 L 22 5.72 Z" />
            </group>
            <!-- 右边的矩形 -->
            <group
                android:name="left_button">
                <path
                    android:name="left_button_path"
                    android:fillAlpha="1"
                    android:fillColor="@android:color/white"
                    android:pathData="M 7.88 3.39 L 6.6 1.86 L 2 5.71 L 3.29 7.24 L 7.88 3.39 Z" />
            </group>
        </group>
    </group>

    <!-- 圆形和表针的组成 -->
    <group
        android:name="alarm"
        android:translateX="12"
        android:translateY="12">
        <group
            android:name="alarm_pivot"
            android:translateX="-12"
            android:translateY="-12">
            <!-- 表针 -->
            <group
                android:name="alarm_hands">
                <path
                    android:name="alarm_hands_path"
                    android:fillAlpha="1"
                    android:fillColor="@android:color/white"
                    android:pathData="M 12.5 8 L 11 8 L 11 14 L 15.75 16.85 L 16.5 15.62 L 12.5 13.25 L 12.5 8 Z" />
            </group>
            <!-- 圆形 -->
            <group
                android:name="alarm_body">
                <path
                    android:name="alarm_outline_path"
                    android:fillAlpha="1"
                    android:fillColor="@android:color/white"
                    android:pathData="M 12 4 C 7.03 4 3 8.03 3 13 C 3 17.97 7.02 22 12 22 C 16.97 22 21 17.97 21 13 C 21 8.03 16.97 4 12 4 Z M 12 20 C 8.13 20 5 16.87 5 13 C 5 9.13 8.13 6 12 6 C 15.87 6 19 9.13 19 13 C 19 16.87 15.87 20 12 20 Z" />
            </group>
        </group>
    </group>

</vector>

然后是非常简单的抖动动画

<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:drawable="@drawable/vd_clock_alarm">

    <!-- 只针对button进行抖动的动画,从0-8一直来回抖动,最终回到0 -->
    <target android:name="button">
        <aapt:attr name="android:animation">
            <set android:ordering="sequentially">
                <objectAnimator
                    android:duration="33"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="rotation"
                    android:valueFrom="0"
                    android:valueTo="8" />
                <objectAnimator
                    android:duration="67"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="rotation"
                    android:valueFrom="8"
                    android:valueTo="-8" />
                <objectAnimator
                    android:duration="67"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="rotation"
                    android:valueFrom="-8"
                    android:valueTo="8" />
                <objectAnimator
                    android:duration="67"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="rotation"
                    android:valueFrom="8"
                    android:valueTo="-8" />
                <objectAnimator
                    android:duration="67"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="rotation"
                    android:valueFrom="-8"
                    android:valueTo="8" />
                <objectAnimator
                    android:duration="67"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="rotation"
                    android:valueFrom="8"
                    android:valueTo="-8" />
                <objectAnimator
                    android:duration="67"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="rotation"
                    android:valueFrom="-8"
                    android:valueTo="8" />
                <objectAnimator
                    android:duration="67"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="rotation"
                    android:valueFrom="8"
                    android:valueTo="-8" />
                <objectAnimator
                    android:duration="33"
                    android:interpolator="@android:interpolator/fast_out_slow_in"
                    android:propertyName="rotation"
                    android:valueFrom="-8"
                    android:valueTo="0" />
            </set>
        </aapt:attr>
    </target>

</animated-vector>

闹钟“振铃”动画就是这么简单。

2.3 单选按钮选择效果

单选按钮选择效果.gif

该图标仅使用两条路径绘制:填充的内点和描边的外圈。当单选按钮在未选中状态到选中状态之间转换时,将为三个属性设置动画:

首先我们看看单选按钮这个图片如何“画”出来。
老样子,打开https://material.io/resources/icons/?search=radio_button_checked&icon=radio_button_checked&style=baseline
下载选中后的图svg路径,根据上面动画效果显示,需要一个未选图片,即使中心是空的,然后还有一个选中后的图片,代码如下:

<!-- 未选图片 -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:tint="?attr/colorControlNormal"
    android:viewportWidth="24"
    android:viewportHeight="24">
    <!-- 外圈,pivotX和pivotY都设置12中心点,这样围绕着中心点放大或者缩小 -->
    <group
        android:name="ring_outer"
        android:pivotX="12"
        android:pivotY="12">
        <path
            android:name="ring_outer_path"
            android:pathData="M 12 2 C 6.48 2 2 6.48 2 12 C 2 17.52 6.48 22 12 22 C 17.52 22 22 17.52 22 12 C 22 6.48 17.52 2 12 2 Z"
            android:strokeWidth="2"
            android:strokeColor="@android:color/white" />
    </group>
    <!-- 内圈,pivotX和pivotY都设置12中心点,这样围绕着中心点放大或者缩小。scaleX和scaleY都设置0去掉内圈 -->
    <group
        android:name="dot_group"
        android:pivotX="12"
        android:pivotY="12"
        android:scaleX="0"
        android:scaleY="0">
        <path
            android:name="dot_path"
            android:fillColor="@android:color/white"
            android:pathData="M 12 7 C 9.24 7 7 9.24 7 12 C 7 14.76 9.24 17 12 17 C 14.76 17 17 14.76 17 12 C 17 9.24 14.76 7 12 7 Z" />
    </group>
</vector>


<!-- 已选图片 -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:tint="?attr/colorControlNormal"
    android:viewportWidth="24"
    android:viewportHeight="24">
    <!-- 外环,pivotX和pivotY都设置12中心点,这样围绕着中心点放大或者缩小 -->
    <group
        android:name="ring_outer"
        android:pivotX="12"
        android:pivotY="12">
        <path
            android:name="ring_outer_path"
            android:pathData="M 12 2 C 6.48 2 2 6.48 2 12 C 2 17.52 6.48 22 12 22 C 17.52 22 22 17.52 22 12 C 22 6.48 17.52 2 12 2 Z"
            android:strokeWidth="2"
            android:strokeColor="@android:color/white" />
    </group>
    <!-- 内圈,pivotX和pivotY都设置12中心点,这样围绕着中心点放大或者缩小 -->
    <group
        android:name="dot_group"
        android:pivotX="12"
        android:pivotY="12">
        <path
            android:name="dot_path"
            android:fillColor="@android:color/white"
            android:pathData="M 12 7 C 9.24 7 7 9.24 7 12 C 7 14.76 9.24 17 12 17 C 14.76 17 17 14.76 17 12 C 17 9.24 14.76 7 12 7 Z" />
    </group>
</vector>

接下来是个过渡动画

<?xml version="1.0" encoding="utf-8"?>
<animated-selector xmlns:android="http://schemas.android.com/apk/res/android">

  <!-- 选中的图片 -->
  <item
    android:id="@+id/checked"
    android:drawable="@drawable/vd_checkable_radiobutton_checked"
    android:state_checked="true"/>

  <!-- 未选的图片 -->
  <item
    android:id="@+id/unchecked"
    android:drawable="@drawable/vd_checkable_radiobutton_unchecked"/>

  <!-- 从未选过渡到选中的动画 -->
  <transition
    android:drawable="@drawable/avd_checkable_radiobutton_unchecked_to_checked"
    android:fromId="@id/unchecked"
    android:toId="@id/checked"/>

  <!-- 从选中过渡到未选的动画 -->
  <transition
    android:drawable="@drawable/avd_checkable_radiobutton_checked_to_unchecked"
    android:fromId="@id/checked"
    android:toId="@id/unchecked"/>

</animated-selector>
<!-- 从未选过渡到选中的动画 -->
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:drawable="@drawable/vd_checkable_radiobutton_unchecked">
    <!-- 外圈的动画 -->
    <target android:name="ring_outer">
        <aapt:attr name="android:animation">
            <set>
                <!-- 标记顺序执行动画 -->
                <set android:ordering="sequentially">
                    <objectAnimator
                        android:duration="166"
                        android:interpolator="@android:interpolator/fast_out_slow_in"
                        android:propertyName="scaleX"
                        android:valueFrom="1.0"
                        android:valueTo="0.5"
                        android:valueType="floatType" />
                    <objectAnimator
                        android:duration="16"
                        android:interpolator="@android:interpolator/fast_out_slow_in"
                        android:propertyName="scaleX"
                        android:valueFrom="0.5"
                        android:valueTo="0.9"
                        android:valueType="floatType" />
                    <objectAnimator
                        android:duration="316"
                        android:interpolator="@android:interpolator/fast_out_slow_in"
                        android:propertyName="scaleX"
                        android:valueFrom="0.9"
                        android:valueTo="1.0"
                        android:valueType="floatType" />
                </set>
                <set android:ordering="sequentially">
                    <objectAnimator
                        android:duration="166"
                        android:interpolator="@android:interpolator/fast_out_slow_in"
                        android:propertyName="scaleY"
                        android:valueFrom="1.0"
                        android:valueTo="0.5"
                        android:valueType="floatType" />
                    <objectAnimator
                        android:duration="16"
                        android:interpolator="@android:interpolator/fast_out_slow_in"
                        android:propertyName="scaleY"
                        android:valueFrom="0.5"
                        android:valueTo="0.9"
                        android:valueType="floatType" />
                    <objectAnimator
                        android:duration="316"
                        android:interpolator="@android:interpolator/fast_out_slow_in"
                        android:propertyName="scaleY"
                        android:valueFrom="0.9"
                        android:valueTo="1.0"
                        android:valueType="floatType" />
                </set>
            </set>
        </aapt:attr>
    </target>
    <!-- 外圈的路径 -->
    <target android:name="ring_outer_path">
        <aapt:attr name="android:animation">
            <set>
                <set android:ordering="sequentially">
                    <objectAnimator
                        android:duration="166"
                        android:interpolator="@anim/checkable_radiobutton"
                        android:propertyName="strokeWidth"
                        android:valueFrom="2.0"
                        android:valueTo="18.0"
                        android:valueType="floatType" />
                    <objectAnimator
                        android:duration="16"
                        android:interpolator="@android:interpolator/fast_out_slow_in"
                        android:propertyName="strokeWidth"
                        android:valueFrom="18.0"
                        android:valueTo="2.0"
                        android:valueType="floatType" />
                    <objectAnimator
                        android:duration="316"
                        android:interpolator="@android:interpolator/fast_out_slow_in"
                        android:propertyName="strokeWidth"
                        android:valueFrom="2.0"
                        android:valueTo="2.0"
                        android:valueType="floatType" />
                </set>
            </set>
        </aapt:attr>
    </target>
    <!-- 内点的动画 -->
    <target android:name="dot_group">
        <aapt:attr name="android:animation">
            <set>
                <set android:ordering="sequentially">
                    <objectAnimator
                        android:duration="166"
                        android:interpolator="@android:interpolator/fast_out_slow_in"
                        android:propertyName="scaleX"
                        android:valueFrom="0.0"
                        android:valueTo="0.0"
                        android:valueType="floatType" />
                    <objectAnimator
                        android:duration="16"
                        android:interpolator="@android:interpolator/fast_out_slow_in"
                        android:propertyName="scaleX"
                        android:valueFrom="0.0"
                        android:valueTo="1.5"
                        android:valueType="floatType" />
                    <objectAnimator
                        android:duration="316"
                        android:interpolator="@android:interpolator/fast_out_slow_in"
                        android:propertyName="scaleX"
                        android:valueFrom="1.5"
                        android:valueTo="1.0"
                        android:valueType="floatType" />
                </set>
                <set android:ordering="sequentially">
                    <objectAnimator
                        android:duration="166"
                        android:interpolator="@android:interpolator/fast_out_slow_in"
                        android:propertyName="scaleY"
                        android:valueFrom="0.0"
                        android:valueTo="0.0"
                        android:valueType="floatType" />
                    <objectAnimator
                        android:duration="16"
                        android:interpolator="@android:interpolator/fast_out_slow_in"
                        android:propertyName="scaleY"
                        android:valueFrom="0.0"
                        android:valueTo="1.5"
                        android:valueType="floatType" />
                    <objectAnimator
                        android:duration="316"
                        android:interpolator="@android:interpolator/fast_out_slow_in"
                        android:propertyName="scaleY"
                        android:valueFrom="1.5"
                        android:valueTo="1.0"
                        android:valueType="floatType" />
                </set>
            </set>
        </aapt:attr>
    </target>
</animated-vector>

<!-- 从选中过渡到未选的动画 -->
<animated-vector
  xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:aapt="http://schemas.android.com/aapt"
  android:drawable="@drawable/vd_checkable_radiobutton_checked">
  <!-- 外圈的动画 -->
  <target android:name="ring_outer">
    <aapt:attr name="android:animation">
      <set>
        <set android:ordering="sequentially">
          <objectAnimator
            android:duration="183"
            android:interpolator="@android:interpolator/fast_out_slow_in"
            android:propertyName="scaleX"
            android:valueFrom="1.0"
            android:valueTo="0.9"
            android:valueType="floatType"/>
          <objectAnimator
            android:duration="16"
            android:interpolator="@anim/checkable_radiobutton"
            android:propertyName="scaleX"
            android:valueFrom="0.9"
            android:valueTo="0.5"
            android:valueType="floatType"/>
          <objectAnimator
            android:duration="300"
            android:interpolator="@anim/checkable_radiobutton"
            android:propertyName="scaleX"
            android:valueFrom="0.5"
            android:valueTo="1.0"
            android:valueType="floatType"/>
        </set>
        <set android:ordering="sequentially">
          <objectAnimator
            android:duration="183"
            android:interpolator="@android:interpolator/fast_out_slow_in"
            android:propertyName="scaleY"
            android:valueFrom="1.0"
            android:valueTo="0.9"
            android:valueType="floatType"/>
          <objectAnimator
            android:duration="16"
            android:interpolator="@anim/checkable_radiobutton"
            android:propertyName="scaleY"
            android:valueFrom="0.9"
            android:valueTo="0.5"
            android:valueType="floatType"/>
          <objectAnimator
            android:duration="300"
            android:interpolator="@anim/checkable_radiobutton"
            android:propertyName="scaleY"
            android:valueFrom="0.5"
            android:valueTo="1.0"
            android:valueType="floatType"/>
        </set>
      </set>
    </aapt:attr>
  </target>
  <!-- 外圈的路径 -->
  <target android:name="ring_outer_path">
    <aapt:attr name="android:animation">
      <set>
        <set android:ordering="sequentially">
          <objectAnimator
            android:duration="183"
            android:interpolator="@android:interpolator/fast_out_slow_in"
            android:propertyName="strokeWidth"
            android:valueFrom="2.0"
            android:valueTo="2.0"
            android:valueType="floatType"/>
          <objectAnimator
            android:duration="16"
            android:interpolator="@android:interpolator/fast_out_slow_in"
            android:propertyName="strokeWidth"
            android:valueFrom="2.0"
            android:valueTo="18.0"
            android:valueType="floatType"/>
          <objectAnimator
            android:duration="300"
            android:interpolator="@android:interpolator/fast_out_slow_in"
            android:propertyName="strokeWidth"
            android:valueFrom="18.0"
            android:valueTo="2.0"
            android:valueType="floatType"/>
        </set>
      </set>
    </aapt:attr>
  </target>
  <!-- 内点的动画 -->
  <target android:name="dot_group">
    <aapt:attr name="android:animation">
      <set>
        <set android:ordering="sequentially">
          <objectAnimator
            android:duration="183"
            android:interpolator="@android:interpolator/fast_out_slow_in"
            android:propertyName="scaleX"
            android:valueFrom="1.0"
            android:valueTo="1.4"
            android:valueType="floatType"/>
          <objectAnimator
            android:duration="16"
            android:interpolator="@android:interpolator/fast_out_slow_in"
            android:propertyName="scaleX"
            android:valueFrom="1.4"
            android:valueTo="0.0"
            android:valueType="floatType"/>
          <objectAnimator
            android:duration="300"
            android:interpolator="@android:interpolator/fast_out_slow_in"
            android:propertyName="scaleX"
            android:valueFrom="0.0"
            android:valueTo="0.0"
            android:valueType="floatType"/>
        </set>
        <set android:ordering="sequentially">
          <objectAnimator
            android:duration="183"
            android:interpolator="@android:interpolator/fast_out_slow_in"
            android:propertyName="scaleY"
            android:valueFrom="1.0"
            android:valueTo="1.4"
            android:valueType="floatType"/>
          <objectAnimator
            android:duration="16"
            android:interpolator="@android:interpolator/fast_out_slow_in"
            android:propertyName="scaleY"
            android:valueFrom="1.4"
            android:valueTo="0.0"
            android:valueType="floatType"/>
          <objectAnimator
            android:duration="300"
            android:interpolator="@android:interpolator/fast_out_slow_in"
            android:propertyName="scaleY"
            android:valueFrom="0.0"
            android:valueTo="0.0"
            android:valueType="floatType"/>
        </set>
      </set>
    </aapt:attr>
  </target>
</animated-vector>

选择过渡动画原理也是很简单,通过内圆和外圆不断的放大,缩小造成视觉差的效果。

2.4 线性进度加载效果

线性进度加载效果.gif

由三个路径组成:半透明背景和两个内部矩形路径。 在动画期间,两个内部矩形会水平平移并以不同程度缩放。下面的xml以图片和动画一起。

<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt">

    <aapt:attr name="android:drawable">
        <!-- 这是一个宽度360dp,高度10dp的画布 -->
        <vector
            android:width="360dp"
            android:height="10dp"
            android:viewportWidth="360"
            android:viewportHeight="10">
            <!-- 这个group挪到translateX180,translateY5即是画布的中间 -->
            <group
                android:name="progress_group"
                android:translateX="180"
                android:translateY="5">
                <path
                    android:name="background_track"
                    android:fillAlpha="?android:attr/disabledAlpha"
                    android:fillColor="?attr/colorControlActivated"
                    android:pathData="M -180 -1 L 180 -1 L 180 1 L -180 1 Z" />
                <!-- 调用了下面的rect2_grp -->
                <group
                    android:name="rect2_grp"
                    android:scaleX="0.1"
                    android:translateX="-197.60001">
                    <path
                        android:name="rect2"
                        android:fillColor="?attr/colorControlActivated"
                        android:pathData="M -144 -1 L 144 -1 L 144 1 L -144 1 Z" />
                </group>
                <!-- 调用了下面的rect1_grp -->
                <group
                    android:name="rect1_grp"
                    android:scaleX="0.1"
                    android:translateX="-522.59998">
                    <path
                        android:name="rect1"
                        android:fillColor="?attr/colorControlActivated"
                        android:pathData="M -144 -1 L 144 -1 L 144 1 L -144 1 Z" />
                </group>
            </group>
        </vector>
    </aapt:attr>

    <!-- 在改变translateX的同时修改scaleX缩放 -->
    <target android:name="rect2_grp">
        <aapt:attr name="android:animation">
            <set>
                <objectAnimator
                    android:duration="2000"
                    android:pathData="M -197.60001,0 c 14.28182,0 85.07782,0 135.54689,0 c 54.26191,0 90.42461,0 168.24331,0 c 144.72154,0 316.40982,0 316.40982,0"
                    android:propertyXName="translateX"
                    android:repeatCount="infinite">
                    <!-- 运动轨迹 -->
                    <aapt:attr name="android:interpolator">
                        <pathInterpolator android:pathData="M 0,0 C 0.0375,0 0.128764607715,0.0895380946618 0.25,0.218553507947 C 0.322410320025,0.295610602487 0.436666666667,0.417591408114 0.483333333333,0.489826169306 C 0.69,0.80972296795 0.793333333333,0.950016125212 1,1 " />
                    </aapt:attr>
                </objectAnimator>
                <objectAnimator
                    android:duration="2000"
                    android:pathData="M 0,0.1 L 1,0.571379510698 L 2,0.909950256348 L 3,0.1"
                    android:propertyYName="scaleX"
                    android:repeatCount="infinite">
                    <!-- 运动轨迹 -->
                    <aapt:attr name="android:interpolator">
                        <pathInterpolator android:pathData="M 0,0 C 0.06834272400867,0.01992566661414 0.19220331656133,0.15855429260523 0.33333333333333,0.34926160892842 C 0.38410433133433,0.41477913453861 0.54945792615267,0.68136029463551 0.66666666666667,0.68279962777002 C 0.752586273196,0.68179620963216 0.737253971954,0.878896194318 1,1" />
                    </aapt:attr>
                </objectAnimator>
            </set>

        </aapt:attr>
    </target>

    <!-- 在改变translateX的同时修改scaleX缩放 -->
    <target android:name="rect1_grp">
        <aapt:attr name="android:animation">
            <set>
                <objectAnimator
                    android:duration="2000"
                    android:pathData="M -522.59998,0 c 48.89972,0 166.02656,0 301.21729,0 c 197.58128,0 420.9827,0 420.9827,0"
                    android:propertyXName="translateX"
                    android:repeatCount="infinite">
                    <!-- 运动轨迹 -->
                    <aapt:attr name="android:interpolator">
                        <pathInterpolator android:pathData="M 0,0 L 0.2 0 C 0.3958333333336,0 0.474845090492,0.206797621729 0.5916666666664,0.417082932942 C 0.7151610251224,0.639379624869 0.81625,0.974556908664 1,1" />
                    </aapt:attr>
                </objectAnimator>
                <objectAnimator
                    android:duration="2000"
                    android:pathData="M 0 0.1 L 1 0.826849212646 L 2 0.1"
                    android:propertyYName="scaleX"
                    android:repeatCount="infinite">
                    <!-- 运动轨迹 -->
                    <aapt:attr name="android:interpolator">
                        <pathInterpolator android:pathData="M 0 0 L 0.3665 0 C 0.47252618112021,0.062409910275 0.61541608570164,0.5 0.68325,0.5 C 0.75475061236836,0.5 0.75725829093844,0.814510098964 1,1" />
                    </aapt:attr>
                </objectAnimator>
            </set>

        </aapt:attr>
    </target>

</animated-vector>



那么该章节系列到此结束,以下是4个demo的相关地址
放大/旋转/移动 转换各例子
展开折叠效果
单选按钮选择效果
线性进度加载效果

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容