SVG
SVG是Android 5.0中新加入的一個新特性。
從5.0開始android也開始支持SVG圖了,這使得我們可以實現更多的好看的動畫。
什么是SVG?
SVG的全稱是Scalable Vector Graphics,叫可縮放矢量圖形。它和位圖(Bitmap)相對,SVG不會像位圖一樣因為縮放而讓圖片質量下降。下面這個圖片展示了SVG和位圖的區別,左邊的位圖在放大后出現了鋸齒,而右邊的SVG任然清晰。
為什么要使用SVG
- SVG 可被非常多的工具讀取和修改(比如記事本),由於使用xml格式定義,所以可以直接被當作文本文件打開,看里面的數據;
- SVG 與 JPEG 和 GIF 圖像比起來,尺寸更小,且可壓縮性更強,SVG 圖就相當於保存了關鍵的數據點,比如要顯示一個圓,需要知道圓心和半徑,那么SVG 就只保存圓心坐標和半徑數據,而平常我們用的位圖都是以像素點的形式根據圖片大小保存對應個數的像素點,因而SVG尺寸更小;
- SVG 是可伸縮的,平常使用的位圖拉伸會發虛,壓縮會變形,而SVG格式圖片保存數據進行運算展示,不管多大多少,可以不失真顯示;
- SVG 圖像可在任何的分辨率下被高質量地打印;
- SVG 可在圖像質量不下降的情況下被放大;
- SVG 圖像中的文本是可選的,同時也是可搜索的(很適合制作地圖);
- SVG 可以與 Java 技術一起運行;
- SVG 是開放的標准;
- SVG 文件是純粹的 XML;
使用SVG
-
可以用SVG圖片直接轉換成Android可用格式。
我們可以通過上面的變動手動把普通SVG轉成android用的VectorDrawable,或者我們可以用這個網站自動完成轉換 http://inloop.github.io/svg2android/
-
自己使用代碼制作。
用下面的列子來介紹相關的標簽
vector_drawable.xml
<vector xmlns:android="http://schemas.android.com/apk/res/ android"
android:width="200dp"
android:height="200dp"
android:viewportHeight="1000"
android:viewportWidth="1000">
<group
android:name="rotation"
android:pivotX="500.0"
android:pivotY="500.0"
android:rotation="0.0">
<path
android:name="vector"
android:fillColor="#00ffff"
android:pathData="M67,750 L500,0 500,0 933,750 67,750 M146,853 A 0.1,500 0 0 1 146,148 M146,148 A 500,0.1 0 0 1 851,148 M851,148 A 0.1,500 0 0 1 851,853 M851,853 A 500,0.1 0 0 1 146,853" />
</group>
</vector>
注意到和普通SVG圖內容不同地方在與viewBox那變成了 viewportWidth, viewportHeight .
viewportWidth , viewportHeight 就相當於畫這個SVG的畫布大小。
width和height是規定這個SVG圖像最終的顯示大小的,一般用dp表示。
第二個不同是有一個普通SVG里的fill到android里要變成fillColor,這里就是SVG圖像填充的顏色。
第三點不同是,普通SVG的path的數據是d開頭的標簽,在android里要寫成pathData。
綜上所述,只要把viewBox的大小改成viewport的大小,把填充顏色的fill改成fillColor,把Path中的d,改成pathData就行了。
android:name="rotation" 為group 和 path 加上 name 屬性 是為了后面能便於加上動畫。
android:pathData="M67,750 L500,0 500,0 933,750 67,750 M146,853 A 0.1,500 0 0 1 146,148 M146,148 A 500,0.1 0 0 1 851,148 M851,148 A 0.1,500 0 0 1 851,853 M851,853 A 500,0.1 0 0 1 146,853"
M = moveto 相當於 android Path 里的moveTo(),用於移動起始點
L = lineto 相當於 android Path 里的lineTo(),用於畫線
H = horizontal lineto 用於畫水平線
V = vertical lineto 用於畫豎直線
C = curveto 相當於cubicTo(),三次貝塞爾曲線
S = smooth curveto 同樣三次貝塞爾曲線,更平滑
Q = quadratic Belzier curve quadTo(),二次貝塞爾曲線
T = smooth quadratic Belzier curveto 同樣二次貝塞爾曲線,更平滑
A = elliptical Arc 相當於arcTo(),用於畫弧
Z = closepath 相當於closeTo(),關閉path
把制作好的SVG圖片放置在 drawable 目錄下就可以用了。
<ImageView
android:layout_width="250dp"
android:layout_height="300dp"
android:src="@drawable/vector_drawable"
android:id="@+id/iv_main"/>
animated-vector
animated-vector 可以去創建一個矢量資源的動畫。
同樣的,創建出來的 vector_animation.xml 放置在 drawable 下。
animated-vector.xml
<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/vector_drawable" >
<target
android:name="vector"
android:animation="@anim/vector_anim" />
<target
android:name="rotation"
android:animation="@anim/vector_rotation" />
</animated-vector>
android:drawable="@drawable/vector_drawable" 設置矢量資源文件。
target 設置動畫,第一個為 @drawable/vector_drawable 下 name 為 vector 設置 動畫。第二個 同理。
以下是動畫效果文件: (動畫文件放置在 anim 文件目錄下)
vector_anim.xml 這個是制作了一個變形動畫
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="3000"
android:propertyName="pathData"
android:valueFrom="M67,750 L500,0 500,0 933,750 67,750 M146,853 A 0.1,500 0 0 1 146,148 M146,148 A 500,0.1 0 0 1 851,148 M851,148 A 0.1,500 0 0 1 851,853 M851,853 A 500,0.1 0 0 1 146,853"
android:valueTo="M147,853 L147,147 853,147 853,853 147,853 M146,853 A 0.1,500 0 0 1 146,148 M146,148 A 500,0.1 0 0 1 851,148 M851,148 A 0.1,500 0 0 1 851,853 M851,853 A 500,0.1 0 0 1 146,853"
android:valueType="pathType" />
<objectAnimator
android:startOffset="3000"
android:duration="3000"
android:propertyName="pathData"
android:valueFrom="M147,853 L147,147 853,147 853,853 147,853 M147,853 A 0.1,500 0 0 1 147,147 M147,147 A 500,0.1 0 0 1 853,147 M853,147 A 0.1,500 0 0 1 853,853 M851,853 A 500,0.1 0 0 1 147,853"
android:valueTo="M147,853 L147,147 853,147 853,853 147,853 M147,853 A 500,500 0 0 1 147,147 M147,147 A 500,500 0 0 1 853,147 M853,147 A 500,500 0 0 1 853,853 M853,853 A 500,500 0 0 1 147,853"
android:valueType="pathType" />
</set>
vector_rotation.xml 這個是制作了一個旋轉動畫
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="6000"
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="1440" />
</set>
使用該動畫:
<ImageView
android:layout_width="250dp"
android:layout_height="300dp"
android:src="@drawable/vector_animation"
android:id="@+id/iv_main"/>
開啟動畫的代碼:
final ImageView iv = (ImageView) findViewById(R.id.iv_main);
iv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(iv.getDrawable() instanceof Animatable){
//開始動畫
((Animatable) iv.getDrawable()).start();
}
}
});
效果如下:
Lottie庫播放動畫
Android 5.x 之后提供了對 SVG 的支持,通過 VectorDrawable、AnimatedVectorDrawable 的結合可以實現一些稍微復雜的動畫,但是兼容性是一個問題,不過整個實現流程非常麻煩,每次全新實現一個動畫都得重頭來過,最最關鍵的是,如果一個公司下的 App,iOS 也要實現一套一樣的動畫,資源的占用就顯得過大了。
而 Airbnb 開源的這個項目完美的解決以上難題
假設我們要做一個引導頁面的歡迎動畫,這個一般設計師會用 Adobe 旗下的 After Effects (簡稱 AE)來做個動畫出來,設計師用 AE 做個動畫比工程師用代碼去實現一個動畫要快的多的多,調整起來也很方便,之后 AE 上有一款插件叫做 Bodymovin,這個插件也比較屌,可以直接根據 AE 上的動畫文件導出 json 文件,這個 json 文件描述了該動畫的一些關鍵點的坐標以及運動軌跡,之后我們在項目中引用 Lottie 開源庫,在布局文件中簡單的加上這么一句就完美的實現了。
在項目的build.gradle文件中加入:
dependencies {
compile 'com.airbnb.android:lottie:1.0.1'
...
}
使用Lottie
Lottie 支持Jellybean (API 16)及以上的系統,使用這個Lottie應該把 xxxxx.json 文件放在 app/src/main/assets 目錄下。
以下例子中把O.json文件放在規定的目錄下。
最簡單的使用方式是直接在布局文件中添加:
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/animation_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:lottie_fileName="O.json"
app:lottie_loop="true"
app:lottie_autoPlay="true" />
或者
布局文件中:
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/animation_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
-----------------------------------------------------
代碼中:
LottieAnimationView animationView = (LottieAnimationView) findViewById(R.id.animation_view);
animationView.setAnimation("O.json");
//設置是否循環播放
animationView.loop(true);
//播放動畫
animationView.playAnimation();
注意的是:這方法將在后台線程異步加載數據文件,並在加載完成后開始渲染顯示動畫.
效果如圖:
可以通過API控制動畫,並且設置一些監聽
animationView.addAnimatorUpdateListener((animation) -> {
// Do something.
});
也可以使用以下方法進行對動畫的設置:
btnStop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//animationView.isAnimating()動畫是否在播放
if (animationView.isAnimating()) {
//動畫停止
animationView.cancelAnimation();
}
}
});
在使用遮罩的情況下,LottieAnimationView 使用 LottieDrawable來渲染動畫.如果需要的話,你可以直接使用drawable形式:
final LottieDrawable drawable = new LottieDrawable();
LottieComposition.fromAssetFileName(this, "O.json", new LottieComposition.OnCompositionLoadedListener() {
@Override
public void onCompositionLoaded(LottieComposition composition) {
drawable.setComposition(composition);
drawable.loop(true);
drawable.playAnimation();
}
});
如果你需要頻發使用某一個動畫,可以使用LottieAnimationView內置的一個緩存策略:
animationView.setAnimation("O.json", LottieAnimationView.CacheStrategy.Strong);
public enum CacheStrategy {
None,
Weak,
Strong
}
其中CacheStrategy的值可以是Strong,Weak或者None,它們用來決定LottieAnimationView對已經加載並轉換好的動畫持有怎樣形式的引用(強引用/弱引用).