Android中的矢量图

概述

VectorDrawable是通过XML文件中的一系列点,线和曲线及其相关颜色信息定义的。 使用VectorDrawable的主要优点是图像可扩展性。 它可以缩放而不损耗显示质量,这意味着相同的文件被调整为不同的屏幕密度,而不会损耗图像质量。 从而得到较小的APK文件和较少的开发人员维护。 使用矢量图像可以进行动画,通过使用多个XML文件代替每个显示分辨率的多个图像。

让我们通过一个例子来了解好处。 尺寸为100×100dp的图像可以在较小的显示分辨率上呈现良好的质量。 在较大的设备上,应用可能需要使用400 x 400 dp版本的图片。 通常,开发人员创建资产的多个版本以适应不同的屏幕密度。 这种方法消耗更多的开发工作,并导致更大的APK,这需要更多的空间在设备上。

从Android 5.0(API级别21)开始,有两个类支持矢量图形作为可绘制资源:VectorDrawable和AnimatedVectorDrawable。 在Android 5.0(API级别21)之前,支持库23.2或更高版本提供对Vector Drawables和Animated Vector Drawables的全面支持。

注意:为了优化重绘图性能,为每个VectorDrawable创建一个位图缓存。 因此,引用相同的VectorDrawable意味着共享相同的位图缓存。 如果这些引用不同意相同的大小,在每次改变大小时位图将重新创建和重绘。 换句话说,如果VectorDrawable用于不同的大小,创建多个VectorDrawable更有效,每个大小一个。

关于 VectorDrawable 类

VectorDrawable定义了一个静态的drawable对象。 与SVG格式类似,每个矢量图形被定义为树的层次结构,它由path 和 group组成。 每个path包含对象轮廓的几何体,group包含转换的详细信息。 所有path的绘制顺序与XML文件中显示的顺序相同。


Sample hierarchy of a vector drawable asset

下面是一个VectorDrawable XML文件示例,在充电模式下呈现电池的图像。

<!-- res/drawable/battery_charging.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    <!-- intrinsic size of the drawable -->
    android:height="24dp"
    android:width="24dp"
    <!-- size of the virtual canvas -->
    android:viewportWidth="24.0"
    android:viewportHeight="24.0">
   <group
         android:name="rotationGroup"
         android:pivotX="10.0"
         android:pivotY="10.0"
         android:rotation="15.0" >
      <path
        android:name="vect"
        android:fillColor="#FF000000"
        android:pathData="M15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33V9h4.93L13,7v2h4V5.33C17,4.6 16.4,4 15.67,4z"
        android:fillAlpha=".3"/>
      <path
        android:name="draw"
        android:fillColor="#FF000000"
        android:pathData="M13,12.5h2L11,20v-5.5H9L11.93,9H7v11.67C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V9h-4v3.5z"/>
   </group>
</vector>

此XML呈现以下图像:


vector、group、path和clip-path标签的相关属性如下所示:

vector 标签 --- 用于定义VectorDrawable
android:name
定义此VectorDrawable的名称。
Animatable : No.
android:width
用于定义drawable的固有宽度。 支持所有尺寸单位,通常使用dp指定。
Animatable : No.
android:height
用于定义drawable的固有高度。 支持所有尺寸单位,通常使用dp指定。
Animatable : No.
android:viewportWidth
用于定义viewport空间的宽度。 viewport是绘制paths路径数据的虚拟画布。
Animatable : No.
android:viewportHeight
用于定义viewport空间的高度。 viewport是绘制paths路径数据的虚拟画布。
Animatable : No.
android:tint
定义drawable 的 tint 颜色。默认是没有 tint 颜色的
Animatable : No.
android:tintMode
定义tint的 Porter-Duff blending 模式,默认值为 src_in
Animatable : No.
android:autoMirrored
指示当布局方向为RTL(从右到左)时,drawable是否需要被镜像。
Animatable : No.
android:alpha
drawable的不透明度。
Animatable : Yes.

group 标签 --- 定义一组paths 或 subgroups,以及transformation信息。 
transformations是在与viewport相同的坐标中定义。 并且按照缩放,旋转,然后平移的顺序应用transformations。
android:name
定义group的名称。
Animatable : No.
android:rotation
group的旋转角度。
Animatable : Yes.
android:pivotX
group缩放和旋转时的X轴参考点。 是在viewport空间中定义的(即在vector标签的viewportWidth范围中取值)。
Animatable : Yes.
android:pivotY
group缩放和旋转时的Y轴参考点。 是在viewport空间中定义的(即在vector标签中viewportHeight范围中取值)。
Animatable : Yes.
android:scaleX
X坐标上的缩放倍数。
Animatable : Yes.
android:scaleY
Y坐标上的缩放倍数。
Animatable : Yes.
android:translateX
X坐标上的平移量。 是在viewport空间中定义的(即在vector标签的viewportWidth范围中取值)。
Animatable : Yes.
android:translateY
Y坐标上的平移量。 是在viewport空间中定义的(即在vector标签中viewportHeight范围中取值)。
Animatable : Yes.

path 标签 --- 定义要绘制的路径。
android:name
定义path的名称。
Animatable : No.
android:pathData
使用与SVG path data中的“d”属性完全相同的格式定义path data。 是在viewport空间中定义的(即使用vector标签中viewportWidth 、viewportHeight范围中的值定义path data)。
Animatable : Yes.
android:fillColor
指定用于填充path的颜色。 可能是color,或者对于SDK 24+的color state list或gradient color。 如果此属性是动画属性,动画设置的任何值都将覆盖原始值。 如果未指定此属性,则不绘制path填充。
Animatable : Yes.
android:strokeColor
指定用于绘制path轮廓的颜色。 可能是color,或者对于SDK 24+的color state list或gradient color。 如果此属性是动画属性,动画设置的任何值都将覆盖原始值。 如果未指定此属性,则不绘制path轮廓。
Animatable : Yes.
android:strokeWidth
定义路径轮廓的宽度。
Animatable : Yes.
android:strokeAlpha
路径轮廓的不透明度。
Animatable : Yes.
android:fillAlpha
填充路径的不透明度。
Animatable : Yes.
android:trimPathStart
从路径起始位置截断路径的比例,取值范围从0到1
Animatable : Yes.
android:trimPathEnd
从路径结束位置截断路径的比例,取值范围从0到1
Animatable : Yes.
android:trimPathOffset
设置路径截取的范围 Shift trim region (allows showed region to include the start and end), 取值范围从0到1
Animatable : Yes.
android:strokeLineCap
设置路径轮廓线帽的形状:butt,round,square。体呈现的效果可以参考下图
Animatable : No.
android:strokeLineJoin
设置路径轮廓交界处的连接方式:miter,round,bevel。具体呈现的效果可以参考下图
Animatable : No.
android:strokeMiterLimit
Sets the Miter limit for a stroked path. 用来限制miterLengh和strokeWidth之间的比例,
两条直线之间的角度是锐角时。 miterLength和storeWidth之间的比例可以大于storeMiterLimit。 
在这种情况下,两条直线之间的交点的形状将从“Miter-Join”改变为“Bevel-Join”。
具体具体呈现的效果可以参考下图。
Animatable : No.
android:fillType
设置路径的fillType。 类型可以是“ evenOdd”或“ nonZero”。 它们的行为与SVG的“fill-rule”属性相同。 有关更多详细信息,请参考(https://www.w3.org/TR/SVG/painting.html#FillRuleProperty)
Animatable : No.

clip-path 标签 --- 定义当前修剪的路径。 请注意,clip-path仅适用于当前group及其子项。
android:name
定义clip path的名称。
Animatable : No.
android:pathData
使用与SVG path data中的“d”属性相同的格式定义clip-path数据。
Animatable : Yes.
strokeLineCap的三种取值

strokeLineJoin的三种取值

strokeMiterLimit

关于 AnimatedVectorDrawable 类

这个类使用ObjectAnimator或AnimatorSet定义的动画来动画VectorDrawable的属性。
从API 25开始,AnimatedVectorDrawable在RenderThread(而不是早期API的UI线程)上运行。 这意味着AnimatedVectorDrawable中的动画可以保持平滑,即使在UI线程上有重的工作负载。 注意:如果UI线程没有响应,RenderThread可以继续动画,直到UI线程能够推送另一个帧。 因此,不可能使用UI线程动画精确地协调支持RenderThread的AnimatedVectorDrawable。 另外,onAnimationEnd(Drawable)将在RenderThread上完成AnimatedVectorDrawable后调用该帧。

AnimatedVectorDrawable可以在三个单独的XML文件或一个XML中定义。
1> 多个XML文件的示例
以下XML文件演示了矢量图形的动画。
VectorDrawable's XML file: vd.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android"
   android:height="64dp"
   android:width="64dp"
   android:viewportHeight="600"
   android:viewportWidth="600" >
   <group
      android:name="rotationGroup"
      android:pivotX="300.0"
      android:pivotY="300.0"
      android:rotation="45.0" >
      <path
         android:name="vectorPath"
         android:fillColor="#000000"
         android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
   </group>
</vector>

AnimatedVectorDrawable's XML file: avd.xml

<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
   android:drawable="@drawable/vd" >
     <target
         android:name="rotationGroup"
         android:animation="@anim/rotation" />
     <target
         android:name="vectorPath"
         android:animation="@anim/path_morph" />
</animated-vector>

Animator XML files that are used in the AnimatedVectorDrawable's XML file: rotation.xml and path_morph.xml

<objectAnimator
   android:duration="6000"
   android:propertyName="rotation"
   android:valueFrom="0"
   android:valueTo="360" />
<set xmlns:android="http://schemas.android.com/apk/res/android">
   <objectAnimator
      android:duration="3000"
      android:propertyName="pathData"
      android:valueFrom="M300,70 l 0,-70 70,70 0,0   -70,70z"
      android:valueTo="M300,70 l 0,-70 70,0  0,140 -70,0 z"
      android:valueType="pathType"/>
</set>

animated-vector和target标签的相关属性如下所示:

android:animation
引用作用于path、group或者vector drawable的动画
android:drawable
引用vector drawable资源
android:name
path、group或者vector drawable的名称

单个 XML 文件的示例
通过使用此方法,您可以通过XML Bundle Format将相关的XML文件合并为单个XML文件。 在构建应用程序时,aapt tag创建单独的资源,并在animated-vector中引用它们。 此方法需要构建工具24或更高版本,并且向后兼容。

<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="24dp"
            android:height="24dp"
            android:viewportWidth="24"
            android:viewportHeight="24">
            <path
                android:name="root"
                android:strokeWidth="2"
                android:strokeLineCap="square"
                android:strokeColor="?android:colorControlNormal"
                android:pathData="M4.8,13.4 L9,17.6 M10.4,16.2 L19.6,7" />
        </vector>
    </aapt:attr>
    <target android:name="root">
        <aapt:attr name="android:animation">
            <objectAnimator
                android:propertyName="pathData"
                android:valueFrom="M4.8,13.4 L9,17.6 M10.4,16.2 L19.6,7"
                android:valueTo="M6.4,6.4 L17.6,17.6 M6.4,17.6 L17.6,6.4"
                android:duration="300"
                android:interpolator="@android:interpolator/fast_out_slow_in"
                android:valueType="pathType" />
        </aapt:attr>
    </target>
</animated-vector>

Vector Drawables向后兼容性解决方案

下表总结了可用于向后兼容的两种技术:


1> PNG generation
Android 5.0(API级别21)和更高版本提供了vector drawable support。 如果您的应用程序的最低API级别较低,Vector Asset Studio会将vector drawable文件添加到您的项目中; 此外,在构建时,Gradle以各种分辨率创建PNG光栅(raster)图像。 Gradle生成由build.gradle文件中的Domain Specific Language (DSL) generatedDensities属性指定的PNG密度。

对于Android 5.0(API级别21)及更高版本,Vector Asset Studio支持所有VectorDrawable XML元素。 为了向后兼容Android 4.4(API级别20)及更低版本,Vector Asset Studio支持以下XML元素:


2> Support Library
为了在5.0(API级别21)之前的Android设备上支持Vector Drawable和Animated Vector Drawable,VectorDrawableCompat和AnimatedVectorDrawableCompat分别通过两个新的支持库提供:support-vector-drawable和animated-vector-drawable。

Android Studio 1.4在构建时通过生成PNG文件引入了对Vector Drawables的有限兼容性支持(就是上面第一种技术)。 Vector Drawable和Animated Vector Drawable支持库提供灵活性和广泛的兼容性 - 它是一个支持库,所以你可以使用它在Android 2.1(API级别7+)的Android平台。 要将应用程序配置为使用Vector支持库,请将vectorDrawables元素添加到app module中的build.gradle文件中。

使用以下代码片段配置vectorDrawables元素:

//For Gradle Plugin 2.0+
 android {
   defaultConfig {
     vectorDrawables.useSupportLibrary = true
    }
 }
//For Gradle Plugin 1.5 or below
android {
  defaultConfig {
    // Stops the Gradle plugin’s automatic rasterization of vectors
    generatedDensities = []
  }
  // Flag notifies aapt to keep the attribute IDs around
  aaptOptions {
    additionalParameters "--no-version-vectors"
  }
}

您可以在API 11和更高版本的设备上使用VectorDrawableCompat和AnimatedVectorDrawableCompat。Android加载Drawable的方式中不是接受可绘制ID的每个地方(例如在XML文件中)都支持加载Vector Drawable。 值得庆幸的是,AppCompat添加了许多功能,使其易于使用Vector Drawable。 首先,当使用ImageView(或子类,如ImageButton和FloatingActionButton)时,你将能够使用新的app:srcCompat属性来引用Vector Drawable以及任何其他地方使用android:src属性:

<ImageView
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  app:srcCompat="@drawable/ic_add" />

要在运行时更改Drawable时,可以像以前一样使用setImageResource()方法。 使用AppCompat和app:srcCompat将Vector Drawable集成到应用程序中是最简单的方法。

支持库限制
Animated Vector Drawable在API 21之前的平台上运行时可以做什么样的事情有一些限制。下面的列表解释了与支持库不兼容的功能:

Path Morphing (PathType evaluator) :用于将一个路径变形为另一个路径。
Path Interpolation :通过定义一个灵活的插值器(表示为路径)来代替像LinearInterpolator这样的系统定义的插值器。
Move along path :几何对象可以作为动画的一部分在任意路径上移动。

举例说明

1> path标签的trimPathStart和trimPathEnd属性的动画
先来看一下效果图:


实现代码如下:
VectorDrawable's XML file:vd_trim_path_searchbar.xml

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

    <path
        android:name="search"
        android:pathData="M141,17 A9,9 0 1,1 142,16 L149,23"
        android:strokeAlpha="0.8"
        android:strokeColor="#000000"
        android:strokeLineCap="round"
        android:strokeWidth="2"/>
    <path
        android:name="bar"
        android:pathData="M0,23 L149,23"
        android:strokeAlpha="0.8"
        android:strokeColor="#000000"
        android:strokeLineCap="square"
        android:strokeWidth="2"/>
</vector>

AnimatedVectorDrawable's XML file: avd_trim_path_searchbar.xml

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

    <target
        android:name="search"
        android:animation="@animator/anim_trim_path_searchbar_in"/>

    <target
        android:name="bar"
        android:animation="@animator/anim_trim_path_searchbar_out"/>

</animated-vector>

Animator XML files that are used in the AnimatedVectorDrawable's XML file: anim_trim_path_searchbar_in.xml and anim_trim_path_searchbar_out.xml

anim_trim_path_searchbar_in.xml
<objectAnimator
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:propertyName="trimPathStart"
    android:repeatCount="infinite"
    android:repeatMode="reverse"
    android:valueFrom="0"
    android:valueTo="1"
    android:valueType="floatType"/>

anim_trim_path_searchbar_out.xml
<objectAnimator
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:propertyName="trimPathStart"
    android:repeatCount="infinite"
    android:repeatMode="reverse"
    android:valueFrom="1"
    android:valueTo="0"
    android:valueType="floatType"/>

在代码中配置并且开启动画:

<ImageView
    android:id="@+id/imageview_search_bar_anim"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:srcCompat="@drawable/avd_trim_path_searchbar"/>

Drawable drawable = imageView.getDrawable();
if (drawable instanceof Animatable) {
    ((Animatable) drawable).start();
}

2> path标签或者group标签的pathData属性动画(即Path Morphing动画,只支持Android L+ 版本)
例1 : 先来看一下效果图:



实现代码如下:
VectorDrawable's XML file:vd_path_paw.xml

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="120dp"
    android:height="120dp"
    android:viewportHeight="24"
    android:viewportWidth="24">
    <path
        android:name="toe1"
        android:fillColor="#ffffff"
        android:pathData="M 4.5 7 C 5.88071187458 7 7 8.11928812542 7 9.5 C 7 10.8807118746 5.88071187458 12 4.5 12 C 3.11928812542 12 2 10.8807118746 2 9.5 C 2 8.11928812542 3.11928812542 7 4.5 7 Z" />
    <path
        android:name="toe2"
        android:fillColor="#ffffff"
        android:pathData="M 9 3 C 10.3807118746 3 11.5 4.11928812542 11.5 5.5 C 11.5 6.88071187458 10.3807118746 8 9 8 C 7.61928812542 8 6.5 6.88071187458 6.5 5.5 C 6.5 4.11928812542 7.61928812542 3 9 3 Z" />
    <path
        android:name="toe3"
        android:fillColor="#ffffff"
        android:pathData="M 15 3 C 16.3807118746 3 17.5 4.11928812542 17.5 5.5 C 17.5 6.88071187458 16.3807118746 8 15 8 C 13.6192881254 8 12.5 6.88071187458 12.5 5.5 C 12.5 4.11928812542 13.6192881254 3 15 3 Z" />
    <path
        android:name="toe4"
        android:fillColor="#ffffff"
        android:pathData="M 19.5 7 C 20.8807118746 7 22 8.11928812542 22 9.5 C 22 10.8807118746 20.8807118746 12 19.5 12 C 18.1192881254 12 17 10.8807118746 17 9.5 C 17 8.11928812542 18.1192881254 7 19.5 7 Z" />
    <path
        android:fillColor="#ffffff"
        android:pathData="M17.34 14.86c-.87-1.02-1.6-1.89-2.48-2.91-.46-.54-1.05-1.08-1.75-1.32-.11-.04-.22-.07-.33-.09-.25-.04-.52-.04-.78-.04s-.53 0-.79 .05 c-.11 .02 -.22 .05 -.33 .09 -.7 .24 -1.28 .78 -1.75 1.32-.87 1.02-1.6 1.89-2.48 2.91-1.31 1.31-2.92 2.76-2.62 4.79 .29 1.02 1.02 2.03 2.33 2.32 .73 .15 3.06-.44 5.54-.44h.18c2.48 0 4.81 .58 5.54 .44 1.31-.29 2.04-1.31 2.33-2.32 .31 -2.04-1.3-3.49-2.61-4.8z" />
</vector>

AnimatedVectorDrawable's XML file: avd_path_paw.xml

<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/vd_path_paw">
    <target
        android:name="toe1"
        android:animation="@animator/anim_path_morph_toe1"/>
    <target
        android:name="toe2"
        android:animation="@animator/anim_path_morph_toe2"/>
    <target
        android:name="toe3"
        android:animation="@animator/anim_path_morph_toe3"/>
    <target
        android:name="toe4"
        android:animation="@animator/anim_path_morph_toe4"/>
</animated-vector>

Animator XML files that are used in the AnimatedVectorDrawable's XML file: anim_path_morph_toe1.xml 、anim_path_morph_toe2.xml、anim_path_morph_toe3.xml and anim_path_morph_toe4.xml

anim_path_morph_toe1.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="sequentially">
    <objectAnimator
        android:duration="420"
        android:propertyName="pathData"
        android:repeatCount="-1"
        android:repeatMode="reverse"
        android:valueFrom="M 4.5 7 C 5.88071187458 7 7 8.11928812542 7 9.5 C 7 10.8807118746 5.88071187458 12 4.5 12 C 3.11928812542 12 2 10.8807118746 2 9.5 C 2 8.11928812542 3.11928812542 7 4.5 7 Z"
        android:valueTo="M 4.5 9 C 5.88071187458 9 7 10.1192881254 7 11.5 C 7 12.8807118746 5.88071187458 14 4.5 14 C 3.11928812542 14 2 12.8807118746 2 11.5 C 2 10.1192881254 3.11928812542 9 4.5 9 Z"
        android:valueType="pathType" />
    <objectAnimator
        android:duration="840"
        android:propertyName="pathData"
        android:repeatCount="-1"
        android:repeatMode="reverse"
        android:valueFrom="M 4.5 9 C 5.88071187458 9 7 10.1192881254 7 11.5 C 7 12.8807118746 5.88071187458 14 4.5 14 C 3.11928812542 14 2 12.8807118746 2 11.5 C 2 10.1192881254 3.11928812542 9 4.5 9 Z"
        android:valueTo="M 4.5 4 C 5.88071187458 4 7 5.11928812542 7 6.5 C 7 7.88071187458 5.88071187458 9 4.5 9 C 3.11928812542 9 2 7.88071187458 2 6.5 C 2 5.11928812542 3.11928812542 4 4.5 4 Z"
        android:valueType="pathType" />
    <objectAnimator
        android:duration="420"
        android:propertyName="pathData"
        android:repeatCount="-1"
        android:repeatMode="reverse"
        android:valueFrom="M 4.5 4 C 5.88071187458 4 7 5.11928812542 7 6.5 C 7 7.88071187458 5.88071187458 9 4.5 9 C 3.11928812542 9 2 7.88071187458 2 6.5 C 2 5.11928812542 3.11928812542 4 4.5 4 Z"
        android:valueTo="M 4.5 7 C 5.88071187458 7 7 8.11928812542 7 9.5 C 7 10.8807118746 5.88071187458 12 4.5 12 C 3.11928812542 12 2 10.8807118746 2 9.5 C 2 8.11928812542 3.11928812542 7 4.5 7 Z"
        android:valueType="pathType" />
</set>

anim_path_morph_toe2.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="sequentially"
    android:shareInterpolator="true">
    <objectAnimator
        android:duration="480"
        android:propertyName="pathData"
        android:repeatCount="-1"
        android:repeatMode="reverse"
        android:startOffset="100"
        android:valueFrom="M 9 3 C 10.3807118746 3 11.5 4.11928812542 11.5 5.5 C 11.5 6.88071187458 10.3807118746 8 9 8 C 7.61928812542 8 6.5 6.88071187458 6.5 5.5 C 6.5 4.11928812542 7.61928812542 3 9 3 Z"
        android:valueTo="M 9 5 C 10.3807118746 5 11.5 6.11928812542 11.5 7.5 C 11.5 8.88071187458 10.3807118746 10 9 10 C 7.61928812542 10 6.5 8.88071187458 6.5 7.5 C 6.5 6.11928812542 7.61928812542 5 9 5 Z"
        android:valueType="pathType" />
    <objectAnimator
        android:duration="960"
        android:propertyName="pathData"
        android:repeatCount="-1"
        android:repeatMode="reverse"
        android:valueFrom="M 9 5 C 10.3807118746 5 11.5 6.11928812542 11.5 7.5 C 11.5 8.88071187458 10.3807118746 10 9 10 C 7.61928812542 10 6.5 8.88071187458 6.5 7.5 C 6.5 6.11928812542 7.61928812542 5 9 5 Z"
        android:valueTo="M 9 0 C 10.3807118746 0 11.5 1.11928812542 11.5 2.5 C 11.5 3.88071187458 10.3807118746 5 9 5 C 7.61928812542 5 6.5 3.88071187458 6.5 2.5 C 6.5 1.11928812542 7.61928812542 0 9 0 Z"
        android:valueType="pathType" />
    <objectAnimator
        android:duration="480"
        android:propertyName="pathData"
        android:repeatCount="-1"
        android:repeatMode="reverse"
        android:valueFrom="M 9 0 C 10.3807118746 0 11.5 1.11928812542 11.5 2.5 C 11.5 3.88071187458 10.3807118746 5 9 5 C 7.61928812542 5 6.5 3.88071187458 6.5 2.5 C 6.5 1.11928812542 7.61928812542 0 9 0 Z"
        android:valueTo="M 9 3 C 10.3807118746 3 11.5 4.11928812542 11.5 5.5 C 11.5 6.88071187458 10.3807118746 8 9 8 C 7.61928812542 8 6.5 6.88071187458 6.5 5.5 C 6.5 4.11928812542 7.61928812542 3 9 3 Z"
        android:valueType="pathType" />
</set>

anim_path_morph_toe3.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="sequentially"
    android:shareInterpolator="true">
    <objectAnimator
        android:duration="420"
        android:propertyName="pathData"
        android:repeatCount="-1"
        android:repeatMode="reverse"
        android:startOffset="200"
        android:valueFrom="M 15 3 C 16.3807118746 3 17.5 4.11928812542 17.5 5.5 C 17.5 6.88071187458 16.3807118746 8 15 8 C 13.6192881254 8 12.5 6.88071187458 12.5 5.5 C 12.5 4.11928812542 13.6192881254 3 15 3 Z"
        android:valueTo="M 15 5 C 16.3807118746 5 17.5 6.11928812542 17.5 7.5 C 17.5 8.88071187458 16.3807118746 10 15 10 C 13.6192881254 10 12.5 8.88071187458 12.5 7.5 C 12.5 6.11928812542 13.6192881254 5 15 5 Z"
        android:valueType="pathType" />
    <objectAnimator
        android:duration="840"
        android:propertyName="pathData"
        android:repeatCount="-1"
        android:repeatMode="reverse"
        android:valueFrom="M 15 5 C 16.3807118746 5 17.5 6.11928812542 17.5 7.5 C 17.5 8.88071187458 16.3807118746 10 15 10 C 13.6192881254 10 12.5 8.88071187458 12.5 7.5 C 12.5 6.11928812542 13.6192881254 5 15 5 Z"
        android:valueTo="M 15 0 C 16.3807118746 0 17.5 1.11928812542 17.5 2.5 C 17.5 3.88071187458 16.3807118746 5 15 5 C 13.6192881254 5 12.5 3.88071187458 12.5 2.5 C 12.5 1.11928812542 13.6192881254 0 15 0 Z"
        android:valueType="pathType" />
    <objectAnimator
        android:duration="420"
        android:propertyName="pathData"
        android:repeatCount="-1"
        android:repeatMode="reverse"
        android:valueFrom="M 15 0 C 16.3807118746 0 17.5 1.11928812542 17.5 2.5 C 17.5 3.88071187458 16.3807118746 5 15 5 C 13.6192881254 5 12.5 3.88071187458 12.5 2.5 C 12.5 1.11928812542 13.6192881254 0 15 0 Z"
        android:valueTo="M 15 3 C 16.3807118746 3 17.5 4.11928812542 17.5 5.5 C 17.5 6.88071187458 16.3807118746 8 15 8 C 13.6192881254 8 12.5 6.88071187458 12.5 5.5 C 12.5 4.11928812542 13.6192881254 3 15 3 Z"
        android:valueType="pathType" />
</set>

anim_path_morph_toe4.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="sequentially"
    android:shareInterpolator="true">
    <objectAnimator
        android:duration="450"
        android:propertyName="pathData"
        android:repeatCount="-1"
        android:repeatMode="reverse"
        android:startOffset="300"
        android:valueFrom="M 19.5 7 C 20.8807118746 7 22 8.11928812542 22 9.5 C 22 10.8807118746 20.8807118746 12 19.5 12 C 18.1192881254 12 17 10.8807118746 17 9.5 C 17 8.11928812542 18.1192881254 7 19.5 7 Z"
        android:valueTo="M 19.5 9 C 20.8807118746 9 22 10.1192881254 22 11.5 C 22 12.8807118746 20.8807118746 14 19.5 14 C 18.1192881254 14 17 12.8807118746 17 11.5 C 17 10.1192881254 18.1192881254 9 19.5 9 Z"
        android:valueType="pathType" />
    <objectAnimator
        android:duration="900"
        android:propertyName="pathData"
        android:repeatCount="-1"
        android:repeatMode="reverse"
        android:valueFrom="M 19.5 7 C 20.8807118746 7 22 8.11928812542 22 9.5 C 22 10.8807118746 20.8807118746 12 19.5 12 C 18.1192881254 12 17 10.8807118746 17 9.5 C 17 8.11928812542 18.1192881254 7 19.5 7 Z"
        android:valueTo="M 19.5 4 C 20.8807118746 4 22 5.11928812542 22 6.5 C 22 7.88071187458 20.8807118746 9 19.5 9 C 18.1192881254 9 17 7.88071187458 17 6.5 C 17 5.11928812542 18.1192881254 4 19.5 4 Z"
        android:valueType="pathType" />
    <objectAnimator
        android:duration="450"
        android:propertyName="pathData"
        android:repeatCount="-1"
        android:repeatMode="reverse"
        android:valueFrom="M 19.5 4 C 20.8807118746 4 22 5.11928812542 22 6.5 C 22 7.88071187458 20.8807118746 9 19.5 9 C 18.1192881254 9 17 7.88071187458 17 6.5 C 17 5.11928812542 18.1192881254 4 19.5 4 Z"
        android:valueTo="M 19.5 7 C 20.8807118746 7 22 8.11928812542 22 9.5 C 22 10.8807118746 20.8807118746 12 19.5 12 C 18.1192881254 12 17 10.8807118746 17 9.5 C 17 8.11928812542 18.1192881254 7 19.5 7 Z"
        android:valueType="pathType" />
</set>

在代码中配置并且开启动画:

<ImageView
    android:id="@+id/imageview_path_paw"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@android:color/holo_blue_light"
    app:srcCompat="@drawable/avd_path_paw"/>

AnimatedVectorDrawable animatedVectorDrawable = (AnimatedVectorDrawable) getContext().getDrawable(R.drawable.avd_path_paw);

imageView.setImageDrawable(animatedVectorDrawable);
if (animatedVectorDrawable != null) {
    animatedVectorDrawable.start();
}

例2 : 先来看一下效果图:


实现代码如下:
VectorDrawable's XML file:vd_path_heart.xml

<vector
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="120dp"
    android:height="120dp"
    android:viewportHeight="56"
    android:viewportWidth="56">
    <path
        android:name="empty"
        android:fillColor="#ff4444"
        android:pathData="M32.95,19 C31.036,19 29.199,19.8828338 28,21.2724796 C26.801,19.8828338 24.964,19 23.05,19 C19.6565,19 17,21.6321526 17,24.9945504 C17,29.1089918 20.74,32.4713896 26.405,37.5667575 L28,39 L29.595,37.5667575 C35.26,32.4713896 39,29.1089918 39,24.9945504 C39,21.6321526 36.3435,19 32.95,19 L32.95,19 Z M28.1155,35.9536785 L28,36.0572207 L27.8845,35.9536785 C22.654,31.2506812 19.2,28.1444142 19.2,24.9945504 C19.2,22.8201635 20.8555,21.1798365 23.05,21.1798365 C24.744,21.1798365 26.394,22.2643052 26.9715,23.7520436 L29.023,23.7520436 C29.606,22.2643052 31.256,21.1798365 32.95,21.1798365 C35.1445,21.1798365 36.8,22.8201635 36.8,24.9945504 C36.8,28.1444142 33.346,31.2506812 28.1155,35.9536785 L28.1155,35.9536785 Z"/>
    <clip-path
        android:name="clip"
        android:pathData="M18 37 L38 37 L38 37 L18 37 Z"/>
    <path
        android:name="full"
        android:fillColor="#ff4444"
        android:pathData="M28,39 L26.405,37.5667575 C20.74,32.4713896 17,29.1089918 17,24.9945504 C17,21.6321526 19.6565,19 23.05,19 C24.964,19 26.801,19.8828338 28,21.2724796 C29.199,19.8828338 31.036,19 32.95,19 C36.3435,19 39,21.6321526 39,24.9945504 C39,29.1089918 35.26,32.4713896 29.595,37.5667575 L28,39 L28,39 Z"/>
</vector>

AnimatedVectorDrawable's XML file: avd_path_heart_empty.xml and avd_path_heart_full.xml

avd_path_heart_empty.xml
<animated-vector
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/vd_path_heart">
    <target
        android:name="clip"
        android:animation="@animator/anim_heart_empty" />
</animated-vector>

avd_path_heart_full.xml
<animated-vector
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/vd_path_heart">
    <target
        android:name="clip"
        android:animation="@animator/anim_heart_full" />
</animated-vector>

Animator XML files that are used in the AnimatedVectorDrawable's XML file: anim_heart_empty.xml and anim_heart_full.xml

anim_heart_empty.xml
<objectAnimator
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="600"
    android:interpolator="@android:interpolator/decelerate_cubic"
    android:propertyName="pathData"
    android:valueFrom="M0 0 L56 0 L56 56 L0 56 Z"
    android:valueTo="M18 37 L38 37 L38 37 L18 37 Z"
    android:valueType="pathType"/>

anim_heart_full.xml
<objectAnimator
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="800"
    android:interpolator="@android:interpolator/decelerate_cubic"
    android:propertyName="pathData"
    android:valueFrom="M18 37 L38 37 L38 37 L18 37 Z"
    android:valueTo="M0 0 L56 0 L56 56 L0 56 Z"
    android:valueType="pathType"/>

在代码中配置并且开启动画:

<ImageView
    android:id="@+id/imageview_heart_empty_anim"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:srcCompat="@drawable/avd_path_heart_empty"/>

AnimatedVectorDrawable emptyToFull = (AnimatedVectorDrawable) getContext().getDrawable(R.drawable.avd_path_heart_full);
AnimatedVectorDrawable fullToEmpty = (AnimatedVectorDrawable) getContext().getDrawable(R.drawable.avd_path_heart_empty);

AnimatedVectorDrawable animDrawable = isEmpty ? fullToEmpty: emptyToFull;
imageView.setImageDrawable(animDrawable);
if (animDrawable != null) {
    animDrawable.start();
}
isEmpty = !isEmpty;

参考文档

Add Multi-Density Vector Graphics
Vector Drawable
VectorDrawable XML相关标签属性说明
AnimatedVectorDrawable XML相关标签属性说明

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,486评论 25 707
  • Android Vector曲折的兼容之路 两年前写书的时候,就在研究Android L提出的Vector,可研究...
    eclipse_xu阅读 34,929评论 30 263
  • 1. 矢量图SVG简介 Android 5.0系统中引入了 VectorDrawable 来支持矢量图(SVG),...
    登高且赋阅读 2,556评论 2 15
  • 【读经】 路得记1章 【金句】 拿俄米见路得定意要跟随自己去,就不再劝她了。 (路得记 1:18 和合本) 【感动...
    chanor阅读 477评论 0 0
  • 今天本来想去单位加班,谁知道到了单位听到一个不好的消息,立马没加班的动力了! 我到单位的时候我们的一个领导跟劳资的...
    fuwl阅读 339评论 0 0