Android樣式的開發:drawable彙總篇

Android有很多種drawable類型,除了前幾篇詳細講解的shapeselectorlayer-list,還有上一篇提到的color、bitmap、clip、scale、inset、transition、rotate、animated-rotate、lever-list等等,本篇文章將彙總介紹所有剩下的drawable資源。

普通圖片

圖片是最常用的drawable資源,格式包括:png(推薦)、jpg(可接受)、gif(不建議)。用圖片資源需要根據不同屏幕密度提供多張不同尺寸的圖片,它們的關係如下表:

密度分類 密度值範圍 代表分辨率 圖標尺寸 圖片比例
mdpi 120~160dpi 320x480px 48x48px 1
hdpi 160~240dpi 480x800px 72x72px 1.5
xhdpi 240~320dpi 720x1280px 96x96px 2
xxhdpi 320~480dpi 1080x1920px 144x144px 3
xxxhdpi 480~640dpi 1440x2560px 192x192px 4

本來還有一個ldpi的,但現在這種小屏幕的設備基本滅絕了,所以不需要再考慮適配。如上表所示,一套圖片一般需要提供5張不同比例的圖片。還好有切圖工具,可以讓切圖變得簡單,這裏推薦兩款:Cutterman和Cut&Slice me,都是Photoshop下的插件,輸出支持android、ios和web三種平臺。
使用切圖工具雖然方便了,但還是無法避免一套圖片需要提供多張不同尺寸的圖片,這會加大安裝包的大小。另外,需要對圖片做改動時,比如換個顏色,必須更換所有尺寸圖片。所以,建議儘量減少引入圖片,而通過使用shape、layer-list等自己畫,易於修改和維護,也減少了安裝包大小,適配性也更好。

bitmap標籤

可以通過bitmap標籤對圖片做一些設置,如平鋪、拉伸或保持圖片原始大小,也可以指定對齊方式。看看bitmap標籤的一些屬性吧:

  • android:src 必填項,指定圖片資源,只能是圖片,不能是xml定義的drawable資源
  • android:gravity 設置圖片的對齊方式,比如在layer-list中,默認會盡量填滿整個視圖,導致圖片可能會被拉伸,爲了避免被拉伸,就可以設置對齊方式,可取值爲下面的值,多個取值可以用 | 分隔:

    • top 圖片放於容器頂部,不改變圖片大小
    • bottom 圖片放於容器底部,不改變圖片大小
    • left 圖片放於容器左邊,不改變圖片大小
    • right 圖片放於容器右邊,不改變圖片大小
    • center 圖片放於容器中心位置,包括水平和垂直方向,不改變圖片大小
    • fill 拉伸整張圖片以填滿容器的整個高度和寬度,默認值
    • center_vertical 圖片放於容器垂直方向的中心位置,不改變圖片大小
    • center_horizontal 圖片放於容器水平方向的中心位置,不改變圖片大小
    • fill_vertical 在垂直方向上拉伸圖片以填滿容器的整個高度
    • fill_horizontal 在水平方向上拉伸圖片以填滿容器的整個寬度
    • clip_vertical 附加選項,裁剪基於垂直方向的gravity設置,設置top時會裁剪底部,設置bottom時會裁剪頂部,其他情況會同時裁剪頂部和底部
    • clip_horizontal 附加選項,裁剪基於水平方向的gravity設置,設置left時會裁剪右側,設置right時會裁剪左側,其他情況會同時裁剪左右兩側
  • android:antialias 設置是否開啓抗鋸齒

  • android:dither 設置是否抖動,圖片與屏幕的像素配置不同時會用到,比如圖片是ARGB 8888的,而屏幕是RGB565

  • android:filter 設置是否允許對圖片進行濾波,對圖片進行收縮或者延展使用濾波可以獲得平滑的外觀效果

  • android:tint 給圖片着色,比如圖片本來是黑色的,着色後可以變成白色

  • android:tileMode 設置圖片平鋪的方式,取值爲下面四種之一:

    • disable 不做任何平鋪,默認設置
    • repeat 圖片重複鋪滿
    • mirror 使用交替鏡像的方式重複圖片的繪製
    • clamp 複製圖片邊緣的顏色來填充容器剩下的空白部分,比如引入的圖片如果是白色的邊緣,那麼圖片所在的容器裏除了圖片,剩下的空間都會被填充成白色
  • android:alpha 設置圖片的透明度,取值範圍爲0.0~1.0之間,0.0爲全透明,1.0爲全不透明,API Level最低要求是11,即Android 3.0

  • android:mipMap 設置是否可以使用mipmap,但API Level最低要求是17,即Android 4.2

  • android:autoMirrored 設置圖片是否需要鏡像反轉,當佈局方向是RTL,即從右到左佈局時纔有用,API Level 19(Android 4.4)才添加的屬性

  • android:tileModeX 和tileMode一樣設置圖片的平鋪方式,只是這個屬性只設置水平方向的平鋪方式,這是API Level 21(Android 5.0)才添加的屬性

  • android:tileModeY 和tileMode一樣設置圖片的平鋪方式,只是這個屬性只設置垂直方向的平鋪方式,這是API Level 21(Android 5.0)才添加的屬性

  • android:tintMode 着色模式,也是API Level 21(Android 5.0)才添加的屬性

點九圖片

點九圖片文件擴展名爲:.9.png,通過點九圖片可以做局部拉伸,比如,一張圓角矩形圖片,我們不想讓它的四個邊角都被拉伸從而導致模糊失真,使用點九圖就可以控制拉伸區域,讓四個邊角保持完美顯示。效果如下圖:

畫點九圖一般用Android SDK工具集裏的draw9patch工具,只需要在四條邊畫黑線就可以了,如下圖所示:

拉伸區域就是圖片會被拉伸的部分,可以爲1個點,也可以爲一條線,甚至也可以爲斷開的幾個點或幾條線,總之,有黑點的地方就會被拉伸,沒有黑點的地方就不會被拉伸。而顯示內容區域其實就等於默認給使用的控件設置了padding,控件的內容只能顯示在內容區域內。

nine-patch標籤

使用nine-patch標籤可以對點九圖片做一些設置處理,不過可設置的屬性並不多:

  • android:src 必填項,必須指定點九類型的圖片
  • android:dither 設置是否抖動,圖片與屏幕的像素配置不同時會用到,比如圖片是ARGB 8888的,而屏幕是RGB565
  • android:tint 給圖片着色,比如圖片本來是黑色的,着色後可以變成白色
  • android:tintMode 着色模式,API Level 21(Android 5.0)才添加的屬性
  • android:alpha 設置圖片的透明度,取值範圍爲0.0~1.0之間,0.0爲全透明,1.0爲全不透明,API Level最低要求是11
  • android:autoMirrored 設置圖片是否需要鏡像反轉,當佈局方向是RTL,即從右到左佈局時纔有用,API Level 19(Android 4.4)才添加的屬性

color標籤

color標籤是drawable裏最簡單的標籤了,只有一個屬性:android:color,指定顏色值。這個標籤一般很少用,因爲基本都可以通過其他更方便的方式定義顏色。另外,顏色值一般都在colors.xml文件中定義,其根節點爲resources。看看兩者的不同:

<!-- 文件:res/drawable/white.xml -->
<color xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="#FFFFFF" />
<!-- 文件:res/values/colors.xml -->
<resources>
    <color name="white">#FFFFFF</color>
</resources>

引用的時候,前一種通過@drawable/white引用,後一種通過@color/white引用。

inset標籤

使用inset標籤可以對drawable設置邊距,其用法和View的padding類似,只不過padding是設置內容與邊界的距離,而inset則可以設置背景drawable與View邊界的距離。inset標籤的可設置屬性如下:

  • android:drawable 指定drawable資源,如果不設置該屬性,也可以定義drawable類型的子標籤
  • android:visible 設置初始的可見性狀態,默認爲false
  • android:insetLeft 左邊距
  • android:insetRight 右邊距
  • android:insetTop 頂部邊距
  • android:insetBottom 底部邊距
  • android:inset 設置統一邊距,會覆蓋上面四個屬性,但API Level要求爲21,即Android 5.0

clip標籤

使用clip標籤可以對drawable進行裁剪,在做進度條時很有用。通過設置level值控制裁剪多少,level取值範圍爲0~10000,默認爲0,表示完全裁剪,圖片將不可見;10000則完全不裁剪,可見完整圖片。看看clip標籤可以設置的屬性:

  • android:drawable 指定drawable資源,如果不設置該屬性,也可以定義drawable類型的子標籤
  • android:clipOrientation 設置裁剪的方向,取值爲以下兩個值之一:

    • horizontal 在水平方向上進行裁剪,條狀的進度條就是水平方向的裁剪
    • vertical 在垂直方向上進行裁剪
  • android:gravity 設置裁剪的位置,可取值如下,多個取值用 | 分隔:

    • top 圖片放於容器頂部,不改變圖片大小。當裁剪方向爲vertical時,會裁掉圖片底部
    • bottom 圖片放於容器底部,不改變圖片大小。當裁剪方向爲vertical時,會裁掉圖片頂部
    • left 圖片放於容器左邊,不改變圖片大小,默認值。當裁剪方向爲horizontal,會裁掉圖片右邊部分
    • right 圖片放於容器右邊,不改變圖片大小。當裁剪方向爲horizontal,會裁掉圖片左邊部分
    • center 圖片放於容器中心位置,包括水平和垂直方向,不改變圖片大小。當裁剪方向爲horizontal時,會裁掉圖片左右部分;當裁剪方向爲vertical時,會裁掉圖片上下部分
    • fill 拉伸整張圖片以填滿容器的整個高度和寬度。這時候圖片不會被裁剪,除非level設爲了0,此時圖片不可見
    • center_vertical 圖片放於容器垂直方向的中心位置,不改變圖片大小。裁剪和center時一樣
    • center_horizontal 圖片放於容器水平方向的中心位置,不改變圖片大小。裁剪和center時一樣
    • fill_vertical 在垂直方向上拉伸圖片以填滿容器的整個高度。當裁剪方向爲vertical時,圖片不會被裁剪,除非level設爲了0,此時圖片不可見
    • fill_horizontal 在水平方向上拉伸圖片以填滿容器的整個寬度。當裁剪方向爲horizontal時,圖片不會被裁剪,除非level設爲了0,此時圖片不可見
    • clip_vertical 附加選項,裁剪基於垂直方向的gravity設置,設置top時會裁剪底部,設置bottom時會裁剪頂部,其他情況會同時裁剪頂部和底部
    • clip_horizontal 附加選項,裁剪基於水平方向的gravity設置,設置left時會裁剪右側,設置right時會裁剪左側,其他情況會同時裁剪左右兩側

那怎麼設置level呢?android沒有提供直接在xml裏設置level的屬性,這需要通過代碼去設置。舉例用法如下:

  1. 定義clip.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <clip xmlns:android="http://schemas.android.com/apk/res/android"
        android:clipOrientation="horizontal"
        android:drawable="@drawable/img4clip"
        android:gravity="left" />
    
  2. 在ImageView中引用:

    <ImageView
        android:id="@+id/img"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/bg_img"
        android:src="@drawable/clip" />
    
  3. 在代碼中設置level:

    ImageView img =  (ImageView) findViewById(R.id.img);
    img.getDrawable().setLevel(5000); //level範圍值0~10000 
    

scale標籤

使用scale標籤可以對drawable進行縮放操作,和clip一樣是通過設置level來控制縮放的比例。scale標籤可以設置的屬性如下:

  • android:drawable 指定drawable資源,如果不設置該屬性,也可以定義drawable類型的子標籤
  • android:scaleHeight 設置可縮放的高度,用百分比表示,格式爲XX%,0%表示不做任何縮放,50%表示只能縮放一半
  • android:scaleWidth 設置可縮放的寬度,用百分比表示,格式爲XX%,0%表示不做任何縮放,50%表示只能縮放一半
  • android:scaleGravity 設置drawable縮放後的位置,取值和bitmap標籤的一樣,就不一一列舉說明了,不過默認值是left
  • android:useIntrinsicSizeAsMinimum 設置drawable原有尺寸作爲最小尺寸,設爲true時,縮放基本無效,API Level最低要求爲11

使用的時候,和clip一樣,用法如下:

  1. 定義scale.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <scale xmlns:android="http://schemas.android.com/apk/res/android"
        android:drawable="@drawable/img4scale"
        android:scaleGravity="left"
        android:scaleHeight="50%"
        android:scaleWidth="50%"
        android:useIntrinsicSizeAsMinimum="false" />
    
  2. 在ImageView中引用:

    <ImageView
        android:id="@+id/img"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/bg_img"
        android:src="@drawable/scale" />
    
  3. 在代碼中設置level:

    ImageView img =  (ImageView) findViewById(R.id.img);
    img.getDrawable().setLevel(5000); //level範圍值0~10000 
    

level-list標籤

當需要在一個View中顯示不同圖片的時候,比如手機剩餘電量不同時顯示的圖片不同,level-list就可以派上用場了。level-list可以管理一組drawable,每個drawable設置一組level範圍,最終會根據level值選取對應的drawable繪製出來。level-list通過添加item子標籤來添加相應的drawable,其下的item只有三個屬性:

  • android:drawable 指定drawable資源,如果不設置該屬性,也可以定義drawable類型的子標籤
  • android:minLevel 該item的最小level值
  • android:maxLevel 該item的最大level值

以下是示例代碼:

<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:drawable="@drawable/battery_low"
        android:maxLevel="10"
        android:minLevel="0" />
    <item
        android:drawable="@drawable/battery_below_half"
        android:maxLevel="50"
        android:minLevel="10" />
    <item
        android:drawable="@drawable/battery_over_half"
        android:maxLevel="99"
        android:minLevel="50" />
    <item
        android:drawable="@drawable/battery_full"
        android:maxLevel="100"
        android:minLevel="100" />
</level-list>

那麼,當電量剩下10%時則可以設置level值爲10,將會匹配第一張圖片:

img.getDrawable().setLevel(10);

item的匹配規則是從上到下的,當設置的level值與前面的item的level範圍匹配,則採用。一般item的添加按maxLevel從小到大排序下來,此時minLevel可以不用指定也能匹配到。如上面代碼就可以簡化如下:

<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:drawable="@drawable/battery_low"
        android:maxLevel="10" />
    <item
        android:drawable="@drawable/battery_below_half"
        android:maxLevel="50" />
    <item
        android:drawable="@drawable/battery_over_half"
        android:maxLevel="99" />
    <item
        android:drawable="@drawable/battery_full"
        android:maxLevel="100" />
</level-list>

但不能反過來將android:maxLevel="100"的item放在最前面,那樣所有電量都只匹配第一條了。

transition標籤

transition其實是繼承自layer-list的,只是,transition只能管理兩層drawable,另外提供了兩層drawable之間切換的方法,切換時還會有淡入淡出的動畫效果。示例代碼如下:

<?xml version="1.0" encoding="utf-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/on" />
    <item android:drawable="@drawable/off" />
</transition>

transition標籤生成的Drawable對應的類爲TransitionDrawable,要切換時,需要主動調用TransitionDrawable的startTransition()方法,參數爲動畫的毫秒數,也可以調用reverseTransition()方法逆向切換。

((TransitionDrawable)drawable).startTransition(500); //正向切換,即從第一個drawable切換到第二個
((TransitionDrawable)drawable).reverseTransition(500); //逆向切換,即從第二個drawable切換回第一個

rotate標籤

使用rotate標籤可以對一個drawable進行旋轉操作,在shape篇講環形時最後舉了個進度條時就用到了rotate標籤。另外,比如你有一張箭頭向上的圖片,但你還需要一個箭頭向下的圖片,這時就可以使用rotate將向上的箭頭旋轉變成一張箭頭向下的drawable。
先看看rotate標籤的一些屬性吧:

  • android:drawable 指定drawable資源,如果不設置該屬性,也可以定義drawable類型的子標籤
  • android:fromDegrees 起始的角度度數
  • android:toDegrees 結束的角度度數,正數表示順時針,負數表示逆時針
  • android:pivotX 旋轉中心的X座標,浮點數或是百分比。浮點數表示相對於drawable的左邊緣距離單位爲px,如5; 百分比表示相對於drawable的左邊緣距離按百分比計算,如5%; 另一種百分比表示相對於父容器的左邊緣,如5%p; 一般設置爲50%表示在drawable中心
  • android:pivotY 旋轉中心的Y座標
  • android:visible 設置初始的可見性狀態,默認爲false

示例代碼如下,目標是將一張箭頭向上的圖片轉180度,轉成一張箭頭向下的圖片:

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/ic_arrow"
    android:fromDegrees="0"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toDegrees="180" />

將它引用到ImageView裏,發現圖片根本沒有轉變。其實,要讓它可以旋轉,還需要設置level值。level取值範圍爲0~10000,應用到rotate,則與fromDegrees~toDegrees相對應,如上面例子的角度範圍爲0~180,那麼,level取值0時,則旋轉爲0度;level爲10000時,則旋轉180度;level爲5000時,則旋轉90度。因爲level默認值爲0,所以圖片沒有轉變。那麼,我們想轉180度,其實可以將fromDegrees設爲180,而不設置toDegrees,這樣,不用再在代碼裏設置level圖片就可以旋轉180了。

animation-list標籤

通過animation-list可以將一系列drawable構建成幀動畫,就是將一個個drawable,一幀一幀的播放。通過添加item子標籤設置每一幀使用的drawable資源,以及每一幀持續的時間。示例代碼如下:

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">
    <item
        android:drawable="@drawable/anim1"
        android:duration="1000" />
    <item
        android:drawable="@mipmap/anim2"
        android:duration="1000" />
    <item
        android:drawable="@mipmap/anim3"
        android:duration="1000" />
</animation-list>

android:oneshot屬性設置是否循環播放,設爲true時,只播放一輪就結束,設爲false時,則會輪詢播放。
android:duration屬性設置該幀持續的時間,以毫秒數爲單位。
animation-list對應的Drawable類爲AnimationDrawable,要讓動畫運行起來,需要主動調用AnimationDrawable的start()方法。另外,如果在Activity的onCreate()方法裏直接調用start()方法會沒有效果,因爲view還沒有初始化完成是播放不了動畫的。

animated-rotate

rotate標籤只是將原有的drawable轉個角度變成另一個drawable,它是靜態的。而animated-rotate則會讓drawable不停地做旋轉動畫。
animated-rotate可設置的屬性只有四個:

  • android:drawable 指定drawable資源,如果不設置該屬性,也可以定義drawable類型的子標籤
  • android:pivotX 旋轉中心的X座標
  • android:pivotY 旋轉中心的Y座標
  • android:visible 設置初始的可見性狀態,默認爲false

示例代碼:

<?xml version="1.0" encoding="utf-8"?>
<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/img_daisy"
    android:pivotX="50%"
    android:pivotY="50%"
    android:visible="false" />

寫在最後

至此,drawable資源基本都講完了,但還不是全部,Android 5.0新增的幾個標籤:animated-selector、vector、animated-vector、ripple,因爲還沒弄清楚具體的用法,而且也涉及到Material Design,所以不在本篇講解,後續做Material Design專題分享的時候會再詳細講解用法。
PS:selector標籤下的item其實還可以添加set標籤,這是添加動畫集的標籤,下一篇就將分享下一些常用動畫的製作。


原文地址: http://keeganlee.me/post/android/20150916

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章