什么是svg
SVG 是一种基于 XML 语法的图像格式,全称是可缩放矢量图(Scalable Vector Graphics)。其他图像格式都是基于像素处理的,SVG 则是属于对图像的形状描述,所以它本质上是文本文件,体积较小,且不管放大多少倍都不会失真。
位图vs矢量图
位图
比如png等,是由像素点构成的图像,对于不同的屏幕需要不同的适配,同一张图片在不同屏幕上可能会失真。
Android系统在使用png等位图时如下图:
会将不同dpi的图片先经过解码之后再绘制显示在屏幕上。
矢量图
矢量图是用xml文件来存放图片绘制的矢量信息,可以适配不同的屏幕,不会因为拉伸等导致图片失真。
Android中svg中的过程如下:
svg结构
svg保存在xml文件里面,是一棵树如下图:
一个简单的svg对应的xml如下所示:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20"
android:viewportHeight="20"
tools:ignore="MissingDefaultResource">
<path
android:pathData="M19.3188,18.0273L13.232,11.9406C14.1766,10.7195 14.6875,9.2266 14.6875,7.6563C14.6875,5.7766 13.9539,4.0141 12.6273,2.6852C11.3008,1.3563 9.5336,0.625 7.6563,0.625C5.7789,0.625 4.0117,1.3586 2.6852,2.6852C1.3563,4.0117 0.625,5.7766 0.625,7.6563C0.625,9.5336 1.3586,11.3008 2.6852,12.6273C4.0117,13.9563 5.7766,14.6875 7.6563,14.6875C9.2266,14.6875 10.7172,14.1766 11.9383,13.2344L18.025,19.3188C18.1,19.3938 18.2219,19.3938 18.2969,19.3188L19.3188,18.2992C19.3938,18.2242 19.3938,18.1023 19.3188,18.0273ZM11.3687,11.3687C10.375,12.3602 9.0578,12.9062 7.6563,12.9062C6.2547,12.9062 4.9375,12.3602 3.9437,11.3687C2.9523,10.375 2.4063,9.0578 2.4063,7.6563C2.4063,6.2547 2.9523,4.9352 3.9437,3.9437C4.9375,2.9523 6.2547,2.4063 7.6563,2.4063C9.0578,2.4063 10.3773,2.95 11.3687,3.9437C12.3602,4.9375 12.9062,6.2547 12.9062,7.6563C12.9062,9.0578 12.3602,10.3773 11.3687,11.3687Z"
android:fillColor="#1F2329"
android:fillType="evenOdd"/>
</vector>
上面xml中
1、path对应路径
2、M代表移动画笔到对应的坐标 (move)
3、L代表直线(line)
4、C代表绘制曲线(curve)
5、A代表弧线(Arc)
6、Z表示结束(close)
Android 中使用svg
项目配置
在app的build.gradle文件的defaultConfig中添加vectorDrawables.useSupportLibrary = true如下:
android {
defaultConfig {
vectorDrawables.useSupportLibrary = true
}
}
图片所在module的build.gradle中也添加上
android {
defaultConfig {
vectorDrawables.useSupportLibrary = true
}
}
图片导入
figma上一些小的图片可以下载svg格式:
在Android studio按下面方式进行导入
导入后会自动生成xml文件
search.xml
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20"
android:viewportHeight="20"
tools:ignore="MissingDefaultResource">
<path
android:pathData="M19.3188,18.0273L13.232,11.9406C14.1766,10.7195 14.6875,9.2266 14.6875,7.6563C14.6875,5.7766 13.9539,4.0141 12.6273,2.6852C11.3008,1.3563 9.5336,0.625 7.6563,0.625C5.7789,0.625 4.0117,1.3586 2.6852,2.6852C1.3563,4.0117 0.625,5.7766 0.625,7.6563C0.625,9.5336 1.3586,11.3008 2.6852,12.6273C4.0117,13.9563 5.7766,14.6875 7.6563,14.6875C9.2266,14.6875 10.7172,14.1766 11.9383,13.2344L18.025,19.3188C18.1,19.3938 18.2219,19.3938 18.2969,19.3188L19.3188,18.2992C19.3938,18.2242 19.3938,18.1023 19.3188,18.0273ZM11.3687,11.3687C10.375,12.3602 9.0578,12.9062 7.6563,12.9062C6.2547,12.9062 4.9375,12.3602 3.9437,11.3687C2.9523,10.375 2.4063,9.0578 2.4063,7.6563C2.4063,6.2547 2.9523,4.9352 3.9437,3.9437C4.9375,2.9523 6.2547,2.4063 7.6563,2.4063C9.0578,2.4063 10.3773,2.95 11.3687,3.9437C12.3602,4.9375 12.9062,6.2547 12.9062,7.6563C12.9062,9.0578 12.3602,10.3773 11.3687,11.3687Z"
android:fillColor="#1F2329"
android:fillType="evenOdd"/>
</vector>
在布局中引用
<ImageView
android:id="@+id/Search"
android:layout_width="@dimen/size_24_dp"
android:layout_height="@dimen/size_24_dp"
android:layout_marginEnd="@dimen/size_14_dp"
android:src="@drawable/search"
/>
修改svg图片颜色
有时候设计给的图片一样但是颜色不同,如果每一个颜色svg都导入会增加包大小,可以通过在引用svg的xml中通过tint设置svg图片的颜色。
<ImageView
android:id="@+id/Search"
android:layout_width="@dimen/size_24_dp"
android:layout_height="@dimen/size_24_dp"
android:layout_marginEnd="@dimen/size_14_dp"
android:src="@drawable/search"
app:tint="@color/c_00B8E5" />
svg图片添加点击态
有时候需要根据不同状态显示不同颜色,svg中可以实现这种功能,首先定义一个color类型的selector。在res目录下创建一个color的文件夹,然后创建search_selector.xml如下:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/c_00B8E5" android:state_pressed="true" />
<item android:color="@color/black" />
</selector>
在需要点击态的xml中通过tint来控制颜色变化
<ImageView
android:id="@+id/Search"
android:layout_width="@dimen/size_24_dp"
android:layout_height="@dimen/size_24_dp"
android:clickable="true"
android:focusable="true"
android:src="@drawable/search"
app:tint="@color/search_selector" />
svg使用优化
在 api level 21以上使用svg默认情况下
1、会在drawable-anydpi-v24中生成seach.xml文件
search.xml的大小为673B
2、会在drawable-xxhdpi-v4中生成seach.png文件
search.png的大小为1.2kB
由于使用svg时生成了png图片所以包大小并没有减少。一定要配置
android {
defaultConfig {
vectorDrawables.useSupportLibrary = true
}
}
可以使用下面的方式使得的apk中不生成png图片,只保留svg的xml文件。
svg动画使用
如下在布局中添加svg的xml作为srcCompat
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/breakHeart"
android:layout_width="160dp"
android:layout_height="160dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/break_heart_anim" />
其中break_heart_anim.xml是svg动画xml。
<animated-vector xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
tools:targetApi="lollipop">
<aapt:attr name="android:drawable">
<vector
android:name="heartbreak"
android:width="56dp"
android:height="56dp"
android:viewportWidth="56"
android:viewportHeight="56">
<group
android:name="broken_heart_left_group"
android:pivotX="28"
android:pivotY="37.3">
<path
android:name="broken_heart_left"
android:pathData="M 28.031 21.054 C 28.02 21.066 28.01 21.078 28 21.09 C 26.91 19.81 25.24 19 23.5 19 C 20.42 19 18 21.42 18 24.5 C 18 28.28 21.4 31.36 26.55 36.03 L 28 37.35 L 28.002 37.348 L 27.781 36.988 L 28.489 36.073 L 27.506 34.764 L 28.782 33.027 L 26.944 31.008 L 29.149 28.725 L 27.117 27.143 L 29.149 25.018 L 26.488 22.977 L 28.031 21.054 L 28.031 21.054 Z"
android:fillColor="#ff0000"/>
</group>
...
</vector>
</aapt:attr>
<target android:name="broken_heart_left_group">
<aapt:attr name="android:animation">
<objectAnimator
android:propertyName="rotation"
android:duration="400"
android:valueFrom="0"
android:valueTo="-20"
android:valueType="floatType"
android:interpolator="@android:interpolator/linear_out_slow_in"/>
</aapt:attr>
</target>
</animated-vector>
在代码中使用下面方式开启动画
breakHeart.setOnClickListener {
if (breakHeart.drawable is AnimatedVectorDrawable) {
(breakHeart.drawable as AnimatedVectorDrawable).start()
}
}
动画效果如下
svg优点
1、svg比png,webp等小。
可以看到上面png和xml的大小对比,使用png大小差不多是xml的一倍。
2、svg放大不会失真
svg缺点
svg不支持硬件加速,所以渲染速度比png慢,下图是微信的数据对比
SVG在加载的过程中得到非常大优势,而Draw的时候因为没有硬件渲染导致性能远不如PNG。但通过在加载阶段的大幅提升,让SVG在整体耗时上赢了PNG。
svg兼容性
Android 4.4(API level 20)及以下版本,有两种解决方案 :
① 将矢量图生成为 PNG 图片 ;
② 使用 23.2 及以上版本的支持库 ;
在app的build.gradle文件中添加:
android {
defaultConfig {
generatedDensities = ['xhdpi', 'xxhdpi']
}
}
Android 5.0(API level 21)及以上版本,可以直接使用vector.xml文件,不需要将vector.xml文件转换成png。但是需要在build.gradle中添加
android {
defaultConfig {
vectorDrawables.useSupportLibrary = true
}
}
svg批量转换工具
https://github.com/MegatronKing/SVG-Android/tree/master/svg-vector-cli
使用方法
command line introductions
[-d/-dir] the target svg directory
[-f/-file] the target svg file
[-o/output] the output vector file or directory
[-w/width] the width size of target vector image
[-h/height] the height size of target vector image
command line samples
java -jar svg2vector-cli.jar -d D:\svg
or
java -jar svg2vector-cli.jar -f D:\svg\icon_facebook.svg
or
java -jar svg2vector-cli.jar -d D:\svg -o D:\vector
or
java -jar svg2vector-cli.jar -f D:\svg\icon_facebook.svg -o D:\vector\icon_facebook.xml
or
java -jar svg2vector-cli.jar -d D:\svg -o D:\vector -w 24 -h 24
or
java -jar svg2vector-cli.jar -f D:\svg\icon_facebook.svg -o D:\vector\icon_facebook.xml -w 24 -h 24
todo:
tint修改的是fillColor有些情况下点击态只需要修改strokeColor不需要修改fillColor
android:fillColor="#ffffff"
android:strokeColor="#2A97B9"/>
参考
1、https://developer.mozilla.org/en-US/docs/Web/SVG
2、https://developer.android.com/studio/write/vector-asset-studio
3、https://www.androidhive.info/2017/02/android-working-svg-vector-drawables/
4、https://developer.android.com/topic/libraries/support-library/packages?hl=zh-cn
5、https://www.growfox.co.uk/blog/5-reasons-you-should-be-using-svgs-over-pngs
6、https://mp.weixin.qq.com/s?__biz=MzAwNDY1ODY2OQ==&mid=207863967&idx=1&sn=3d7b07d528f38e9f812e8df7df1e3322&scene=4#wechat_redirect
4、https://medium.com/android-dev-hacks/android-vector-drawables-bfb515ba8f2e
5、https://tech.bytedance.net/articles/11584