Android中的矢量动画

Android中的矢量动画

自Android 5.0(API 21)开始,Vector drawable(矢量图像)正式得到了支持,可以通过VectorDrawableAnimatedVectorDrawable来实现矢量图像。不过这两个在5.0以上的系统运行,因此为了支持更多5.0以下的系统,我们可以通过导入Android support library的方式并使用VectorDrawableCompatAnimatedVectorDrawableCompat来实现矢量图。

对于API 24及更高版本,AnimatedVectorDrawableCompat会自动委托给AnimatedVectorDrawable。 对于API 24之下的版本,此类相当是带有ObjectAnimatorAnimatorSet属性的VectorDrawableCompat,从而创建动画drawable。同理VectorDrawableCompat也会自动委托给VectorDrawable

SVG

SVG的全称是Scalable Vector Graphics(可缩放矢量图形),它是专门用于网络的矢量图形标准。与SVG对应的是Bitmap(位图),位图是由一个个像素点组成的,当图片放大到一定的大小时,会出现马赛克现象;而矢量图则是由一个个点组成,结果数学计算利用直线和曲线绘制而成,无论如何方法都不会出现马赛克现象。

SVG相比Bitmap:

优点

  • SVG使用XML格式定义图形,可以很方便的用多种根据打开修改(比如记事本)
  • SVG由点来存储,由计算机根据点信息绘制,不会失真,无需根据分辨率适配多套图标
  • SVG的占用空间明显比Bitmap小
  • SVG可以转换为Path路径,与Path动画结合形成丰富的动画

缺点

  • 没有位图表达的色彩丰富

标准的SVG语法中支持很多标签,比如rect(绘制矩形)、circle(绘制圆形)、line(绘制线段)、polyline(绘制折现)、ellipse(绘制椭圆)、polygon(绘制多边形)等。

但是Android中并没有对原生的SVG语法进行支持,而是以一种简化的方式对SVG进行兼容。Android是通过使用Path标签实现SVG的图像。虽然较为复杂,但是对于SVG图像的转换Android Studio中提供了Vector Asset Studio工具,可以轻松完成。

VectorDrawable

Android中的矢量图是VectorDrawable(平时较多使用VectorDrawableCompat以兼容更多版本,API 24及以上自动委托给VectorDrawable),VectorDrawable是一个在XML文件中定义了一组点、线、曲线、颜色和其他相关信息的矢量图形。其最大的优势便是缩放不失真,这能有效减少APK体积并减少开发人员的维护,而矢量动画就是建立在VectorDrawable上形成的。

VectorDrawable以树状结构定义属性,由PathGroup组成。Path用来定义可绘图的路径,Group则用于定义一系列路径或者将Path标签分组。Path绘制顺序与XML文件中显示的顺序相同。它可以使用元素在XML文件中定义。

在静态图像中,单纯使用一个path标签实现还是使用一组path标签实现没有什么实质性的区别;在动画中,我们可以指定每个path路径做特定的动画,通过group标签则可以将原本由一个path路径实现的内容分为多个path路径来实现,每个path路径可以指定特定的动画,这样一来显示效果就丰富多彩了。

Android中的矢量动画_第1张图片

AnimatedVectorDrawable

AnimatedVectorDrawable将动画属性添加到矢量图形中。我们更多的是使用AnimatedVectorDrawableCompat,其具有更好的兼容性,对于API 24及更高版本,此类会自动委托给AnimatedVectorDrawable

同时自API 25开始,AnimatedVectorDrawable在RenderThread(渲染线程)上运行(而不是如早期API在UI线程上运行),这意味着即使UI线程上有大量负载,动画也能流畅的展现。

定义一个AnimatedVectorDrawable可以使用三个独立的XML文件,也可以使用一个XML文件。

AnimatedVectorDrawable can be defined in either three separate XML files, or one XML.

Define an AnimatedVectorDrawable in three separate XML files(三个独立的XML定义方式)

XML for the VectorDrawable containing properties to be animated(包含动画属性的VectorDrawable的XML)

通过对VectorDrawable设置动画属性来实现动画效果。而对于动画将由ObjectAnimator执行,它的对象可以是根元素,可以是组元素,还可以是路径元素。(The ObjectAnimator’s target can be the root element, a group element or a path element),对于目标元素,他们要求是不重名的,而没有动画的元素则可以不命名。

以下是VectorDrawable的动画属性:

元素 动画属性 说明
alpha Drawable的透明度,默认为1.0(即不透明)
rotation 定义该组图像的旋转度数
pivotX 定义该组缩放和旋转时的X参考点
pivotY 定义该组缩放和旋转时的Y参考点
scaleX 定义该组X轴缩放大小
scaleY 定义该组Y轴缩放大小
translateX 定义该组沿X轴平移的距离
translateY 定义该组沿Y轴平移的距离
pathData 对SVG矢量图的描述
fillAlpha 填充颜色的透明度
fillColor 填充颜色
strokeColor 描边颜色
strokeWidth 描边宽度
strokeAlpha 描边透明度
trimPathStart 指定路径从哪里开始
trimPathEnd 指定路径在哪里结束
trimPathOffset 指定路径的位移距离
strokeLineCap 描边线条重点形状(线帽):butt(无线帽)、round(圆形)、square(方形)

其中,对于pathData有很多指令:

  • M(M x,y)=moveto:将画笔移动到指定的坐标位置
  • L(L x,y)=lineto:画直线到指定的坐标位置
  • H(H x)=horizontal lineto:画水平线到指定的X坐标位置
  • V(V y)=vertical lineto:画垂直线到指定的Y坐标位置
  • C(C x1,y1,x2,y2,endx,endy)=curveto:三阶贝塞尔曲线
  • S(S x2,y2,endx,endy)=smooth curveto:根据上一个指令终点最为起点的三阶贝塞尔曲线
  • Q(Q x,y,endx,endy)=quadratic Belzier curve:二阶贝塞尔曲线
  • T(T endx,endy)=smooth quadratic Belzier curve:根据上一个指令终点最为起点的二阶贝塞尔曲线
  • A(A rx,ry,xRotation,flag1,flag2,x,y)=elliptical Arc:弧线
    • rx,ry:指椭圆的半轴大小
    • xRotation:指椭圆的X轴和水平方向顺时针方向的夹角
    • flag1:只有两个值,1表示大角度弧度,0表示小角度弧度
    • flag2:只有两个值,1表示顺时针,0表示逆时针
    • x,y:终点坐标
  • Z=closepath():关闭路径

注意:

  • 坐标轴以(0,0)点为中心,X轴水平向右,Y轴水平向下
  • 所有指令大小写皆可,但是大写表示绝对定位,参照全局坐标系;小写表示相对定位,参照父容器坐标系
  • 指令和数据间的空格可以省略
<vector xmlns:android="http://schemas.android.com/apk/res/android"
     
     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="v"
             android:fillColor="#000000"
             android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
     group>
 vector>

XML for AnimatedVectorDrawable

组成一个AnimatedVectorDrawable通常包含一个VectorDrawable和一个或多个目标元素。 对于目标元素可以通过android:name指定其目标,并通过android:animation将该目标与其对应的动画元素(ObjectAnimatorAnimatorSet)进行连接。

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

XML for Animations defined using ObjectAnimator or AnimatorSet

这个XML文件是目标元素的动画效果。例如上面的rotation.xml和path_morph.xml。

rotation.xml通过objectAnimator定义的是在6000毫秒内旋转360度:

<objectAnimator
     android:duration="6000"
     android:propertyName="rotation"
     android:valueFrom="0"
     android:valueTo="360" />

path_morph.xml则将path从一个形状变换为另一个形状。

对于Path变换必须与之前的形状兼容,也就是说形状相同,顺序相同,并且路径中的变化命令也需要相同。

 <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>

Define an AnimatedVectorDrawable all in one XML file(一个XML搞定AnimatedVectorDrawable)

由于AAPT工具支持将几个相关XML文件捆绑在一起的新格式,我们可以将之前示例中的XML文件合并到一个XML文件中:

<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: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="v"
                     android:fillColor="#000000"
                     android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
             group>
         vector>
     aapt:attr>

     <target android:name="rotationGroup">
         <aapt:attr name="android:animation">
             <objectAnimator
             android:duration="6000"
             android:propertyName="rotation"
             android:valueFrom="0"
             android:valueTo="360" />
         aapt:attr>
     target>

     <target android:name="v" >
         <aapt:attr name="android:animation">
             <set>
                 <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>
         aapt:attr>
      target>
 animated-vector>

兼容性问题

为了能够支持在低于Android 5.0(API 21)的设备上绘制矢量图像和矢量动画,可以使用VectorDrawableCompatAnimatedVectorDrawableCompat,它们分别通过两个库来提供支持:support-vector-drawableanimated-vector-drawable(包含在com.android.support:appcompat兼容包中)。

做法:

  1. 引用com.android.support:appcompat-v7:23.4.0及以上的版本

  2. 引入对于VectorDrawable的支持:

    对于Gradle Plugin 2.0以上:

    android {
      defaultConfig {
        vectorDrawables.useSupportLibrary = true
        }
    }
    

    对于Gradle Plugin 1.5及以下:

    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"
      }
    }
    

兼容包对以下内容是不支持的:

  • Path Morphing:路径变化动画
  • Path Interpolation:路径差值器(只能使用系统差值器)

使用方式

app:srcCompat

对于ImageViewImageButtonFloatingActionButton等可以使用app:srcCompat来指定矢量动画,并在使用到这个ImageView等的地方获取drawable,然后start。

<ImageView
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  app:srcCompat="@drawable/ic_add" />
(imageview as AnimatedVectorDrawableCompat).Drawable.start()

setImageResource()

还可以通过setImageResource()的方式动态设置。

animatedVectorDrawableCompat = AnimatedVectorDrawableCompat.create(context!!, R.drawable.animated_splash_logo)

imageView.apply {
    setImageDrawable(animatedVectorDrawableCompat)
}

animatedVectorDrawableCompat?.start()

Button、RadioButton

Button并不能直接通过app:srcCompat属性来使用,需要通过selector标签来使用。同时在使用时需要将下面的代码放在Activity的前面。

static {
    AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}

由于在23.2.0中的兼容包中存在问题,23.4.0修复了问题,但为了区分旧版本其添加了一个标签,这个标签需要我们手动打开,而这里的static就是为了打开该标签。

参考

  1. Vector drawables overview
  2. Add multi-density vector graphics
  3. Android自定义控件开发入门与实战 启舰著

你可能感兴趣的:(Android)