1. 程式人生 > >Andrid5.0新特性——SVG(可縮放向量圖)

Andrid5.0新特性——SVG(可縮放向量圖)

SVG一種用於描述影象的標記語言。類似HTML。

SVG嚴格遵從XML語法,並用文字格式的描述性語言來描述影象內容,因此是一種和影象解析度無關的向量圖形格式。

標準制定開發歷史

  • 2001年9月4日,釋出SVG 1.0。
  • 2003年1月4日,釋出SVG 1.1。
  • 2003年1月14日,推出SVG移動子版本:SVG Tiny和SVG Basic。
  • 2008年12月22日,釋出SVG Tiny 1.2。
  • 2011年8月16日,釋出SVG 1.1(第2版),成為W3C目前推薦的標準。
  • W3C目前仍正在研究制定SVG2,目前最新的草稿釋出見

示例

下面是一個紅色三角形的svg示例:

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg width="4cm" height="4cm" viewBox="0 0 400 400" xmlns="http://www.w3.org/2000/svg" version="1.1"> <title>Example triangle01- simple example of a 'path'</title> <desc>A path that draws a triangle</desc
>
<rect x="1" y="1" width="398" height="398" fill="none" stroke="blue" /> <path d="M 100 100 L 300 100 L 200 300 z" fill="red" stroke="blue" stroke-width="3" /> </svg>

效果圖如下:
svg

說明:

  • svg向量圖的根節點就是svg,包含圖片寬高和視口大小等資訊。
  • rect表示一個矩形。
  • path(點選檢視path元素介紹)表示一條路徑,向量圖的形狀就是通過path元素定義的,常見屬性有:
    • M (x y)+:在給定的(x,y)座標開始path;
    • L (x y)+:繪製一條直線從當前點到給定的(x,y)座標;
    • z:閉合當前path。
    • C (x1 y1 x2 y2 x y)+:貝塞爾曲線;

用SVG有什麼好處

  • svg是向量圖,縮放不會失真,點陣圖縮放可能產生失真問題。
  • 螢幕適配方便,一般我們為了適配不同解析度可能會在ldpi、mdpi、hdpi、xhdpi等目錄放對應解析度的jpg/png圖片,現在只需要一個xml檔案,而且xml檔案比jpg/png體積小,有利於縮減apk體積。

SVG相關工具

在Android中使用SVG

在Android中,負責向量圖載入,解析,繪製相關工作的類就是VectorDrawable,通過載入一個基於XML的VectorDrawable檔案,在draw方法中把向量圖繪製到Canvas。在Android中向量圖一般用於圖示和按鈕。

1.這裡使用Method Draw先編輯一個svg格式的向量圖,點選開啟Method Draw,編輯完成後匯出svg格式檔案。

heart.svg

<svg height="400" width="580" xmlns="http://www.w3.org/2000/svg">
    <!-- Created with Method Draw - http://github.com/duopixel/Method-Draw/ -->
    <g stroke="null">
        <title>Layer 1</title>
        <path d="m218.25,65.499992c31.171158,0 61.067017,21.318344 71.75,50.4375c10.683014,-29.119156 40.610474,-50.4375 71.75,-50.4375c40.546692,0 71.75,32.481865 71.75,75.656258c0,59.348114 -60.477264,105.195892 -143.5,193.34375c-83.022751,-88.131226 -143.5,-133.995636 -143.5,-193.34375c0,-43.174393 31.203293,-75.656258 71.75,-75.656258z" fill="#ff0000"
            id="svg_1"
            stroke="#ff0000" stroke-width="1.5" />
    </g>
    <g>
        <title>background</title>
        <rect fill="none" height="402" id="canvas_background" width="582" x="-1" y="-1" />
    </g>
</svg>

2.把svg檔案轉換為Android的VectorDrawable檔案。

  • 手動轉換
    1. 在drawable目錄下新建一個根節點為<vector>的xml檔案。
    2. 把svg檔案中的path資料拷貝到<vector>,比如svg中path的d對應<vector>中path的android:pathData,stroke對應android:strokeColor,stroke-width對應android:strokeWidth等。

  • 使用工具轉換
    使用svg2android把svg檔案轉換為Android Drawable檔案,把匯出檔案放到drawable目錄下。

res/drawable/vector_drawable_heart.xml

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    <!-- 圖片實際寬高 -->
    android:width="580dp"
    android:height="400dp"
    <!-- 畫布的寬高,影象的錨點、座標都是相對於畫布 -->
    android:viewportWidth="580"
    android:viewportHeight="400">

    <!-- draw a path -->
    <path
        android:fillColor="#ff0000"
        android:pathData="M218.25,65.499992c31.171158,0 61.067017,21.318344
71.75,50.4375c10.683014,-29.119156 40.610474,-50.4375 71.75,-50.4375c40.546692,0
71.75,32.481865 71.75,75.656258c0,59.348114 -60.477264,105.195892
-143.5,193.34375c-83.022751,-88.131226 -143.5,-133.995636
-143.5,-193.34375c0,-43.174393 31.203293,-75.656258 71.75,-75.656258z"
        android:strokeColor="#ff0000"
        android:strokeWidth="1.5" />

    <path android:pathData="M -1 -1 H 581 V 401 H -1 V -1 Z" />

</vector>

注意:轉換工具預設把畫布的size作為向量圖的實際寬高,比如一個icon的svg設計時畫布的寬高為480*480,但實際需要的寬高為48*48,所以記得修改<vector>中的android:widthandroid:height為實際需要的寬高,不然就比較耗記憶體。

3.使用向量圖示例

這裡寫圖片描述

<ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/vector_drawable_heart" />

目前這種做法只支援Android5.0+,如果想在Android5.0以下正常執行還要做如下相容處理。

VectorDrawable向下相容

1.在app的build.gradle檔案中新增如下配置,啟用support庫。

android {
    defaultConfig {
        vectorDrawables.useSupportLibrary = true
    }
}

dependencies {
    compile 'com.android.support:appcompat-v7:23.3.0'
}

2.ImageView改為使用support庫的AppCompatImageView,使用app:srcCompat替換android:src

<android.support.v7.widget.AppCompatImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:srcCompat="@drawable/vector_drawable_heart" />

AnimatedVectorDrawable可以讓VectorDrawable加上動畫效果。

VectorDrawable實現動畫效果一般需要3個檔案:

  • VectorDrawable檔案,根節點為<vector>的xml檔案,向量圖。
  • AnimatedVectorDrawable檔案,根節點為<animated-vector>的xml檔案,向量圖動畫定義檔案。
  • 一個或多個屬性動畫檔案。

1.VectorDrawable檔案如下,通過android:name屬性為group和path定義了一個唯一的名字,在AnimatedVectorDrawable檔案中就是通過該名字實現group/path和相關動畫的關聯。

<!--res/drawable/vector_drawable_cpu.xml-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="128dp"
    android:height="128dp"
    android:viewportHeight="600"
    android:viewportWidth="600">

    <group android:name="cpu_box">
        <path
            android:name="cpu"
            android:fillColor="#000000"
            android:pathData="
            M341.087,157.478 c7.417,0,13.435,6.018,13.435,13.435 v170.174c0,7.417-6.018,13.435-13.435,13.435 H170.913 c-7.417,0-13.435-6.018-13.435-13.435V170.913c0-7.417,6.018-13.435,13.435-13.435H341.087z
            M390.348,157.478 c0-19.785-16.041-35.826-35.826-35.826H157.479c-19.785,0-35.826,16.041-35.826,35.826v197.043 c0,19.785,16.041,35.826,35.826,35.826h197.043c19.785,0,35.826-16.041,35.826-35.826V157.478z " />
    </group>
    <group android:name="bottom">
        <path
            android:name="wires_bottom"
            android:fillColor="#000000"
            android:pathData="
        M193.304,408.261V462h-17.913 v-53.739H193.304z
        M264.957,408.261V462h-17.914v-53.739H264.957z
        M300.783,408.261V462h-17.914v-53.739H300.783z
        M229.13,408.261 V462h-17.913v-53.739H229.13z
        M336.609,408.261V462h-17.914v-53.739H336.609z" />
    </group>
    <group android:name="top">
        <path
            android:name="wires_top"
            android:fillColor="#000000"
            android:pathData="
        M193.304,50v53.739h-17.913V50H193.304z
        M264.957,50 v53.739h-17.914V50H264.957z
        M300.783,50v53.739h-17.914V50H300.783z
        M229.13,50v53.739h-17.913V50H229.13z
        M336.609,50v53.739 h-17.914V50H336.609z " />
    </group>
    <group android:name="right">
        <path
            android:name="wires_right"
            android:fillColor="#000000"
            android:pathData="
        M408.261,318.695H462v17.914h-53.739V318.695z
        M408.261,247.043H462v17.914h-53.739V247.043z
        M408.261,211.217 H462v17.913h-53.739V211.217z
        M408.261,282.869H462v17.914h-53.739V282.869z
        M408.261,175.391H462v17.913h-53.739V175.391z" />
    </group>
    <group android:name="left">
        <path
            android:name="wires_left"
            android:fillColor="#000000"
            android:pathData="
        M50,318.695h53.739v17.914H50V318.695z
        M50,247.043h53.739v17.914H50V247.043z
        M50,211.217h53.739v17.913H50V211.217z
        M50,282.869 h53.739v17.914H50V282.869z
        M50,175.391h53.739v17.913H50V175.391z" />
    </group>

</vector>

效果圖如下:

VectorDrawable

2.屬性動畫檔案的編寫,下面是cpu頂部排線的動畫檔案,還有類似的底部和左右排線的動畫檔案,共4個動畫檔案。

<!-- res/animator/pulse_top.xml -->
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:propertyName="translateY"
        android:valueType="floatType"
        android:valueFrom="0"
        android:valueTo="-10"
        android:repeatMode="reverse"
        android:repeatCount="infinite"
        android:duration="250" />
</set>

3.AnimatedVectorDrawable檔案如下,根節點<animated-vector>,通過android:drawable屬性設定使用的向量圖檔案。<target>元素中的android:name對應VectorDrawable檔案中group/path的名稱,android:animation設定group/path使用的動畫。

<!-- res/drawable/animated_cpu.xml -->
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/vector_drawable_cpu">

    <target
        android:name="top"
        android:animation="@animator/pulse_top" />

    <target
        android:name="right"
        android:animation="@animator/pulse_right" />

    <target
        android:name="left"
        android:animation="@animator/pulse_left" />

    <target
        android:name="bottom"
        android:animation="@animator/pulse_bottom" />

</animated-vector>

4.最後,在AppCompatImageView中使用AnimatedVectorDrawable

  • 在xml中使用AnimatedVectorDrawable:
<android.support.v7.widget.AppCompatImageView
        android:id="@+id/image1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        app:srcCompat="@drawable/animated_cpu" />
  • 在程式碼中使用AnimatedVectorDrawable:

使用AnimatedVectorDrawableCompat.create方法載入AnimatedVectorDrawable檔案得到AnimatedVectorDrawable物件。

AnimatedVectorDrawableCompat是support庫中的類,作用是使AnimatedVectorDrawable相容Android5.0以下系統。

    image1 = (AppCompatImageView) findViewById(R.id.image1);
    AnimatedVectorDrawableCompat animatedVectorDrawable = AnimatedVectorDrawableCompat.create(this, R.drawable.animated_cpu);
    image1.setImageDrawable(animatedVectorDrawable);
    addClickAnimationListener(image1);

點選停止/播放動畫

public void addClickAnimationListener(final AppCompatImageView image) {
        image.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Drawable animation = image.getDrawable();
                if (animation instanceof Animatable) {
                    Animatable anim = (Animatable) animation;
                    if (anim.isRunning()) {
                        // 停止動畫
                        anim.stop();
                    } else {
                        // 播放動畫
                        anim.start();
                    }
                }
            }
        });
    }

動畫效果如下:

AnimatedVectorDrawable

注意:目前AnimatedVectorDrawableCompat在Android5.0以下不支援pathData型別屬性動畫。如果需要用到pathData型別屬性動畫可以通過以下方法做相容處理。

  • 使用api識別符號適配不同版本的系統,在drawable和drawable-v21目錄下定義不同的實現。
  • 程式碼中作相容處理
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
    // 在android5.0+下執行下面程式碼
}
  • 使用vector-compat,VectorDrawable和AnimatedVectorDrawable的一個相容庫。

透明度,旋轉,縮放屬性動畫沒有效果?

這裡給VectorDrawable的path新增一個透明度動畫,發現沒有效果,日誌列印如下:

W/PropertyValuesHolder: Method setAlpha() with type int not found on target class class android.support.graphics.drawable.VectorDrawableCompat$VGroup
W/PropertyValuesHolder: Method setRotation() with type float not found on target class class android.support.graphics.drawable.VectorDrawableCompat$VFullPath

日誌中可以看出沒有找到相關的set方法,屬性動畫的實現原理其實是通過java中的反射找到相關set方法去動態修改屬性值實現的。

VectorDrawable(這裡使用的是VectorDrawableCompat,相容Android5.0以下版本)的group對應的類是VectorDrawableCompat$VGroup,path對應的類是VectorDrawableCompat$VFullPath

通過檢視VectorDrawableCompat$VFullPath程式碼發現,VFullPath中透明度屬性的名稱為fillAlpha,於是把android:propertyName值由alpha改為fillAlpha就可以了。

<objectAnimator
        android:duration="1500"
        android:propertyName="fillAlpha"
        android:valueFrom="1"
        android:valueTo="0"
        android:valueType="floatType" />

相關開源專案

vector-compat

VectorDrawable和AnimatedVectorDrawable的一個相容庫,目前支援api 14+。

AndroidSVG

AndroidSVG是Android下的SVG解析、渲染庫。目前幾乎完全支援SVG 1.1的靜態可視元素和SVG 1.2小部分規範(filters除外)。支援API 8+。

支援svg格式向量圖的ImageView

<com.caverock.androidsvg.SVGImageView
    xmlns:svg="http://schemas.android.com/apk/res-auto"
    android:layout_width="100dp"
    android:layout_height="50dp"
    svg:svg="filename.svg"/>

android-pathview

android-pathview是繪製路徑的動畫庫,路徑可來自於svg或者標準Paths,使svg或path具有動畫效果,並且可以改變路徑的顏色、寬度。

vectalign

Android4.4以後AnimatedVectorDrawable可以讓兩個SVG影象無縫過渡(稱為變形動畫),但是這兩個svg影象的path必須引數個數要相等,同時這些引數的型別要匹配(也就是說格式要對齊),如果不對齊會產生異常。簡單的path可以手動修改對齊,但是複雜點的就比較難了。這個工具就是通過命令列的方式將任意兩個svg資源轉換成對齊的模式,而不會改變原始影象的外觀。

素材分享