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转换的应用顺序很重要。要记住的两个规则是:
- 子group继承其父组所应用的转换,意思是当前每次的转变都是以上一个为准
- 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/可以模仿出上图效果,基本结构:
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的相关地址
放大/旋转/移动 转换各例子
展开折叠效果
单选按钮选择效果
线性进度加载效果
网友评论