首页 > 代码库 > Android UI:矢量图使用

Android UI:矢量图使用

一、矢量图简介
最近在进行Android App“瘦身 ”的时候,了解到矢量图(VectorDrawable)相关概念。
从Android5.0(API level 21)开始,有两个类支持矢量图:VectorDrawable和AnimatedVectorDrawable。VectorDrawable是一个矢量图,定义在一个XML文件中的点、线和曲线,和它们相关颜色的信息集合。AnimatedVectorDrawable是矢量图动画,使用多个XML文件而不是针对不同分辨率使用多个图片来实现动画。
使用矢量图主要有如下两个优:
  图片扩展性:它可以进行缩放并且不损失图片的质量,这意味着使用同一个文件对不同屏幕密度调整大小并不损失图片的质量;
  图片大小小:同样大小和内容图片下相比,矢量图比PNG图片更小,这样就能得到更小的APK文件和更少的维护工作;
然而,系统渲染VectorDrawable需要花费更多时间。因为矢量图的初始化加载会比相应的光栅图片消耗更多的CPU周期,但是两者之间的内存消耗和性能接近。因此我们可以只考虑在显示小图片的时候使用矢量图(建议你限制矢量图在200*200dp),越大的图片在屏幕上显示会消耗更长的时间进行绘制;

二、VectorDrawable
1.VectorDrawable简介

VectorDrawable定义了一个静态Drawable对象。和SVG格式非常相似,每个Vector图形被定义成一个树型结构,它由path和group对象组成。每条path包含对象轮廓的几何形状,每个group包含了变化的详细信息。所有path的绘制顺序和它们在XML出现的顺序相同;
技术分享
2.VectorDrawable XML文件生成
Android Studio包含一个称为Vector Asset Studio的工具,它提供了一个简单的方法将矢量图以XML文件的形式添加到项目中。支持如下两种方式:
  添加Meterial Icon;
  导入可拉伸矢量图(SVG)和Adobe Photoshop文档(PSD)文件;
启动Vector Asset Studio
Android Studio->项目窗口->Android视图->选择res目录->New->Vector Asset(Android Plugin for Gradle 1.5.0或者更高);

技术分享
使用Meterial Icon
Google meterial Design规范提供了meterial icons,你可以在你的Android app中使用。Vector Asset Studio帮助你选择,导入和设置meterial icon大小,定义透明度和Right-to-Left(RTL)镜像设置;

选择Meterial Icon->点击Icon按钮->Select Icon对话框(右边)->选择导入图片->OK,根据你的需要改变资源名称,大小,透明度和Right-To-Left镜像设置,点击Next按钮:
技术分享
根据你的需要改变模块和资源目录,点击Finish按钮:
技术分享
在app/src/main/res/drawable目录下,Vector Asset Studio添加了一个定义了适量图片的XML文件ic_assignment_ind_black_24dp.xml;
技术分享
使用本地文件(SVG,PSD)
Vector Asset Studio也可以让你导入你自己的SVG和PSD文件。SVG是W3C一个基于XML的开源标准,PSD文件格式支持Adobe Photoshop。它支持基本的标准,但不支持所有的SVG和PSD功能。当你选择一个SVG或者PSD文件,它会立即给出是否支持图形编码的反馈。尽管矢量图支持一个或者更多的色彩,在许多场景下使用黑色图标(android:fillColor="#FF000000")。使用这种方式,你可以给你填充在布局中的矢量图添加一个tint,然后图标的颜色变成tint的颜色。如果图标的颜色不是黑色,图标的颜色可能和tint颜色混合;
选择Local file(SVG,PSD)->点击Path更多按钮->选择你要导入的图片->点击OK按钮->根据你的需要修改资源名称、大小、和透明度等属性->点击Next按钮(如果SVG或者PSD文件有一下不支持的功能,在Vector Asset Studio底部Errors会显示错误信息);
技术分享
同上,根据你的需求改变资源输出的模块和目录->点击Finish按钮即可;

3.VectorDrawable使用
这里我们就简单使用一个Demo进行演示,显示两个图片和一个动画,目录如下:
技术分享

构建兼容性配置
在Android 5.0之前(API level 21),Support Library 23.2或者更高的版本提供了矢量图片和矢量图片动画完整的支持。Android5.0之前的版本不支持矢量图,如果你支持最小API level是这些版本,你有如下两个选择:
  生成PNG文件;
  使用Support library;
为了在运行Android5.0(API level 21)之前版本设备不支持矢量图片和矢量图片动画,VectorDrawableCompat和AnimatedVectorDrawableCompat通过两个新的Support Libraries:support-vector-drawable和animated-vector-drawable分别进行支持;
在你app模块的build.gradle文件中添加vectorDrawables元素,使你的app使用矢量图support library;
flight/build.gradle文件
apply plugin: ‘com.android.library‘
android {
    ... ...
    defaultConfig {
        ... ...
        vectorDrawables.useSupportLibrary = true
    }
    ... ... 
}
dependencies {
    ... ...
    compile ‘com.android.support:appcompat-v7:25.0.1‘
}
打包完成后,我们分析下APK打包了矢量图的xml文件;
技术分享
我们也可以采用低版本构建生成PNG图片的兼容方式;
flight/build.gradle文件
apply plugin: ‘com.android.library‘
android {
    ... ...
    aaptOptions {
        additionalParameters "--no-version-vectors"
    }
    ... ...
}
打包完成后,我们分析下APK发现xml文件对应生成了对应png图片;
技术分享
VectorDrawable定义
只需要一个资源文件(如上使用Vector Asset Studio导入)就可以创建矢量图片,而位图图片需要为每个屏幕密度提供一个文件。如果要创建一个矢量图片,在<vector>XML元素中定义如下:
flight/src/main/res/drawable/heart.xml文件
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="100dp"
    android:height="100dp"
    android:viewportHeight="32"
    android:viewportWidth="32">
    <path
        android:fillColor="#8000"
        android:pathData=http://www.mamicode.com/"M20.5,9.5c-1.955,0,-3.83,1.268,-4.5,3c-0.67,-1.732,-2.547,-3,-4.5,-3C8.957,9.5,7,11.432,7,14>flight/src/main/res/drawable/battery.xml文件
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="100dp"
    android:height="100dp"
    android:viewportHeight="24.0"
    android:viewportWidth="24.0">
    <group
        android:name="rotationGroup"
        android:pivotX="10.0"
        android:pivotY="10.0"
        android:rotation="15.0">
        <path
            android:name="vect"
            android:fillAlpha=".3"
            android:fillColor="#FF000000"
            android:pathData=http://www.mamicode.com/"M15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33V9h4.93L13,7v2h4V5.33C17,4.6 16.4,4 15.67,4z" />>VectorDrawable引用
flight/src/main/res/layout/activity_flight.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto
    xmlns:tools="http://schemas.android.com/tools"
    ... ... 
    tools:context="com.qunar.flight.FlightActivity">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="flight" />
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:srcCompat="@drawable/heart"/>
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:srcCompat="@drawable/battery"/>
</LinearLayout>
注意:如果你不使用support library兼容,使用生成png图片,那么不用添加命名空间,采用android:src属性即可;
运行结果
技术分享

三、AnimatedVectorDrawable实践
1.AnimatedVectorDrawable简介

AnimatedVectorDrawable向矢量图添加动画属性。你可以分三个文件或者一个Drawable的XML文件定义矢量图动画。为了更好的理解,让我们来看看这两种方式:多个XML文件和单个XML文件;
2.多个XML文件
使用这种方式,你需要定义三个单独的XML文件:
  一个VectorDrawable XML文件;
  一个AnimatedVectorDrawable XML文件定义了目标VectorDrawable,动画用于目标path和group,属性和动画定义为ObjectAnimator对象或者AnimatorSet对象;
  一个动画XML文件;
AnimatedVectorDrawable多XML文件定义
首先我们想将哪个矢量图片动起来(vd.xml),该图片要运行什么样的动画(rotation.xml和path_morph.xml),将矢量图和动画结合(avd.xml);
flight/src/main/drawable/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=http://www.mamicode.com/"M300,70 l 0,-70 70,70 0,0 -70,70z" />>flight/src/main/res/anim/rotation.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:duration="6000"
        android:propertyName="rotation"
        android:valueFrom="0"
        android:valueTo="360" />
</set>
flight/src/main/res/anim/path_morph.xml
<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>
flight/src/main/drawable/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>
AnimatedVectorDrawable引用
接下来,我们就可以在视图中应用该适量图片动画,运行动画;
flight/src/main/layout/activity_flight.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    ... ... 
    tools:context="com.qunar.flight.FlightActivity">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="flight" />
    ... ... 
    <ImageView
        android:id="@+id/imageview1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src=http://www.mamicode.com/"@drawable/avd"/>>flight/src/main/java/com/qunar/flight/FlightActivity.java
public class FlightActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_flight);

        ImageView imageView = (ImageView) findViewById(R.id.imageview1);
        Drawable drawable = imageView.getDrawable();
        if (drawable instanceof Animatable){
            ((Animatable) drawable).start();
        }
    }
}
运行效果
技术分享
3.单个XML文件
使用这种方式,你可以合并相关的XML文件到一个使用XML Bundle格式的XML文件中。在构建app的时候,aapt tag会创建单独的资源并且在矢量动画引用它们。这种方式要求Build Tools 24或者更高,并且输出是向后兼容的;
AnimatedVectorDrawable单文件定义
flight/src/main/drawable/single_avd.xml

<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" >
     <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=http://www.mamicode.com/"M300,70 l 0,-70 70,70 0,0 -70,70z" />>注意:为了优化重绘性能,每个VectorDrawable对象创建了Bitmap缓存。因此,引用相同的VectorDrawable意味着共享相同的Btimap缓存。如果这些引用大小上不一致,Bitmap将会在大小每次变化的时候重建和重绘。换句话说,如果VectorDrawable使用了不同的大小,为每个大小创建VectorDrawable会效率更高;

四、代码库

QProject:https://github.com/Pengchengxiang/QProject  分支:ui/vectordrawable




Android UI:矢量图使用