1. 程式人生 > >Android Drawable Mipmap Vector使用及Vector兼容

Android Drawable Mipmap Vector使用及Vector兼容

比較 targe parameter tab bsp keep activity 編寫 contain

原文地址:http://blog.csdn.net/eclipsexys/article/details/51838119

http://blog.csdn.net/qq_15545283/article/details/51472458

一.谷歌在app中圖標的適配的歷史

在安卓的發展歷程中,由於設備碎片化的原故,谷歌在app中圖標的適配上做出一步又一步的改進,大體有這麽幾個階段:

  1. 首先有了drawable-(m|h|xh|xxh|xxxh)dpi
  2. 自android studio後,又有了mipmap-(m|h|xh|xxh|xxxh)dpi
  3. 隨著Android L的發布,帶來了VectorDrawable,矢量圖的支持

    • 第一種方案大家都很熟悉, 但也是我們頭痛的地方,因為每種icon都需要出幾套不同分辨率,這無形的增加了app的容量,而且也增加了美工和開發人員的工作量,但是我們又不得不去做。
    • 第二種是第一種的升級版, 沒有實質上的區別,但是在縮放上提供了更好的性能和更少的內存占用。關於圖片是該放到drawable文件夾還是mipmap文件夾,可以看 這裏。
      官方建議是:APP的logo放到mipmap中,其他的放到drawable中。看這裏 https://developer.android.com/studio/write/image-asset-studio.html#configure
    • 第三種,矢量圖,先大概解釋下:矢量圖在很久很久以前就已經應用起來了,是一種基於xml的圖像,因為圖片不提供具體的像素,只提供的是繪圖的指令,所以好處是 占用內存非常小,性能高,可以任意縮放而不會失真,但是缺點也很明顯,沒有位圖表達的色彩豐富。
      然而現在app風格越來越扁平, 擬物化已經成了過去,矢量圖成了越來越多人的選擇。But,Android和iOS對於矢量圖的支持還非常弱.

二.Android Vector曲折的兼容之路

1.少說廢話,來看怎麽使用。

1.1 配置參數,在pre-L版本上兼容

Android Studio中配置gradle參數

//在gradle2.0及以上:
android {
  defaultConfig {
  vectorDrawables.useSupportLibrary = true
}}
//在gradle 1.5以前
android {
  defaultConfig {
    // Stops the Gradle plugin’s automatic rasterization of vectors
    generatedDensities = []
  }
  // Flag to tell aapt to keep the attribute ids around
  aaptOptions {
    additionalParameters "--no-version-vectors"
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

當然,最重要的還有添加appcompat的支持:

compile ‘com.android.support:appcompat-v7:23.4.0‘

如果需要在TextView的drawabletop等上邊使用,還需要在對應的Activity中加入這個代碼:

static {
    AppCompatDelegate.setCompatVectorFromSourcesEnabled(true);
}

並且,我們要在普通控件上使用Vector,就必須依附於StateListDrawable,InsetDrawable,LayerDrawable,LevelListDrawable,RotateDrawable,代碼如下:


<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/icon_shopping"/>
</selector>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5
 <TextView
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:layout_marginTop="10dp"
       android:drawableLeft="@drawable/shopping_res"
       android:text="我想要小圖標"/>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

1.2創建vector圖片

  1. 要從一般使用的PNG圖像轉換到SVG圖像,對於設計師來說,並不是一件難事,因為大部分的設計工具(PS、Illustrator等等)都支持導出各種格式的圖像,如PNG、JPG,當然,也包括SVG,因此,設計師可以完全按照原有的方式進行設計,只是最後導出的時候,選擇SVG即可。
  2. 使用SVG的編輯器來進行SVG圖像的編寫,例如http://editor.method.ac/
    技術分享
  3. 利用一些工具,自己轉換一些比較基礎的圖像。找到矢量圖下載網站,比如這個阿裏巴巴UX矢量庫下載SVG圖,利用轉換網站轉換為Android Vector Drawable
    http://inloop.github.io/svg2android/就是一個非常牛逼的網站,可以在線將SVG圖像轉換為Android Vector Drawable
    技術分享
  4. 使用Android Studio

    步驟如下:

    1. 新建:
      技術分享

    2. 點擊Local SVG 選擇路徑,並命名drawable文件名字

    技術分享

    就在drawable目錄生成了如下圖

    技術分享

1.3在控件中使用

有了靜態的Vector圖像,就可以在控件中使用了。

這裏我們使用的都是普通的ImageView,好像並不是AppcomatImageView,這是因為使用了Appcomat(所在的Activity繼承了AppcompatActivity)後,系統會自動把ImageView轉換為AppcomatImageView。
(當然你可以在普通Activity中使用  android.support.v7.widget.AppCompatImageView 來直接使用。)
  • ImageView\ImageButton

對於ImageView這樣的控件,要兼容Vector圖像,只需要將之前的android:src屬性,換成app:srcCompat即可,示例代碼如下所示:

<ImageView
    android:id="@+id/iv"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:srcCompat="@drawable/vector_image"/>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

在代碼中設置的話,代碼如下所示:

ImageView iv = (ImageView) findViewById(R.id.iv);
iv.setImageResource(R.drawable.vector_image);
  • 1
  • 2
  • 1
  • 2
setBackgroundResource也是可以設置Vector的API
  • Button

Button並不能直接使用app:srcCompat來使用Vector圖像,需要通過Selector來進行使用,首先,創建兩個圖像,用於Selector的兩個狀態,代碼如下所示:

selector1.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="24dp"
        android:height="24dp"
        android:viewportHeight="24.0"
        android:viewportWidth="24.0">
    <path
        android:fillColor="#FF000000"
        android:pathData="M14.59,8L12,10.59 9.41,8 8,9.41 10.59,12 8,14.59 9.41,16 12,13.41 14.59,16 16,14.59 13.41,12 16,9.41 14.59,8zM12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
</vector>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

selector2.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="24dp"
        android:height="24dp"
        android:viewportHeight="24.0"
        android:viewportWidth="24.0">
    <path
        android:fillColor="#FF000000"
        android:pathData="M11,15h2v2h-2zM11,7h2v6h-2zM11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
</vector>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

selector.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/selector1" android:state_pressed="true"/>
    <item android:drawable="@drawable/selector2"/>
</selector>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

非常簡單,只是把普通的Selector中的圖像換成了Vector圖像而已,接下來,在Button中使用這個Selector即可:

<Button
    android:id="@+id/btn"
    android:layout_width="70dp"
    android:layout_height="70dp"
    android:background="@drawable/selector"/>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

然後運行,如果你認為可以運行,那就是太天真了,都說了是兼容,怎麽能沒有坑呢,這裏就是一個坑……

這個坑實際上是有歷史淵源的,Google的一位開發者在博客中寫到:

First up, this functionality was originally released in 23.2.0, but then we found some memory usage and Configuration updating issues so we it removed in 23.3.0. In 23.4.0 (technically a fix release) we’ve re-added the same functionality but behind a flag which you need to manually enable.

實際上,他們的這個改動,就影響了類似DrawableContainers(DrawableContainers which reference other drawables resources which contain only a vector resource)這樣的類,它的一個典型,就是Selector(StateListDrawable也是)。這個開發者在文中提到的flag,就是下面的這段代碼,放在Activity的前面就可以了:

static {
    AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

開啟這個flag後,你就可以正常使用Selector這樣的DrawableContainers了。同時,你還開啟了類似android:drawableLeft這樣的compound drawable的使用權限,以及RadioButton的使用權限,以及ImageView’s src屬性。

  • RadioButton

RadioButton的Button同樣可以定義,代碼如下所示:

<RadioButton
    android:layout_width="50dp"
    android:layout_height="50dp"
    android:button="@drawable/selector"/>
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

1.4AnimationVerctorDrawable 使用基礎(動畫效果)

動態Vector(即AnimationVerctorDrawable)才是Android Vector Drawable的精髓所在

動態的Vector需要通過animated-vector標簽來進行實現,它就像一個粘合劑,將控件與Vector圖像粘合在了一起,一個基礎的animated-vector代碼如下所示:

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

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

</animated-vector>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

實際上這裏面只有兩個重點是需要關註的,XXXXX1和XXXXX2。一個具體的示例如下所示:

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

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

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

</animated-vector>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

這裏表示目標圖像是drawable/ic_arrow,對left、right分別使用了anim_left、anim_right動畫。這裏的name屬性,就是在靜態Vector圖像中group或者path標簽的name屬性。

animated-vector標簽在現在的Android Studio中實際上是會報錯的,但這個並不影響編譯和運行,屬於Android Studio的Bug。

目標圖像

XXXXX1是目標Vector圖像,也就是靜態的Vector圖像,例如:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="120dp"
        android:height="120dp"
        android:viewportHeight="24.0"
        android:viewportWidth="24.0">

    <group android:name="left">
        <path
            android:fillColor="#FF000000"
            android:pathData="M9.01,14L2,14v2h7.01v3L13,15l-3.99,-4v3"/>
    </group>

    <group android:name="right">
        <path
            android:fillColor="#FF000000"
            android:pathData="M14.99,13v-3L22,10L22,8h-7.01L14.99,5L11,9l3.99,4"/>
    </group>

</vector>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

可以發現,這裏的Vector圖像比之前我們看見的要多了一個group標簽。group標簽的作用有兩個:

對Path進行分組,由於我們後面需要針對Path進行動畫,所以可以讓具有同樣動畫效果的Path在同一個Group中
拓展動畫效果,單個的path標簽是沒有translateX和translateY屬性的,因此無法使用屬性動畫來控制path translateY,而group標簽是有的,所以我們需要先將相關的path標簽元素包裹在一個個的group標簽中.

動畫效果

XXXXX2實際上就是模板要實現的動畫,動畫效果實際上就是基礎的屬性動畫,例如:

anim_left.xml

<objectAnimator
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:interpolator="@android:interpolator/anticipate_overshoot"
    android:propertyName="translateX"
    android:repeatCount="infinite"
    android:repeatMode="reverse"
    android:valueFrom="0"
    android:valueTo="-10"
    android:valueType="floatType"/>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

anim_right.xml

<objectAnimator
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:interpolator="@android:interpolator/anticipate_overshoot"
    android:propertyName="translateX"
    android:repeatCount="infinite"
    android:repeatMode="reverse"
    android:valueFrom="0"
    android:valueTo="10"
    android:valueType="floatType"/>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在代碼中使用

ImageView imageView = (ImageView) findViewById(R.id.iv);
AnimatedVectorDrawableCompat animatedVectorDrawableCompat = AnimatedVectorDrawableCompat.create(
        this, R.drawable.square_anim
);
imageView.setImageDrawable(animatedVectorDrawableCompat);
((Animatable) imageView.getDrawable()).start();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

動態Vector兼容性問題

向下兼容問題

一說到兼容,就不得不提到坑,幾乎所有的為了兼容而做的改動,都會留下一些不可填滿的坑,動態Vector動畫也不例外,雖然Google已經對Vector圖像進行了Android 2.1以上的兼容,但對於動態Vector動畫,還是有很多限制的,例如:

Path Morphing,即路徑變換動畫,在Android pre-L版本下是無法使用的。
Path Interpolation,即路徑插值器,在Android pre-L版本只能使用系統的插值器,不能自定義。
Path Animation,即路徑動畫,這個一般使用貝塞爾曲線來代替,所以沒有太大影響。

向上兼容問題

除了在低版本上的兼容性問題,在L版本以上,也存在兼容性問題,即繼承了AppCompatActivity的界面,如果直接設置ImageView的srcCompat,那麽Path Morphing動畫是無法生效的,因為默認的AppCompatActivity已經默認使用ImageViewCompat給轉換了,但是AnimatedVectorDrawableCompat是不支持Path Morphing動畫的,所以,在AppCompatActivity界面裏面就無效了。

解決辦法很簡單,即使用代碼來給ImageView添加動畫:

ImageView imageView = (ImageView) view;
AnimatedVectorDrawable morphing = (AnimatedVectorDrawable) getDrawable(morphing);
imageView.setImageDrawable(morphing);
if (morphing != null) {
    morphing.start();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

註意不要使用AnimatedVectorDrawableCompat即可。

抽取string兼容問題

開發者有時候為了代碼簡潔可能會把Vector圖像中的pathData放到string.xml中,然後在Vector圖像中引用string。

但這種方式如果通過生成png來兼容5.0以下機型的話,會報pathData錯誤,編譯器不會去讀取string.xml,只能把pathData寫到Vector圖像中,動畫文件中也是一樣,這也是為了兼容做出的犧牲嗎,不得而知。

其它兼容問題

其它非常奇怪、詭異、不能理解的兼容性問題,只能通過版本文件夾的方式來進行兼容了,例如drawable-v21和drawable,分別創建兩個文件名相同的資源在兩個文件夾下,這樣在21以上版本,會使用drawable-v21的資源,而其它會使用drawable下的資源。

1.5動態Vector進階

  • 用好ObjectAnimator

所謂Vector動畫進階,實際上就是在利用ObjectAnimator的一些屬性,特別是trimPathStart、trimPathEnd這兩個針對Vector的屬性(要註意pathData屬性不兼容pre-L)。

這兩個屬性的官方文檔如下所示:

android:trimPathStart
The fraction of the path to trim from the start, in the range from 0 to 1.
android:trimPathEnd
The fraction of the path to trim from the end, in the range from 0 to 1.
android:trimPathOffset
Shift trim region (allows showed region to include the start and end), in the range from 0 to 1.

其實很簡單,就是一個圖像的截取,設置一個比例即可,即當前繪制多少比例的圖像,其余部分不繪制,Start和End分別就是從PathData的Start和End開始算,大家參考幾個例子就能理解了。

  • 理解Path Morph

Path Morph動畫是Vector動畫的一個高級使用,說到底,也就是兩個PathData的轉換,但是這種轉換並不是隨心所欲的,對於兩個PathData,它們能進行Path Morph的前提是,它們具有相同個數的關鍵點,即兩個路徑的變換,只是關鍵點的坐標變化,掌握了這一個基本原理,實現Path Morph就非常容易了。

1.6 Vector性能問題

解釋一下。

Bitmap的繪制效率並不一定會比Vector高,它們有一定的平衡點,當Vector比較簡單時,其效率是一定比Bitmap高的,所以,為了保證Vector的高效率,Vector需要更加簡單,PathData更加標準、精簡,當Vector圖像變得非常復雜時,就需要使用Bitmap來代替了
Vector適用於ICON、Button、ImageView的圖標等小的ICON,或者是需要的動畫效果,由於Bitmap在GPU中有緩存功能,而Vector並沒有,所以Vector圖像不能做頻繁的重繪
Vector圖像過於復雜時,不僅僅要註意繪制效率,初始化效率也是需要考慮的重要因素
SVG加載速度會快於PNG,但渲染速度會慢於PNG,畢竟PNG有硬件加速,但平均下來,加載速度的提升彌補了繪制的速度缺陷。

Google的這個視頻中,已經對Vector的效率問題做了解釋,可以參考下:

https://www.youtube.com/watch?v=wlFVIIstKmA&feature=youtu.be&t=6m3s

3.SVG和Vector簡介

1.概念

SVG,即Scalable Vector Graphics 矢量圖,這種圖像格式在前端中已經使用的非常廣泛了,詳見WIKI:https://en.wikipedia.org/wiki/Scalable_Vector_Graphics

Vector,在Android中指的是Vector Drawable,也就是Android中的矢量圖,詳見:https://developer.android.com/reference/android/graphics/drawable/VectorDrawable.html

因此,可以說Vector就是Android中的SVG實現,因為Android中的Vector並不是支持全部的SVG語法,也沒有必要,因為完整的SVG語法是非常復雜的,但已經支持的SVG語法已經夠用了,特別是Path語法,幾乎是Android中Vector的標配,詳細可以參考:http://www.w3.org/TR/SVG/paths.html

2.Vector語法簡介

Android以一種簡化的方式對SVG進行了兼容,這種方式就是通過使用它的Path標簽,通過Path標簽,幾乎可以實現SVG中的其它所有標簽,雖然可能會復雜一點,但這些東西都是可以通過工具來完成的,所以,不用擔心寫起來會很復雜。

Path指令解析如下所示:

支持的指令:
    M = moveto(M X,Y) :將畫筆移動到指定的坐標位置
    L = lineto(L X,Y) :畫直線到指定的坐標位置
    H = horizontal lineto(H X):畫水平線到指定的X坐標位置
    V = vertical lineto(V Y):畫垂直線到指定的Y坐標位置
    C = curveto(C X1,Y1,X2,Y2,ENDX,ENDY):三次貝賽曲線
    S = smooth curveto(S X2,Y2,ENDX,ENDY)
    Q = quadratic Belzier curve(Q X,Y,ENDX,ENDY):二次貝賽曲線
    T = smooth quadratic Belzier curveto(T ENDX,ENDY):映射
    A = elliptical Arc(A RX,RY,XROTATION,FLAG1,FLAG2,X,Y):弧線
    Z = closepath():關閉路徑

使用原則:
    坐標軸為以(0,0)為中心,X軸水平向右,Y軸水平向下
    所有指令大小寫均可。大寫絕對定位,參照全局坐標系;小寫相對定位,參照父容器坐標系
    指令和數據間的空格可以省略
    同一指令出現多次可以只用一個

註意,’M’處理時,只是移動了畫筆, 沒有畫任何東西。 它也可以在後面給出上同時繪制不連續線。

有個圖解:
技術分享

關於這些語法,開發者需要的並不是全部精通,而是能夠看懂即可,其它的都可以交給工具來實現。

3.Vector Drawable相對於普通的Drawable的好處

Android 5.0發布的時候,Google提供了Vector的支持。Vector Drawable相對於普通的Drawable來說,有以下幾個好處:

1. Vector圖像可以自動進行適配,不需要通過分辨率來設置不同的圖片
2. Vector圖像可以大幅減少圖像的體積,同樣一張圖,用Vector來實現,可能只有PNG的幾十分之一
3. 使用簡單,很多設計工具,都可以直接導出SVG圖像,從而轉換成Vector圖像
4. 功能強大,不用寫很多代碼就可以實現非常復雜的動畫
5. 成熟、穩定,前端已經非常廣泛的進行使用了

Vector圖像剛發布的時候,是只支持Android 5.0+的,對於Android pre-L的系統來說,並不能使用,所以,可以說那時候的Vector並沒有什麽卵用。不過自從AppCompat 23.2之後,Google對p-View的Android系統也進行了兼容,也就是說,Vector可以使用於Android 2.1以上的所有系統,只需要引用com.android.support:appcompat-v7:23.2.0以上的版本就可以了,但是有很多坑,如上。

Android Drawable Mipmap Vector使用及Vector兼容