【Android Drawable系列】- shape xml屬性詳解

Android Drawable系列
日常開發中,一些簡單的背景或者圖形都會使用xml的 shape
標籤完成,經常使用在按鈕的背景上。
shape
的優點還是很多的
- 檔案比切圖小
- 節約記憶體
- 支援拉伸
shape
的屬性雖然比較簡單,但是也能繪製出一些比較複雜的形狀
概覽
首先來看看 shape
標籤所支援的標籤以及屬性,如下圖:

Shape
左側是 shape
標籤的自身屬性,右側是 shape
標籤所支援的標籤和標籤的屬性。
下面是一份xml屬性例項和說明
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:dither=["true" | "false"]//將在點陣圖的畫素配置與螢幕不同時(例如:ARGB 8888 點陣圖和 RGB 565 螢幕)啟用點陣圖的抖動;值為“false”時則停用抖動。預設值為 true。 android:shape=["rectangle" | "oval" | "line" | "ring"]//分別為矩形、橢圓、線、環。預設為矩形rectangle android:innerRadius="integer"// shape為ring時有效,內環半徑 android:innerRadiusRatio="float"// shape為ring時有效,內環的厚度比,即環的圖形寬度與內環半徑的比例,按照這個比例計算內環半徑,預設為3,可被innerRadius值覆蓋 android:thickness="integer"// shape為ring時有效,環的厚度 android:thicknessRatio="float"// shape為ring時有效,環的厚度比,即環的圖形寬度與環的厚度的比例,按照這個比例計算環的厚度,預設為9,可被thickness值覆蓋 android:tint="color"// 給shape著色 android:tintMode=["src_in" | "src_atop" | "src_over" | "add" | "multiply" | "screen"] // 著色型別 android:useLevel=["true" | "false"]// 較少用,一般設為false,否則圖形不顯示。為true時可在LevelListDrawable使用 android:visible=["true" | "false"] > <!-- 圓角 --> <corners android:radius="integer"// 圓角半徑,設定下面四個屬性時,對應的位置屬性會被覆蓋 android:topLeftRadius="integer"// 左上角圓角半徑 android:topRightRadius="integer"// 右上角圓角半徑 android:bottomLeftRadius="integer"// 左下角圓角半徑 android:bottomRightRadius="integer" // 右下角圓角半徑 /> <!-- 漸變 --> <gradient android:type=["linear" | "radial" | "sweep"]// 漸變型別,線性、放射性、掃描性;預設為線性 android:angle="integer"// 漸變角度,漸變型別為linear時有效;預設為0,從左至右漸變,角度逆時針方向計算,角度需要時45的整數倍數 android:centerColor="integer"// 漸變中間位置顏色 android:startColor="color"// 漸變開始位置顏色 android:endColor="color"// 漸變結束位置顏色 android:centerX="float"// 設定漸變中心的X座標,取值區間[0,1],預設為0.5,即中心位置 android:centerY="float"// 設定漸變中心的Y座標,取值區間[0,1],預設為0.5,即中心位置 android:gradientRadius="integer"// type為放射性漸變radial時有效,漸變的半徑 android:useLevel=["true" | "false"] // 與shape中該屬性的一致 /> <!-- 內邊距 --> <padding android:left="integer"// 左邊距 android:top="integer"// 上邊距 android:right="integer"// 右邊距 android:bottom="integer"// 下邊距 /> <!-- 大小 --> <size android:width="integer"// 圖形寬度 android:height="integer"// 圖形高度 /> <!-- 填充 --> <solid android:color="color"// 圖形的填充色 /> <!-- 描邊 --> <stroke android:width="integer"// 描邊的寬度 android:color="color"// 描邊的顏色 android:dashWidth="integer"// 虛線寬度 android:dashGap="integer"// 虛線間隔 /> </shape>
因為shape中的屬性更的是組合使用,所以舉例沒法單獨只使用一個標籤。
solid、corners和stroke
經常使用的就是 solid
、 corners
、 stroke
這三個標籤

下面是這個Button背景的xml程式碼
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <corners android:radius="10dp"/> <solid android:color="#008577"/> <stroke android:width="3dp" android:color="#D81B60" android:dashWidth="10dp" android:dashGap="5dp"/> </shape>
solid
就是整個區域的填充色
corners
則是控制了背景的4個角的圓角半徑
stroke
控制了描邊的屬性, color
表示線的顏色; width
描邊的寬度,其實就是圖中紅線的高度; dashWidth
表示虛線中紅線的寬度; dashGap
表示虛線間隔寬度,也就是紅色之間的間隔距離
corners屬性覆蓋
再來看看 corners
標籤的屬性覆蓋規則。下圖中將 radius
屬性設定為10dp,並同時設定給其他的四個屬性不同的值,從圖中可以看出四個屬性將都 radius
屬性覆蓋了。

具體的xml程式碼:
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <corners android:bottomLeftRadius="30dp" android:bottomRightRadius="40dp" android:radius="10dp" android:topLeftRadius="0dp" android:topRightRadius="20dp"/> <solid android:color="#008577"/> </shape>
padding
再來看看 padding
標籤的作用,圖片如下:

第一行的 TextView
的背景沒有設定 padding
屬性;第二行則是使用了 padding
標籤,不低的地方在於有無內容的差別。 shape
的xml程式碼如下:
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <solid android:color="#008577"/> <padding android:bottom="10dp" android:left="10dp" android:right="10dp" android:top="10dp"/> </shape>
shape
中雖然使用了 padding
標籤,但是在被設定該背景的view上依然可以通過修改 paddingLeft
等對應的相關屬性進行覆蓋。
size
size
控制圖形的寬高尺寸,對應屬性也就 android:width
和 android:height
但是,在作為背景的情況下,shape的size屬性並不是完全有效的,最後的顯示效果還是會由 view
來決定, size
只是在 view
的寬(或高)為 wrap_content
且顯示內容後的實際大小不超過 size
所設定的值得時候才會生效。這個效果其實比較類似 minWidth
和 minHeight
兩個屬性的效果。
下圖中,左邊的內容沒有超出,按照 size
所設定的大小顯示,而右邊的內容超出了 size
所設定的大小,按照實際大小顯示。這裡比較簡單,不上程式碼了。

不要這樣就覺得 size
沒有什麼作用,例如下圖

截圖的原因,圖片被放大了,這些圓使用 ImageView
的 src
顯示時, ImageView
只需要設定 wrap_content
就可以按照設定的大小顯示了。
當然,如果你修改了 ImageView
的大小,那 shape
的顯示就會根據 ImageView
的 scaleType
來決定最後的顯示效果了。
gradient
不太清楚 gradient
標籤的使用頻率,畢竟是UI效果,實際工作中涉及到漸變的時候基本都是設計小姐姐直接給圖的。不過不妨礙我們瞭解 gradient
的用法。
android:type
這裡先說 android:type
屬性,表示漸變型別,有三個可以選擇的屬性 linear
線性、 radial
放射性、 sweep
掃描性,預設為線性
下圖,從左到右的中 android:type
分別是 linear
線性、 radial
放射性、 sweep
掃描性

- 線性:比較好理解,就是一條線從左到右,準確的說是從一邊到另一邊漸變,而這個方向是可以調整的,後面會說明
- 放射性:從一箇中心點向外漸變
- 掃描性:這個比較像雷達,具體看圖,我描述不出來,不過也是有中心點的
漸變顏色
先以預設漸變型別來看下主要的三個屬性:
- android:centerColor 漸變中間位置的顏色
- android:endColor 漸變結束位置的顏色
- android:startColor 漸變開始位置的顏色

上圖中第一個按鈕,設定了開始顏色是紅的,中間顏色是白色,結束顏色是綠的,具體程式碼如下:
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"> <gradient android:centerColor="#FFFFFF" android:startColor="#D81B60" android:endColor="#008577"/> <corners android:radius="10dp"/> </shape>
android:angle
start -> end預設的方向是從左到右,通過設定 android:angle
屬性可以修改這個方向,0 -> 360 增加時,方向是逆時針調整的。上圖中,第2、3、4個按鈕的 shape
的 android:angle
屬性值分別是90、180、270。注意, android:angle
屬性需要時45的整數倍。
android:centerX和android:centerY
這兩個屬性是控制漸變中心位置的座標的,取值區間[0,1],預設為0.5

上圖中,三種漸變型別都設定了中心點的位置, radial
和 sweep
兩種型別比較好理解,就是控制中心在圖形中的的相對位置。
linear
型別也會受到這兩個屬性的改變,因為是線性所以作用應該是對線的中心點的控制,我試了一下, centerX
或者 centerY
都能控制漸變的中心點,而且不受 android:angle
屬性影響。但是 centerX
和 centerY
同時使用時,不存在屬性前後順序而導致屬性覆蓋的的情況,只有 centerX
起作用。
android:gradientRadius
設定漸變的半徑,當type為放射性漸變radial時有效,差異效果如下圖:

當 solid
和 gradient
同時使用時,在程式碼中比較靠下的標籤會覆蓋之前的標籤屬性
shape
shape
的標籤屬性都瞭解完了,接著就是 shape
自身的屬性了
前面舉例的都是矩形,也就是 android:shape="rectangle"
的情況下,接下來看看其他三種類型,
首先是 line
,因為虛線和實線相差兩個屬性,就直接用虛線舉例了

下面是具體的xml程式碼:
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="line"> <stroke android:width="3dp" android:color="#D81B60" android:dashWidth="10dp" android:dashGap="5dp"/> <size android:height="10dp"/> </shape>
經過查資料和一些測試,使用 line
型別有些限制
- 必須使用
stroke
標籤,view
顯示的高度必須大於(必須是大於,等於都不行)stroke
標籤中width
寬度屬性的值。也就是說,view
所設定的高度需要大於虛線高度;如果使用了size
標籤,size
的height
也必須比大於虛線高度 - 4.0 以上手機,預設使用硬解碼,虛線在真機會顯示成實線,可以關閉頁面的硬體加速,也可以給相關的
view
設定成軟解layerType="software"
再來是 oval
,雖然表示的是橢圓,其實只需要顯示圖形的 view
是正方形就可以顯示成圓了。之前在使用 size
就是以圓為例子舉例的,在 ImageView
的 src
中使用 shape
,xml程式碼如下:
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <solid android:color="#008577"/> <size android:width="20dp" android:height="20dp"/> </shape>
當然,也可以通過限制 view
的寬高,來保證顯示圓。
oval
和 rectangle
在使用其他的屬性方面規則基本一直,只是 corners
標籤不在具有任何效果。
最後是 ring
,關於ring的屬性比其他的三種圖形要多出4個屬性
- innerRadius,shape為ring時有效,內環半徑
- innerRadiusRatio,shape為ring時有效,內環的厚度比,即環的圖形寬度與內環半徑的比例,按照這個比例計算內環半徑,預設為3,可被innerRadius值覆蓋
- thickness,shape為ring時有效,環的厚度
- thicknessRatio,shape為ring時有效,環的厚度比,即環的圖形寬度與環的厚度的比例,按照這個比例計算環的厚度,預設為9,可被thickness值覆蓋

上圖中第一個環的xml程式碼如下:
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:useLevel="false" android:shape="ring"> <solid android:color="#008577"/> <size android:width="100dp" android:height="100dp"/> </shape>
可以看到並沒有設定什麼其他的屬性,只是設定了 solid
和 size
,這基本就是預設的一個環了,內環半徑和環的厚度都是分別是按3和9的比例計算的,後面會具體講解這兩個值得意思。
第二個環,則是新增 android:thickness="5dp"
屬性,設定了環的厚度為5dp
第三個環,則是新增 android:innerRadius="10dp"
屬性,設定內環半徑為10dp
第四個環,可以算解釋了 innerRadiusRatio
和 thicknessRatio
兩個屬性,具體xml程式碼如下:
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:useLevel="false" android:innerRadiusRatio="3" android:thicknessRatio="100" android:shape="ring"> <solid android:color="#008577"/> <size android:width="200dp" android:height="200dp"/> </shape>
innerRadiusRatio
和 thicknessRatio
兩個屬性都是對比 view
最終顯示的大小來計算的,可以對應著兩個公式:
innerRadius = view.width / innerRadiusRatio
thickness = view.width / thicknessRatio
這樣以來著兩個屬性就比較好理解了
對於ring的使用還有幾點需要注意:
- 必須寫
useLevel="false"
,否則不顯示 - 如果
android:thickness
和android:innerRadius
設定了值,無論view的大小如何設定,將不會影響圖片大小,這有可能會導致圖形顯示不全。 -
padding
和corners
兩個標籤不起作用,其他的標籤可以正常的使用