当Android遇到SVG

/ 0评 / 9

矢量图SVG

从Android 5.0开始,Android允许开发者使用SVG图片作为图片资源,有关SVG的更多信息可以查看w3school上面的相关介绍。

矢量图有什么优点呢?首先,矢量图不会失真,不管放大多少倍,矢量图都是无损的,其次,矢量图的大小往往是jpg图片的十几分之一,很适合作为程序中使用,而且结合Android退出的矢量图动画,更能做出一些很炫酷的动效,下面我就来介绍下,如何在Android中使用矢量图。

获取一张矢量图并导入

首先想要获取矢量图资源,强烈推荐阿里巴巴图标库,其次,Android Studio也自带了全套的Material Design图标,很是nice。导入方式也很简单,首先在res文件夹上面右键,如下图所示就可以导入SVG图片了,可以选择Material Design中的矢量图或者导入我们自己下载的矢量图。

导入以后的矢量图在drawable文件夹下面,具体是如下形式,显示出来是一个向左的箭头,关键的是(整个文件只有352B不到1KB!!!!)

<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="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
</vector>

vector标签介绍

其中android:width以及android:height不用多说,说的是图片的大小,重点是android:viewportWidth以及android:viewportHeight,这两个属性代表的含义是将前面定义的宽高等分为多少份,上面的例子中就是将24dp分成24份,这样就有了一个24x24的坐标系,然后path代表的是线,android:pathData就是这条线中一些点的坐标,具体可以查看SVG语法。

通过上面的分析,我们可以知道,其实vector类似于描述了一个坐标系,然后通过path等设置一系列的点、线在坐标系中绘制图形,这也是为什么SVG图片不失真的原因。至于上面定义的 android:width="24dp"与android:height="24dp",我们在使用的时候,可以设置控件的宽高,让其被拉伸,毕竟不会失真。

SVG图片的兼容之路

最前面也说了,SVG图像是Android 5.0以后引入的,在5.0之前使用矢量图会怎么样呢?经过我的实验是,啥事都木有,正常显示,先放下手里的板砖听我解释,其实看看下面这张图就知道了。可以看到,在drawable-anydpi-v21文件夹里面的是矢量图,其他几个png图片是Android Studio自动帮我们生成的,当设备版本大于5.0的时候,使用矢量图,5.0以下的时候,会使用png图片。

不过上面也提到了,我们使用矢量图的一个重大原因是矢量图比较小巧,别看上图中显示的是1.99kb,那是被自动对齐以后的,如果编译器帮我们生成了png,那不是与我们的初衷相悖么。

关于这一点,可以参看官方文档如果只想使用SVG图片,那么需要修改 build.gradle 文件以及使用com.android.support:appcompat-v7:23.2.0 +

// Gradle Plugin 2.0+  
android {  
defaultConfig {  
		vectorDrawables.useSupportLibrary = true
		//指定生成哪种分辨率下的位图资源
		vectorDrawables.generatedDensities = ['hdpi','xxhdpi']	 
	}  
}  
//或更高
dependencies {
  compile 'com.android.support:appcompat-v7:23.2.0'
}

如果使用Gradle版本较低

// Gradle Plugin 1.5  
android {  
	defaultConfig {  
	 generatedDensities = []  
	}  

	// This is handled for you by the 2.0+ Gradle Plugin  
	aaptOptions {  
	additionalParameters "--no-version-vectors"  
	}  
}  
//或更高
dependencies {
  compile 'com.android.support:appcompat-v7:23.2.0'
}

在布局文件中使用

<ImageView
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/main_svg_img"
    android:layout_width="50dp"
    android:layout_height="50dp"
    app:srcCompat="@drawable/ic_transfer_within_a_station_black_24dp"/>

可以看到,我这里使用的是app:srcCompat,并且使用此View的Activity必须继承AppCompatActivity,如果不使用app:srcCompat会直接崩溃,如果不继承AppCompatActivity,那么图片就没了,程序运行时改变可以直接调用mSvgImg.setImageResource方法。可参见官方文档

其他控件

对于Button来说,使用 app:srcCompat 没有啥效果,不过我们可以取巧,为Button设置一个android:background为selector,然后在selector中使用SVG图像即可,不过,会崩溃!!!

解决方法,在Activity中加入如下代码

static {
    AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}

不过设置的selector无效,只能显示selector中默认的那个SVG图片。参考链接:http://www.jianshu.com/p/e3614e7abc03

SVG图片动画

终于到了激动人心的动画时刻了,其实对于Android目前来说,一般使用SVG图片用来进行一些nice的动画效果,如下图。使用传统动画都无法实现这种效果。

首先要使用SVG图片动画,其实与我们上面使用SVG图片类似,首先需要一个SVG图片,上图中的图片为。

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

上面name为search的代表那个搜索图标,下面name为bar的代表下划线,之所以这里添加了name属性,是为了让我们在进行动画的时候,能控制指定部分的图像。

有了SVG图片,那么我们还需要使用animated-vector标签来将动画与SVG图像联系在一起。如下

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

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

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

</animated-vector>

可以看到animated-vector中的android:drawable="@drawable/searchbar"代表需要执行动画的SVG图片,里面的target标签一看就懂,name就是我们在上面指定的name,android:animation就是代表在指定的name上面需要进行的动画。

好了,接下来我们指定动画就可以了,可是我们使用什么区指定动画呢?答案就是属性动画。因为SVG标签path、grou、vector、clip-path都有自己的属性,所以我们可以使用属性动画动态设置其属性,这样就完成动画了。下面贴出其有效属性。

标签 属性名 含义 取值范围 类型
<vector> alpha 透明度 0-1 floatType
<group> rotation 旋转角度 0-360 floatType
pivotX 旋转中心X floatType
pivotY 旋转中心Y ? floatType
scaleX X轴缩放 floatType
scaleY Y轴缩放 floatType
translateX X轴平移 floatType
translateY Y轴平移 floatType
<path> pathData ? ? ?
fillColor 填充色 ? colorType
strokeColor 描边色 ? colorType
strokeWidth 描边宽 ? ?
strokeAlpha 描边透明度 ? ?
fillAlpha 填充透明度 0-1 floatType
trimPathStart 路径开始到结束 0-1 floatType
trimPathOffset ? ? ?
<clip-path> pathData ? ? ?

?号表示不确定或者未实验,官方文档:https://developer.android.com/reference/android/graphics/drawable/AnimatedVectorDrawable.html?hl=zh-cn

下面贴出anim_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"/>

可以看到,设置trimPathStart从1-0,表示先从全部显示慢慢"缩到"没有,然后无限循环,不停倒放,个人实验发现android:valueType不是必须写明的。

然后通过app:srcCompat将animated-vector设置给ImageView即可。

最后启动动画代码

ImageView imageView = (ImageView) view;
Drawable drawable = imageView.getDrawable();
//开始动画,此方法兼容5.0之前
if (drawable instanceof Animatable) {
    ((Animatable) drawable).start();
}

至此,我们已经能够在Android 2.1以上版本上面使用SVG图片了,并且也能使用SVG动画了。

源码地址:https://github.com/CB2Git/BlogDemoRepository/tree/master/VectorDemo

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注