1. SVG 簡單介紹
1.1 是什么
SVG是指可伸縮矢量圖形 (Scalable Vector Graphics),它不同于傳統的位圖,不是通過存儲圖像中每一點的像素值來保存與使用圖形,而是通過 XML 文件來定義一個圖形,通過一些特定的語法和規則來繪制出我們所需的圖像
1.2 和其他格式圖片對比
SVG 的方式是事先定義好怎么去畫這個圖,然后等要用的時候再把它去畫出來,而使用傳統的位圖的話就是已經有了畫出來的圖,然后要用的時候直接把畫好的圖拿出來用
- SVG 是在要用圖的時候再把圖畫出來,所以理所當然的在圖片顯示的時候會花費更多的時間消耗更多的資源。
- 同樣由于上一個原因, SVG 并不太適合層次過于復雜細節過于繁多的圖片。
- 位圖是事先已經畫好的圖片,所以適應性必然沒有 SVG 好,同一張圖片在不同分辨率下顯示會有差異。
- SVG 的文件里存儲了繪制圖片的相關信息,所以我們能夠對圖片的線條有一個非常清晰的感知,這在做動畫的時候特別有用。
- SVG 沒有存儲任何圖像的像素信息,所以 SVG 的文件體積遠小于傳統的位圖文件。
- SVG 的文件畫出來的圖像是矢量圖,所以不會存在失真的問題,理論上支持任何級別的縮放。
2. SVG 使用
2.1 獲取一個SVG文件
要使用 SVG ,那么首先我們肯定得有一個 SVG 文件。我們一般都有兩種方式來獲得一個 SVG 文件:自己寫一個 SVG 文件,或者通過 AI 或一些網站作圖之后導出它的 SVG 文件。
2.1.1 自己手動編寫一個 SVG 文件(Android 中)
前面說過,SVG 文件里面存儲的是如何去繪制目標圖片的相關信息,所以理論上我們是可以從 0 開始寫一個我們自己的 SVG 文件的——只要知道它繪制文件的規則,一切皆可繪制。我們先來看一下一個簡單的 SVG 文件:
在 res\drawable 目錄下,新建一個xml文件
VectorDrawable也是Drawable的一個直接子類, 像其它Drawable那樣通常情況下是在xml中定義, 它對應的xml標簽是<vector/>, 基本結構如下:
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="132dp"
android:height="132dp"
android:viewportHeight="132.0"
android:viewportWidth="132.0">
<path
android:pathData="M50,2 L80.813,2 L80.813,130 L50,130 L50,2 Z"
android:strokeColor="#e33e2b"
android:strokeWidth="8" />
</vector>
- vector是VectorDrawable對應的根標簽
- android:width與android:height對應矢量圖的實際大小(矢量圖是可以無限大, 但通常情況下一個圖片都應該有一個原始大小, 假如你將此VectorDrawable作為一個ImageView的src, ImageView的大小都設置為wrap_content, 則ImageView對應的實際大小就是這里設置的大小)
- android:viewportWidth與android:viewportHeight是指當前Drawable對應的虛擬Canvas的大小, 之所以說是虛擬的是因為實際上并不存在這樣一個Canvas, 又之所以需要這個值是因為在+ + <path/>標簽中的路徑數據要基于具體的坐標系來繪制.
- <path/>標簽對應路徑信息, 這里的path與我們自定義繪制圖形時用的Path原理一樣, 就是記錄一些繪圖操作, 具體對應其中的pathData.PathData中對應的路徑描述符號不需要我們去記, 通常情況下由繪圖軟件生成svg圖片再從svg文件中提取.
這個文件繪制出來的圖形是這樣的(沒錯,在編寫 SVG 的 XML 文件的時候 Android Studio 是可以預覽的,很強大):
效果如下 :
2.1.2 用工具生成SVG文件
手動編寫VectorDrawable是非常困難的,大部分時候得到VectorDrawable的方式應該都是先使用矢量繪圖軟件生成SVG圖片, 再通過一些工具將SVG轉化為VectorDrawable.下面是兩個比較推薦的:
- Android Studio 內置的Vector Asset Studio
- svg2android :一個網站直接上傳你的svg圖片就能生成Vectordrawable.
補充更新:第一種方式生成的svg文件不是標準的svg語法,是擴展后的 ,加入了一些其他的標簽,如s 等 ,這樣在解析是會有問題 。
那么svg格式的圖片怎么得到 ?
可以自己去學學 AI 或者 GIMP 等軟件的使用方法, 用它們來制作圖形然后導成 SVG ,當然這樣的話學習成本有點高——不過沒關系,我們還有低配版的實現方式:Method Draw。這是一個在線制作矢量圖的網站,可以很方便的將在上面制作的圖形導出成 SVG 文件,學習成本相當低,而且能完成我們大部分的需求,總之我覺得還挺好用的。
又發現一個PNG 圖片轉SVG 的網站:
http://vectormagic.com/home : 免費次數有限
http://image.online-convert.com/convert-to-svg :也是一個可以轉換的網站
2.2 如何使用
2.2.1 簡單使用
從Lollipop(Android 5.0)開始, Android引入了對矢量圖的支持, 但并不支持svg這種矢量圖片格式, 而是以VectorDrawable的方式來實現矢量圖的效果。
VectorDrawable與其它Drawable一樣可以被用作View的背景, ImageView的res.
上圖是直接在布局文件中直接引用 SVG 的 XML 文件,代碼中的 ic_android_black_24dp就是已經寫好的 SVG 文件。可以看到,直接把它當做一張普通的圖片來使用就可以了。當然,我們也可以在 Java 代碼里面來使用 SVG 文件,像下面這樣:
mImageView.setImageDrawable(getDrawable(R.drawable.ic_android_black_24dp));
2.2.2 實現動畫效果
先看下效果
從動畫效果上來看,如果我們自己來實現,肯定非常麻煩,幸運的是有人給我們造好這個輪子了,現在我們只要有一個svg格式的圖片, 就可以顯示炫酷的動畫了。這個過程涉及兩個步驟,一個是SVG的解析,一個是解析后的繪制 。
github 項目地址: https://github.com/geftimov/android-pathview ,數據源可以是svg圖片,或者Path類
后來又發現一個庫:https://github.com/mcxtzhang/PathAnimView , 也可以把svg 文件實現動畫效果 。
只要我們有一個SVG文件, 就可以實現動畫效果 。
這個開源庫的用法簡單介紹一下
首先引入
dependencies {
compile 'com.eftimoff:android-pathview:1.0.8@aar'
}
下一步在布局文件中, 引入控件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorAccent"
android:orientation="vertical"
tools:context="demo.com.svddemo.MainActivity">
<com.eftimoff.androipathview.PathView
android:id="@+id/pathView"
android:layout_width="350dp"
android:layout_height="350dp"
app:svg="@raw/monitor"
app:pathColor="@android:color/white"
app:pathWidth="2dp"/>
</LinearLayout>
最后在activity 中
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final PathView pathView = (PathView) findViewById(R.id.pathView);
// final Path path = makeConvexArrow(50, 100);
// pathView.setPath(path);
pathView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
pathView.getPathAnimator().
delay(100).
duration(1500).
interpolator(new AccelerateDecelerateInterpolator()).
start();
}
});
}
private Path makeConvexArrow(float length, float height) {
final Path path = new Path();
path.moveTo(0.0f, 0.0f);
path.lineTo(length / 4f, 0.0f);
path.lineTo(length, height / 2.0f);
path.lineTo(length / 4f, height);
path.lineTo(0.0f, height);
path.lineTo(length * 3f / 4f, height / 2f);
path.lineTo(0.0f, 0.0f);
path.close();
return path;
}
}
看到注釋的部分,調用了makeConvexArraw()方法,如果我們沒有在xml文件里面指定svg文件,我們也可以在代碼中手動指定繪制的路徑
2.3 向下兼容
上面說了VectorDrawable是Android 5.0之后才增加的, 那如何作兼容呢? 實際上這里有兩種方式:
- 不需要添加任何依賴包, Gradle在編譯時會自動生成Vectordrawable對應的位圖資源(如果你支持的最低sdk小于api21, 若大于等于21就不存在兼容性問題了)
- 使用Support Library 23.2+(不會自動生成位圖)
方式1
會在打包APK時自動生成對應的位圖資源, 在低版本的手機上運行時會自動引用位圖, 在5.0及以后的手機上運行時會自動引用Vectordrawable. 這種方式的優點是不需要開發者去顯式的做任何設置, 缺點是同時打包了位圖與矢量圖資源APK包會變大. 生成哪種分辨率下的位圖資源可以通過下面的Gradle配置指定:
defaultConfig {
vectorDrawables.generatedDensities = ['hdpi','xxhdpi']
}
方式2
不會自動生成位圖, 在最終的APK中只存在一份VectorDrawable, 所以不會存在APK包增大的問題, 但需要在使用時作一些調整:
首先需要在你的build.gradle配置文件中增加如下配置:
android {
defaultConfig {
vectorDrawables.useSupportLibrary = true
}
}
上面的配置的作用是強制gradle在編譯時不自動生成兼容低版本的位圖資源.
然后在引用Vectordrawable資源時使用app:srcCompat取代android:src, 若你還想將此Vectordrawable資源用作View的背景,遺憾的是這里沒有一個類似的app:backgroundCompat方法, 你只能通過代碼來設置
Resources resources = context.getResources(Resources, int, Theme);
Theme theme = context.getTheme();
Drawable drawable = VectorDrawableCompat.create(resources, R.drawable.vector_drawable, theme);
view.setBackground(drawable);
今年初Google發布了Android Support Library 23.2其中主要增加了對VectorDrawable與AnimateVectorDrawable的支持.VectorDrawable可以被兼容到Android2.1, AnimateVectorDrawable可以被兼容到Android3.0
注意
- 并不是所有的圖片都適合使用VectorDrawable
A vector drawable is appropriate for simple icons. The material icons provide good examples of the types of images that work well as vector drawables in an app. In contrast, many app launch icons do have many details, so they work better as raster images.
上面是官方的原話, 大概意思就是說VectorDrawable只適合于像material icons這樣的簡單圖片, 并不適合細節過于復雜的圖片(原因也是很好理解的, 基于矢量圖的原理, 它難以反應色彩層次豐富的圖像效果)。
- VectorDrawable只支持部分的SVG特性
所以你在將SVG轉化為VectorDrawable時, 需要注意一下, 不支持的部分特性被丟失后生成的VectorDrawable你是否能接受.
至此, SVG圖片的相關知識就整理完了, 主要是整理了svg圖片基礎介紹 、基本用法及如何實現動畫效果 。
遺留問題
使用幾個在線轉換的網站 ,生成的svg圖片, 在android-pathview 這個開源庫下顯示有問題,在解析svg文件時 有語法錯誤 ,不知道為什么 。
com.caverock.androidsvg.SVGParseException: SVG parse error: At line 2, column 51: syntax error
at com.caverock.androidsvg.SVGParser.parse(SVGParser.java:611)
at com.caverock.androidsvg.SVG.getFromResource(SVG.java:187)
at com.caverock.androidsvg.SVG.getFromResource(SVG.java:172)
at com.eftimoff.androipathview.SvgUtils.load(SvgUtils.java:60)
at com.eftimoff.androipathview.PathView$1.run(PathView.java:242)
at java.lang.Thread.run(Thread.java:818)
Caused by: org.apache.harmony.xml.ExpatParser$ParseException: At line 2, column 51: syntax error
at org.apache.harmony.xml.ExpatParser.parseFragment(ExpatParser.java:515)
at org.apache.harmony.xml.ExpatParser.parseDocument(ExpatParser.java:474)
at org.apache.harmony.xml.ExpatReader.parse(ExpatReader.java:316)
at org.apache.harmony.xml.ExpatReader.parse(ExpatReader.java:279)
at com.caverock.androidsvg.SVGParser.parse(SVGParser.java:599)
at com.caverock.androidsvg.SVG.getFromResource(SVG.java:187)
at com.caverock.androidsvg.SVG.getFromResource(SVG.java:172)
at com.eftimoff.androipathview.SvgUtils.load(SvgUtils.java:60)
at com.eftimoff.androipathview.PathView$1.run(PathView.java:242)
at java.lang.Thread.run(Thread.java:818)
參考文檔
Android實現炫酷SVG動畫效果
擁抱SVG:苦惱于圖片適配 in Android?
Android vector標簽 PathData 畫圖超詳解
【注釋張豪華版 Path酷炫動畫】極速get花式Path (支付寶支付成功動畫)
vectormagic : PNG 圖片轉SVG圖片的網站, 但是免費試用的次數有限 。