图片压缩知识梳理(6) VectorDrawable 及 AnimatedVectorDrawable 使用详解

1、Vector优势

VectorDrawable主要有两个优势:屏幕自适应体积小android

  • 若是咱们使用的是普通的png或者jpg图片,那么为了能让图片在不一样分辨率的机型上可以很好地展示,一般会在各个drawable文件夹下放置不一样分辨率大小的图片文件,而VectorDrawable则不须要,仅仅只使用一套资源,就可以适应任何分辨率的手机。
  • 对于某些图片,VectorDrawable素材的大小要比pngjpg小不少。

2、SVGVector的基本概念

下面,咱们先介绍一下SVGVectorDrawable的基本概念:缓存

  • SVG:它并非Android平台特有的概念,它的全称为可缩放矢量图形,简单的来讲,就是用于描述二维矢量图形的图形格式,更加详细的解释能够参考:SVG - 百度百科
  • VectorDrawable:它是Android当中的SVG实现,它并不支持SVG的所有语法,只是支持部分有必要的部分。

3、获取VectorDrawable

俗话说的好,巧妇难为无米炊,这一节咱们就来介绍一下如何得到一个VectorDrawable,通常获取的方式有如下三种方式:bash

  • 先获取SVG素材,再经过工具转换成为VectorDrawable
  • 经过Android Studio中的素材库,直接获取VectorDrawable
  • 根据Vector的语法,本身描述

3.1 先获取SVG素材,再经过工具转换成为VectorDrawable

首先获取SVG素材,再将它转换成为VectorDrawable是最经常使用的方式,而SVG素材的获取又有如下几种途径:app

  • 网站直接下载SVG图像
  • UI设计师使用专业的工具导出
  • 经过 VectorMagic 软件,将pngjpg素材转换为SVG图像

对于我的开发者而言,通常都会采用第一种方式,咱们这里也只介绍第一种方式。不少文章都推荐过这个网站:iconFront - 阿里巴巴,它提供了SVGPNG素材的直接下载: eclipse

下载完毕以后,在 Android StudioNew选项中,选择 Vector Asset
打开以后,选择 Local file,并打开咱们下载后的图像,再选择 next保存到指定的文件夹:
最后,咱们会获得一个 *.xml文件,这个就是 VectorDrawable

<vector
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:height="24dp" 
    android:viewportHeight="1024.0"
    android:viewportWidth="1024.0" 
    android:width="24dp">
    <path android:fillColor="#FF000000" 
        android:pathData="M887.9,298.2c-12.9,-12.1 -33.2,-11.5 -45.2,1.4L415.9,754l-233.1,-229.7C170.2,511.9 150,512 137.5,524.6c-12.4,12.6 -12.3,32.9 0.4,45.2l256.5,252.7c0.1,0.1 0.2,0.1 0.3,0.2 0.1,0.1 0.1,0.2 0.2,0.3 2,1.9 4.4,3 6.8,4.3 1.2,0.7 2.1,1.7 3.4,2.1 3.8,1.5 7.8,2.2 11.7,2.2 4.2,0 8.4,-0.8 12.3,-2.5 1.3,-0.5 2.3,-1.7 3.6,-2.4 2.4,-1.4 4.9,-2.6 6.9,-4.7 0.1,-0.1 0.1,-0.3 0.2,-0.4 0.1,-0.1 0.2,-0.1 0.3,-0.2l449.2,-478.2C901.4,330.6 900.8,310.3 887.9,298.2z"/>
</vector>
复制代码

3.2 经过Android Studio中的素材库,直接获取VectorDrawable

仍是在上面的那个界面,咱们勾选另一个选项: ide

这里面有 Material Design提供的素材库:
经过这种方式,一样能够获取到一个 *.xmlVectorDrawable

<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="24dp"
        android:height="24dp"
        android:viewportWidth="24.0"
        android:viewportHeight="24.0">
    <path
        android:fillColor="#FF000000"
        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,5c1.66,0 3,1.34 3,3s-1.34,3 -3,3 -3,-1.34 -3,-3 1.34,-3 3,-3zM12,19.2c-2.5,0 -4.71,-1.28 -6,-3.22 0.03,-1.99 4,-3.08 6,-3.08 1.99,0 5.97,1.09 6,3.08 -1.29,1.94 -3.5,3.22 -6,3.22z"/>
</vector>
复制代码

3.3 根据Vector的语法,本身描述

3.13.2当中,咱们能够看到,最终VectorDrawable都是用一个*.xml来表示的,那么,咱们固然能够根据SVG的语法,来编写一个xml文件,经过pathData属性进行描述,不过这种方式较为繁琐,通常不会采用。工具

4、Vector语法

虽说大多数状况下,咱们不会彻底手动去编写Vectorxml文件,可是,对于Vector的基本语法仍是有必要了解一些的,由于在咱们后边谈到的动态Vector中须要了解对于每一个标签有哪些属性能够设置,Vector包含下面三种标签:布局

<vector>
    <group>
        <path>
        <path>
    </group>
</vector>
复制代码

4.1 <vector>标签

  • name:矢量图的名字
  • width, height:矢量图的固有宽高,一般使用dp
  • viewportWidth, viewportHeight:把矢量图的宽高分红多少个单元,这里的每一个单元就对应pathData中的一个点坐标。

4.2 <path>标签

  • name:路径的名称。
  • fillColor:图形的填充颜色。
  • pathData:定义控制点的位置,相似与Canvas当中的Path类。

4.3 <group>标签

<group>用来把多个<path>组合在一块儿,进行相同的处理。性能

更多的属性能够查阅下面这两篇文章:gradle

VectorDrawable 详解 Android 中的 SVG 图像的各个属性意义

5、Vector的兼容性问题

为了让VectorDrawable可以在Android 5.0如下版本的手机上使用,咱们须要引入support包,并修改gradle的配置。

5.1 引入support

VectorDrawable是在Android 5.0以后提出来的,所以,若是咱们须要在低版本上使用,那么就要引入com.android.support:appcompat-v7包,要求版本号大于等于23.2.0,这里咱们选用的是:

compile 'com.android.support:appcompat-v7:25.3.1'
复制代码

Vector相关的两个包为:

5.2 修改gradle配置文件

若是**gradle的版本小于2.0**:

android {
    defaultConfig {
        generatedDensities = []
    }

    aaptOptions {
        additionalParameters "--no-version-vectors"
    }
}
复制代码

若是**gradle的版本大于2.0**,例如咱们所用的版本:

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.3.0'
    }
}
复制代码

那么须要这样配置:

android {
    defaultConfig {
         vectorDrawables.useSupportLibrary = true
    }
}
复制代码

关于更多兼容性的问题,能够查看下面这篇文章

Android Vector 曲折的兼容之路

6、静态VectorDrawable

若是使用静态的方式展现VectorDrawable,那么很简单,只须要像使用普通的drawable同样,首先,咱们按照第三节的方法,获取到一个vectorDrawable,并把它放在drawable文件夹下:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="24dp"
        android:height="24dp"
        android:viewportWidth="24.0"
        android:viewportHeight="24.0">
    <path
        android:fillColor="#FF000000"
        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,5c1.66,0 3,1.34 3,3s-1.34,3 -3,3 -3,-1.34 -3,-3 1.34,-3 3,-3zM12,19.2c-2.5,0 -4.71,-1.28 -6,-3.22 0.03,-1.99 4,-3.08 6,-3.08 1.99,0 5.97,1.09 6,3.08 -1.29,1.94 -3.5,3.22 -6,3.22z"/>
</vector>
复制代码

咱们能够应用于:

  • ImageViewsrc
  • Viewbackground
  • TextViewdrawable
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center_horizontal">
    <ImageView
        android:id="@+id/iv_static"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:clickable="true"
        android:src="@drawable/ic_account_circle_selector"/>
    <View
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:clickable="true"
        android:background="@drawable/ic_account_circle_selector"/>
    <TextView
        android:drawableStart="@drawable/ic_account_circle_black_24dp"
        android:text="UserName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>
复制代码

在上面的例子中,咱们还使用了ic_account_circle_selector,其定义以下:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/ic_account_circle_black_24dp" android:state_pressed="true"/>
    <item android:drawable="@drawable/ic_account_circle_black_normal_24dp"/>
</selector>
复制代码

ic_account_circle_black_24dp就是以前获取到的素材,而ic_account_circle_black_normal_24dp就是改变了它的fillColor属性:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24.0"
    android:viewportHeight="24.0">
    <path
        android:fillColor="#80000000"
        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,5c1.66,0 3,1.34 3,3s-1.34,3 -3,3 -3,-1.34 -3,-3 1.34,-3 3,-3zM12,19.2c-2.5,0 -4.71,-1.28 -6,-3.22 0.03,-1.99 4,-3.08 6,-3.08 1.99,0 5.97,1.09 6,3.08 -1.29,1.94 -3.5,3.22 -6,3.22z"/>
</vector>
复制代码

点击后的效果为:

7、动态VectorDrawable

动态的VectorDrawable,也就是AnimatedVectorDrawable,通常来讲,它的整个结构以下图所示:

为了实现动态的 VectorDrawable,通常须要提供三种类型的 *.xml文件,这三个 xml文件的根标签分别为:

  • vector:图像资源,也就是咱们上面讨论的静态vector,定义于res/drawable文件夹下。
  • objectAnimator:定义图像资源中个元素的动画行为,定义于res/anim文件夹下。
  • animated - vector:对vector中的元素与objectAnimator进行组合,定义于res/drawable文件夹下。

7.1 对<group>标签进行动画

下面,咱们先看一个简单的例子,上面咱们所谈到的三个*.xml文件以下:

7.1.1 vector文件

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24.0"
    android:viewportHeight="24.0">
    <group android:name="group_account" android:pivotX="12" android:pivotY="12">
        <path
            android:fillColor="#FF000000"
            android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,5c1.66,0 3,1.34 3,3s-1.34,3 -3,3 -3,-1.34 -3,-3 1.34,-3 3,-3zM12,19.2c-2.5,0 -4.71,-1.28 -6,-3.22 0.03,-1.99 4,-3.08 6,-3.08 1.99,0 5.97,1.09 6,3.08 -1.29,1.94 -3.5,3.22 -6,3.22z"/>
    </group>
</vector>
复制代码

这里咱们生成了一个头像的Vector素材,它的图像以下,注意到,咱们用一个group标签把path标签包裹起来,而且给它定义了一个name属性为group_account,这个属性在后面会用到。

7.1.2 objectAnimator文件

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:duration="500"
        android:propertyName="rotation"
        android:repeatCount="infinite"
        android:valueFrom="0"
        android:valueTo="360"/>
</set>
复制代码

objectAnimator的定义和属性动画是相同的,咱们须要指定须要变换的属性,也就是propertyName,并经过valueFrom/valueTo指定变化的起点值和终点值,这里咱们选择了采用无限循环的方式来变换目标的rotation属性。

7.1.3 animated-vector文件

<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/account_vector">
    <target
        android:animation="@anim/account_object_animator"
        android:name="group_account"/>
</animated-vector>
复制代码

animated-vectorvector是一一对应的关系,所以须要在根标签中指定android:drawable,也就是在7.1.1中的vector文件。 而每个target标签是内是成对的name - animation属性,name就是在7.1.1中声明的groupname,而animation则是在7.1.2中定义的动画文件。

若是objectAnimator所指定的属性在name所对应的标签中没有,那么不会发生任何变化。

7.1.4 启动动画

首先,在布局的xml文件中,把imageViewsrc指定为7.1.3中的animate-vector

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center_horizontal">
    <ImageView
        android:id="@+id/iv_dynamic"
        android:src="@drawable/account_animated_vector"
        android:text="UserName"
        android:layout_width="50dp"
        android:layout_height="50dp"/>
</LinearLayout>
复制代码

在代码中,须要手动得到这个vectorDrawable,而后启动动画:

public class VectorDrawableActivity extends AppCompatActivity {

    private ImageView mAnimateView;
    private AnimatedVectorDrawable mAnimatedVectorDrawable;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_vector_drawable);
        mAnimateView = (ImageView) findViewById(R.id.iv_dynamic);
        mAnimatedVectorDrawable = (AnimatedVectorDrawable) mAnimateView.getDrawable();
        mAnimateView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mAnimatedVectorDrawable.start();
            }
        });
    }
}
复制代码

效果以下:

7.2 对<path>标签中的属性进行动画

7.1中,演示了如何对group标签的属性进行变换,下面,咱们再演示一下如何对path标签中的属性进行变换。和前面相似,咱们依然须要三种类性的*.xml文件

7.2.1 vector文件

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24.0"
    android:viewportHeight="24.0">
    <path
        android:name="path_check"
        android:fillColor="#FF000000"
        android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
</vector>
复制代码

这里,一样须要给path指定一个名字,用于后面和objectAnimator进行关联,它的素材为:

7.2.2 objectAnimator文件

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:duration="500"
        android:propertyName="trimPathEnd"
        android:valueFrom="0"
        android:valueTo="1"
        android:valueType="floatType"/>
</set>
复制代码

这里,咱们选择的是path标签下的trimPathEnd属性,它表示从Path终点的位置去除Path,与之对应的还有trimPathStart属性,它表示从Path起点的位置去除Path,而trimPathOffset则能够改变Path的起点,这三个值的取值范围都是0~1

7.2.3 animated-vector文件

<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/check_vector">
    <target
        android:animation="@anim/check_object_animator"
        android:name="path_check"/>
</animated-vector>
复制代码

效果以下:

7.3 <path>之间切换

除了上面普通的属性以外,还支持对<path>标签下的<pathData>进行改变,系统会比较两个pathData之间的差异,并自动产生合适的动画。须要注意,它要求这两个pathData的点坐标的个数是相同的

7.3.1 Vector文件

这里,咱们须要生成两个vectorDrawable,首先是起始的VectorDrawable

<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="24dp"
        android:height="24dp"
        android:viewportWidth="24.0"
        android:viewportHeight="24.0">
    <path
        android:name="path_arrow_down"
        android:fillColor="#FF000000"
        android:pathData="M7,10l5,5 5,-5z"/>
</vector>
复制代码

它的素材为:

接着是终点的 VectorDrawable

<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="24dp"
        android:height="24dp"
        android:viewportWidth="24.0"
        android:viewportHeight="24.0">
    <path
        android:fillColor="#FF000000"
        android:pathData="M7,14l5,-5 5,5z"/>
</vector>
复制代码

它对应的素材为:

7.3.2 objectAnimator文件

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:duration="500"
        android:propertyName="pathData"
        android:valueFrom="M7,10l5,5 5,-5z"
        android:valueTo="M7,14l5,-5 5,5z"
        android:valueType="pathType"/>
</set>
复制代码

这里的propertyNamevalueType须要分别定义为pathDatapathType,而起点和终点的值就是咱们在7.3.1生成的两个VectorDrawable所对应的pathData属性的值。

7.3.3 animated-vector文件

<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/arrow_down_vector">
    <target
        android:animation="@anim/arrow_down_object_animator"
        android:name="path_arrow_down"/>
</animated-vector>
复制代码

最终的效果为:

8、VectorDrawable的性能

关于VectorDrawable的性能问题,Android Vector 曲折的兼容之路 这篇文章说的很好,所以直接引用过来了:

  • Bitmap的绘制效率并不必定会比Vector高,它们有必定的平衡点,当Vector比较简单时,其效率是必定比Bitmap高的,因此,为了保证Vector的高效率,Vector须要更加简单,PathData更加标准、精简,当Vector图像变得很是复杂时,就须要使用Bitmap来代替了。
  • Vector适用于ICONButtonImageView的图标等小的ICON,或者是须要的动画效果,因为BitmapGPU中有缓存功能,而Vector并无,因此Vector图像不能作频繁的重绘
  • Vector图像过于复杂时,不只仅要注意绘制效率,初始化效率也是须要考虑的重要因素
  • SVG加载速度会快于PNG,但渲染速度会慢于PNG,毕竟PNG有硬件加速,但平均下来,加载速度的提高弥补了绘制的速度缺陷。

9、参考文献

1. Android Vector 曲折的兼容之路 2. VectorDrawable 怎么玩 3. Android 使用矢量图(SVG, VectorDrawable)实践篇 4. SVG - 百度百科 5. Android 中的 SVG 图像的各个属性意义

相关文章
相关标签/搜索