Android的Drawable
導語
Drawable表示的是一種可以在Canvas上進行繪製的抽象概念,它的種類有很多,最常見的就是顏色和圖片。優點:使用簡單,比自定義View成本低很多,非圖片型別的Drawable佔用空間較小。本章中,首先描述Drawable的層次關係,接著介紹Drawable的分類,最後介紹自定義Drawable相關的知識。
主要內容
- Drawable簡介
- Drawable的分類
- 自定義Drawable
具體內容
Drawable簡介
Drawable有很多種,都表示影象的概念,但不全是圖片,通過顏色也可以構造出各式各樣的影象效果。實際開發中,Drawable常被用來作為View的背景使用。Drawable一般是通過XML來定義的,Drawable是所有Drawable物件的基類。
Drawable的內部寬、高這個引數比較重要,通過getIntrinsicWidth/getIntrinsicHeight這兩個方法獲取。但並不是所有Drawable都有寬高;圖片Drawable的內部寬/高就是圖片的寬/高,但是顏色形成的Drawable並沒有寬/高的概念。
Drawable的分類
常見的有BitmapDrawable、ShapeDrawable、LayerDrawable以及StateListDrawable等。
BitmapDrawable
表示的就是一張圖片,可以直接引用原始圖片即可,也可以通過XML描述它,從而設定更多效果。
<?xml version="1.0" encoding="utf-8"?> <bitmap / nine-patch xmlns:android="http://schemas.android.com/apk/res/android" android:src="@[package:]drawable/drawable_resource" android:antialias=["true" | "false"] android:dither=["true" | "false"] android:filter=["true" | "false"] android:gravity=["top" | "bottom" | "left" | "right" | "center_vertical" | "fill_vertical" | "center_horizontal" | "fill_horizontal" | "center" | "fill" | "clip_vertical" | "clip_horizontal"] android:tileMode=["disabled" | "clamp" | "repeat" | "mirror"] />
屬性分析:
-
android:src
圖片資源id -
android:antialias
是否開啟圖片抗鋸齒功能。開啟後會讓圖片變得平滑,同時也會一定程度上降低圖片的清晰度,建議開啟; -
android:dither
是否開啟抖動效果。當圖片的畫素配置和手機螢幕畫素配置不一致時,開啟這個選項可以讓高質量的圖片在低質量的螢幕上還能保持較好的顯示效果,建議開啟。 -
android:filter
是否開啟過濾效果。當圖片尺寸被拉伸或壓縮時,開啟過濾效果可以保持較好的顯示效果,建議開啟; -
android:gravity
當圖片小於容器的尺寸時,設定此選項可以對圖片進行定位。 -
android:tileMode
平鋪模式,有四種選項[“disabled” | “clamp” | “repeat” | “mirror”]。當開啟平鋪模式後,gravity屬性會被忽略。repeat是指水平和豎直方向上的平鋪效果;mirror是指在水平和豎直方向上的鏡面投影效果;clamp是指圖片四周的畫素會擴充套件到周圍區域,這個比較特別。
NinePatchDrawable:
表示一張.9格式的圖片,它和BitmapDrawable都表示一張圖片。用XML描述的方式也和BitmapDrawable一樣。在bitmap標籤中也可以使用.9圖。
ShapeDrawable
可以理解為通過顏色來構造的圖形,可以是純色或漸變的圖形。
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <corners android:bottomLeftRadius="10dp" android:bottomRightRadius="10dp" android:radius="5dp" android:topLeftRadius="10dp" android:topRightRadius="10dp" /> <gradient android:angle="0" android:centerColor="#cccccc" android:centerX="100" android:centerY="20" android:endColor="#abcdef" android:gradientRadius="100dp" android:startColor="#000000" android:type="linear" android:useLevel="false" /> <solid android:color="#cccccc" /> <stroke android:width="1dp" android:color="#cccccc" android:dashGap="2dp" android:dashWidth="50dp" />
屬性分析:
-
android:shape
表示圖片的形狀,選項:rectangle(矩形)、oval(橢圓)、line(橫線)、ring(圓環)。預設值是矩形,另外line和ring這兩個選項必須通過標籤來指定寬度和顏色,否則看不到效果。
其中,ring有其特殊的5種屬性 -
<corners>
表示shape的四個角的角度(圓角程度)。只適用於矩形shape。其中android:radius是同時為4個角設定相同的角度,優先順序較低,會被topLeftRadius這種具體指定角度的屬性所覆蓋。 -
<gradient>
與<solid>標籤相互排斥的,其中solid表示純色填充,而gradient表示漸變效果;gradient有如下幾個屬性:- android:angle——漸變的角度,預設為0,其值必須是45的倍
- 數,0表示從左往右,90表示從下到上。
- android:centerX 漸變的中心點的橫座標
- android:centerY 漸變的中心點的縱座標;
- android:startColor 漸變的起始色
- android:centerColor 漸變的中間色
- android:endColor 漸變的結束色
- android:gradientRadius 漸變半徑,僅當android:type=”radial”時有效。
- android:type 漸變的型別,有linear(線性漸變)、radial(映象漸變)、swepp(掃描線漸變)三種,預設是線性漸變。
-
<solid>
表示純色填充,通過android:color即可指定shape中填充的顏色。 -
<stroke>
Shape的描邊,有如下屬性:- android:width 描邊的寬度
- android:color 描邊的顏色
- android:dashWidth 組成虛線的線段的寬度
- android:dashGap 組成虛線之間的間距。dashWidth和dashGap有任何一個為0,虛線效果都不能生效。
-
<padding>
表示空白,但不是shape的空白,而是包含它的View的空白。 -
<size>
shape的大小,有兩個屬性:android:width和android:height,分別表示shape的寬高。通過標籤指定寬高後,ShapeDrawable就有固定寬/高了。但是作為view的背景來說,shape還是會被拉伸或者縮小為view的大小。
LayerDrawable
它表示一種層次化的Drawable集合,通過將不同的Drawable放置在不同層後達到一種疊加效果。
<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/res_haimei1" android:bottom="10dp" android:drawable="@mipmap/haimei1" android:left="10dp" android:right="10dp" android:top="10dp" /> <item android:id="@+id/res_icon" android:width="30dp" android:height="30dp" android:drawable="@mipmap/ic_launcher" android:gravity="center" />
StateListDrawable
對應<selector>標籤。它表示Drawable集合,每個Drawable對應View的一種狀態,這樣系統就會根據View的狀態來選擇合適的Drawable。主要用於設定可點選View的背景。
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android" android:constantSize="false" android:dither="true" android:variablePadding="false"> <item android:drawable="@mipmap/ic_launcher" android:state_pressed="true" /> <item android:drawable="@mipmap/haimei1" android:state_pressed="false" />
屬性分析:
-
android:constantSize
StateListDrawable的固有大小是否隨著其狀態的變化而變化,因為不同的Drawable有不同的固有大小。true表示固有大小保持不變,這時它的固有大小是內部所有Drawable的固有大小的最大值。預設值為false。 -
android:dither
是否開啟抖動效果,預設true -
android:variablePadding
StateListDrawable的padding是否隨著狀態變化而變化。true表示變化,false表示padding是內部所有Drawable的padding的最大值。預設為false。
view的常見狀態:
- android:state_pressed:按下狀態,Button按下之後沒有鬆開。
- android:state_focused:View獲取了焦點。
- android:state_selected:使用者選擇了View,如RadioButton。
- android:state_checked:使用者選中了View,適用於CheckBox。
- android:state_enable:View處於可用狀態。
預設的item一般放在最後並且不新增任何狀態,這樣當系統在之前的item無法選擇的時候,就會匹配預設的item,因為item的預設狀態不附帶任何狀態,所以它可以適配任何狀態。
LevelListDrawable
對應<level-list>標籤。同樣表示Drawable集合,集合中的每個Drawable都會有一個等級的概念,根據等級不同來切換對於的Drawable。當它作為View的背景時,可以通過Drawable的setLevel方法來設定不同的等級從而切換具體的Drawable。level的值從0-10000,預設為0。
<?xml version="1.0" encoding="utf-8"?> <level-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:maxLevel="0" android:drawable="@drawable/ic_playmethod_normal" /> <item android:maxLevel="1" android:drawable="@drawable/ic_playmethod_repeat_list" /> <item android:maxLevel="2" android:drawable="@drawable/ic_playmethod_repeat_one" /> <item android:maxLevel="3" android:drawable="@drawable/ic_playmethod_random" /> </level-list>
TransitionDrawable
對應<transition>標籤。用來實現兩個Drawable之間淡入淡出的效果。
<?xml version="1.0" encoding="utf-8"?> <transition xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@mipmap/haimei2" /> <item android:drawable="@mipmap/haimei3" /> </transition>
TransitionDrawable drawable = (TransitionDrawable) imageView.getBackground(); drawable.startTransition(1000);
startTransition和reverseTransition方法實現淡入淡出的效果以及它的逆過程。
InsetDrawable
對應於<inset>標籤。它可以將其他Drawable內嵌到自己當中,並可以在四周留下一定的間距。當一個View希望自己的背景比自己的實際區域小的時候,可以採用InsetDrawable來實現。通過LayerDrawable也可以實現。
<?xml version="1.0" encoding="utf-8"?> <inset xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@mipmap/haimei1" android:insetBottom="10dp" android:insetLeft="10dp" android:insetRight="10dp" android:insetTop="10dp"> <shape android:shape="rectangle"> <solid android:color="#abcdef" /> </shape> </inset>
其中,inset中的shape距離view邊界為10dp。
ScaleDrawable
ScaleDrawable對應於xml檔案中的<scale>標籤,可以根據自己的level將指定的drawable縮放到一定比例。
<?xml version="1.0" encoding="utf-8"?> <scale xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@color/blue" android:level="1" android:scaleGravity="center" android:scaleHeight="20%" android:scaleWidth="20%" />
其中,android:scaleGravity屬性相當於gravity屬性。android:scaleHeight/scaleWidth 表示Drawable的縮放比例。
縮放公式: w -= (int) (w * (10000 - level) * mState.mScaleWidth / 10000)
可見,level越大,Drawable看起來越大;scaleHeight/scaleWidth越大,Drawable看起來越小。注意的是,level設定為0時,Drawable不可見。level不應超過10000。
ClipDrawable
ClipDrawabe對應於<clip>標籤,他可以根據自己當前的等級(level)來裁剪一個Drawable,裁剪方向可以通過Android:clipOrientation和android:gravity兩個屬性共同控制。
<?xml version="1.0" encoding="utf-8"?> <clip xmlns:android="http://schemas.android.com/apk/res/android" android:clipOrientation="vertical\horizontal" android:drawable="@drawable/bitmapdrawable" android:gravity="bottom|top|left|right|center|fill|center_vertical|center_horizontal|fill_vertical|fill_horizontal|clip_vertical|clip_horizontal" />
clipOrientation表示裁剪方向。gravity需要和clipOrientation一起才能發揮作用。
使用步驟:
- 定義ClipDrawable。
- 佈局檔案引用。
- 程式碼控制level。
ImageView imageClip = (ImageView) findViewById(R.id.image_clip); ClipDrawable drawable = (ClipDrawable) imageClip.getDrawable(); drawable.setLevel(5000);
level=0的時候,表示完全裁剪,level=10000的時候表示完全不裁剪,level=5000的時候表示裁剪了一半。即等級越大,裁剪的區域越小。
自定義Drawable
在前面,我們分析了View的工作原理,系統會呼叫Drawable的draw方法繪製view的背景。所以我們可以通過重寫Drawable的draw方法來自定義Drawable。但是,通常我們沒必要自定義Drawable,因為自定義Drawable無法在XML中使用。只有在特殊情況下可以使用自定義Drawable。
ofollow,noindex">圓形自定義Drawable demo,半徑隨著view的變化而變化- draw、setAlpha、setColorFilter和getOpacity這幾個方法都是必須要實現的,其中draw是最重要的。當自定義Drawable有固有大小的時候最好重寫getIntrinsicWidth和getIntrinsicHeight這兩個方法,因為會影響到view的wrap_content佈局。上面的例子中,沒有重寫,其內部大小為-1。
- 內部大小不等於Drawable的實際區域大小,Drawable實際區域的大小可以通過getBounds方法來得到,一般來說它和View的尺寸相同。