shape 大家應該都了解,一種 android 提供的,方便的在 android 繪制生成簡單圖形的 xml 文件,常見的 shape xml 文件如下:
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:innerRadiusRatio="5"
android:shape="ring"
android:thicknessRatio="5"
android:useLevel="false"
>
<stroke android:width="1dp" android:color="@color/colorPrimary"></stroke>
<solid android:color="@color/colorPrimary"></solid>
</shape>
就是這么簡單的一個 xml 文件,通過 shape 支持的 xml 標簽,我們可以使用 shape 繪制出很多常用的圖形,我們常常使用的 button 的背景都是用 shape 的繪制的,若是我們使用 png 代替 shape ,那么就會出很多問題的
shape 有著非常鮮明的優點,明顯就是為了專門解決依賴一類需求,簡單多變的多邊形圖案的繪制:
- shape 支持寬高的無限拉伸,不存在 png 圖片拉伸的問題
首先 shape 不是 bitmap 位圖,是我們通過 xml 文件聲明號繪制信息,然后 shape 根據所在 view 的尺寸動態繪制出來的,和 SVG 矢量圖有異曲同工之妙,只不過沒有 SVG 這么強大罷了 - shape 文件很小,節約 apk 安裝文件大小,也能節約內存
因為 shape 是通過 canvas 繪制出來的,所以不存在 bitmap 位圖來占用大量內存
別看 shape 大家常用,但是 shape 支持很多標簽和繪制技巧,這塊很多同學就不是那么清楚了,用好 shape 真的能省我們很多因思考死的腦細胞,也能讓 UI 少出一些圖
1. shape 標簽
shape 支持的基礎標簽不多,但是這每一個基礎標標簽中都支持很多屬性設置,正式通過這些屬性變化,繪制出一個個看似很復雜的圖形出來,對于這幾個基礎標簽我們來一個一個的看
shape 詳細屬性如下,不包含根標簽
1.1 shape 支持的基礎標簽
shape 支持根標簽,大小,邊框,邊框圓角,填充色,漸變色 這6個標簽,6個標簽相互配合,每個標簽中還有細分屬性。當然了這 6個標簽是干什么的很好理解,一來見名知已,二來也是大家經常使用的
根本標簽
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android">
大小
<size></size>
邊框
<stroke></stroke>
填充色
<solid></solid>
內邊距
<padding></padding>
邊框圓角
<corners></corners>
漸變色
<gradient></gradient>
</shape>
1.2 shape 支持的 <shape> 根標簽
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle|line|oval|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="false|true"
較少用,一般設為false,否則圖形不顯示。為true時可在LevelListDrawable使用
android:visible="false|true"
android:dither="false|true"
將在位圖的像素配置與屏幕不同時(例如:ARGB 8888 位圖和 RGB 565 屏幕)啟用位圖的抖動;值為“false”時則停用抖動。默認值為 true。
>
</shape>
我們需要注意的屬性是 <shape>,該屬性決定了本 shape 文件的形狀,支持4種基礎形狀:
- rectangle / 矩形
- line / 線
- oval / 橢圓
- ring / 圓環
可能 line 線的效果不是我們預想的那樣,這個問題留待以后補充
善用這4種圖形,結合其他屬性設置,我們基本能畫出常用的所有圖形,比如橢圓在正方形的尺寸中就是一個圓,矩形加上圓角就是圓角矩形
剩下的其他參數:
- tint / tintMode 是著色器,能不能在 API 19 上使用,我也沒試
- dither 一般也沒見人寫過
- innerRadius / thickness 是畫圓環的參數,一個內圓半徑,一個外圓寬度
- innerRadiusRatio / thicknessRatio 這是內圓半徑和外圓寬度的比例,默認 3:9,測試過發現反過來寫才能是我們理解的正常顯示
1.3 corners支持的基礎標簽
corners 這個大家最熟悉了吧,圓角,配合 rectangle / 矩形 來使用了,我們使用 corners 圓角 可以畫出圓角矩形來:
- android:radius="integer"
圓角半徑,該值設置時下面四個屬性失效 - android:bottomLeftRadius="integer"
左下角圓角半徑 - android:bottomRightRadius="integer"
右下角圓角半徑 - android:topLeftRadius="integer"
左上角圓角半徑 - android:topRightRadius="integer"
右上角圓角半徑
話說,為啥圓角的此存設置只有一個數值呢,按照 canvas 繪制圓角的思路,應該是有 x,y 2個參數來描述這個圓角的圓角有多大的,為啥這里就一個參數呢。只能這樣理解了,shape 的圓角默認就是正方形尺寸的
1.4 padding 支持的基礎標簽
- android:bottom="integer"
設置底部邊距 - android:left="integer"
左邊邊距 - android:right="integer"
右邊 - android:top="integer"
頂部
1.5 solid 支持的基礎標簽
- android:color="color"
shape的填充色
填充色不用說了吧,不過有一點要說明下,要是在 shape 根標簽里設置了 tint 著色器,那么就會覆蓋 solid 的顏色
1.6 size支持的基礎標簽
- android:height="integer"
寬度 - android:width="integer"
高度
shape 一般都不指定具體的大小,因為大多數時候 shape 是作為view 的背景來存在的,此時 shape 的大小是隨著 view 的大小走的。
但是有的時候 shape 無法從外接獲取準確的大小參數,那么這個時候就需要 shape 自己指定大小了,比如我們在很多時候畫圓和圓環當圖片使用的時候,就必須指定大小了
但是若是 shape 既設置 size 了,外界頁存在依附的 view 的情況下,view 的大小怎么樣呢:
view 的 size = wrap_content
此時 view 的最終大小跟著 shape 的大小走view 的 size 有指定大小時,比如 100dp
此時 shape 不影響 view 的最終大小
1.7 stroke 支持的基礎標簽
- android:color="color"
描邊的顏色 - android:width="integer"
描邊的寬度 - android:dashGap="integer"
虛線間隔 - android:dashWidth="integer"
虛線寬度
邊框不用說了吧沒,注意其中虛線的屬性
1.8 gradient 支持的基礎標簽
找到一篇配圖很準的,漸變可以參考這篇:
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:angle="integer"
漸變角度,當上面type為線性漸變linear時有效。角度為45的倍數,0度時從左往右漸變,角度方向逆時針
android:startColor="color"
漸變開始位置顏色
android:centerColor="color"
漸變中間位置顏色
android:endColor="color"
漸變結束位置顏色
android:centerX="float"
放射中心坐標
android:centerY="float"
放射中心坐標
android:gradientRadius="integer"
type為放射性漸變radial時有效,漸變的半徑
android:type="linear|radial|sweep"
漸變類型,分別為線性、放射性、掃描性漸變,默認為線性漸變linear
android:useLevel="false|true"
與上面shape中該屬性的一致
/>
</shape>
漸變是 shape 中的難點,很多人其實對漸變的設置很不熟悉,其實各個平臺工具中漸變的設置都差不多,shape 中的漸變熟悉了,看其他的也大體能明白,所謂一法通萬法通
什么是漸變,就是從一種顏色勻速過渡到另一種顏色,自行實現可以用 android 中的 color 插值器。這樣肯定就有2個顏色,一個是開始的顏色,一個是結束的顏色,shape 還支持中間顏色,就是3色過渡
這樣就能理解 startColor 、 centerColor 、 endColor 是干啥的了
另外支持3種漸變算法 type
- linear - 線性
- radial - 掃描性
- sweep - 放射性
- 默認是線性漸變 inear
對于這3種漸變算法,來個圖最合適:
一圖在手,勝似千言萬語,大家注意期中顏色變化的起始方向和順序
- linear
線性這個好理解把,就是一條線,從左到右變化 - radial - 掃描性
也叫雷達圖,按照圓心旋轉 - sweep - 放射性
從內到外的變化
圖中 radial 、sweep 都是從中間開始變化的,這是因為我們可以指定中心點,這里我們指定的是圓心
。這2個參數就是 centerX / centerY 了,取值區間[0,1],默認為0.5,即中心位置
另外還支持 centerColor 的便宜位置,默認 centerColor 肯定是在中間的,我們還可以動態設置 centerColor 在任何位置,這個屬性就是 gradientRadius 了,其取值范圍和動畫 xml 取值一樣:
- 30dp,就是 30dp
- 30%,圖形的 30% 處
angle 屬性是旋轉角度,只能以 90 的倍數設置,90 代表旋轉 90度,android 中是以逆時針旋轉為準的
1.9 所有標簽
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:dither="false|true" //將在位圖的像素配置與屏幕不同時(例如:ARGB 8888 位圖和 RGB 565 屏幕)啟用位圖的抖動;值為“false”時則停用抖動。默認值為 true。
android:shape="rectangle|line|oval|ring"http://分別為矩形、線、橢圓、環。默認為矩形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="false|true" // 較少用,一般設為false,否則圖形不顯示。為true時可在LevelListDrawable使用
android:visible="false|true"
>
<!-- 圓角 -->
<corners
android:radius="integer" // 圓角半徑,該值設置時下面四個屬性失效
android:bottomLeftRadius="integer" // 左下角圓角半徑
android:bottomRightRadius="integer" // 右下角圓角半徑
android:topLeftRadius="integer" // 左上角圓角半徑
android:topRightRadius="integer" // 右上角圓角半徑
/>
<!-- 漸變 -->
<gradient
android:useLevel="false|true" // 與上面shape中該屬性的一致
android:type="linear|radial|sweep" // 漸變類型,分別為線性、放射性、掃描性漸變,默認為線性漸變linear
android:angle="integer" // 漸變角度,當上面type為線性漸變linear時有效。角度為45的倍數,0度時從左往右漸變,角度方向逆時針
android:centerColor="color" // 漸變中間位置顏色
android:startColor="color" // 漸變開始位置顏色
android:endColor="color" // 漸變結束位置顏色
android:centerX="float" // type為放射性漸變radial時有效,設置漸變中心的X坐標,取值區間[0,1],默認為0.5,即中心位置
android:centerY="float" // type為放射性漸變radial時有效,設置漸變中心的Y坐標,取值區間[0,1],默認為0.5,即中心位置
android:gradientRadius="integer" // type為放射性漸變radial時有效,漸變的半徑
/>
<!-- 內邊距 -->
<padding
android:bottom="integer" // 設置底部邊距
android:left="integer" // 左邊邊距
android:right="integer" // 右邊
android:top="integer" // 頂部
/>
<!-- 大小 -->
<size
android:height="integer" // 寬度
android:width="integer" // 高度
/>
<!-- 填充 -->
<solid
android:color="color" // shape的填充色
/>
<!-- 描邊 -->
<stroke
android:color="color" // 描邊的顏色
android:width="integer" // 描邊的寬度
android:dashGap="integer" // 虛線間隔
android:dashWidth="integer" // 虛線寬度
/>
</shape>
2. shape 經典實戰
上面講解 xml 屬性時我也沒貼具體的 shape 例子,就是好放到這里,搜集了一些 shape 的典型應用,應該差不多全了,大伙忘了的來這里找找
2.1 shape 畫虛線
shape 虛線的 xml 很簡單
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="line"
android:useLevel="false">
<stroke
android:width="5px"
android:color="@color/colorPrimary"
android:dashGap="5dp"
android:dashWidth="5dp"></stroke>
</shape>
上面的幾個參數就能搞定,但是 shape 畫虛線里面很幾個坑,膈應人極了
- view 的 height 高度必須大于stroke 的 width 寬度,等于都不行,像上面虛線寬 5px,我給 view height 設 5px 都不行,必須大于 5px 才行,我給的 6px,另外 wrap_content 也不行,要不顯示不出來
- 4.0 以上手機,默認使用硬解碼, 虛線在實機會顯示實現,必須給相關的 view 設置成軟解才行 layerType="software"‘
<View
android:layout_width="match_parent"
android:layout_height="6px"
android:layerType="software"
android:background="@drawable/shape_dash"></View>
畫實現和虛線的注意點一樣,區別就在于虛線的幾個參數上
2.2 shape 畫圓角矩形
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="10dp"></corners>
<stroke android:width="1dp" android:color="@color/colorPrimary"></stroke>
<size android:width="20dp" android:height="20dp"></size>
</shape>
圓角矩形沒什么可說的,都是標準配參數就行了
2.3 shape 畫圓形
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@android:color/white"></solid>
<stroke android:width="1dp" android:color="@color/colorPrimary"></stroke>
</shape>
還記得 oval 畫的是什么嗎,是橢圓,前面說過,正方形畫橢圓就是正圓,所以只要能保證尺寸是正方形就行了,可以給外層 view 設置同樣的 寬高,也可以在 shape 里面寫 size 設寬高,不過一般做背景時我們都是不寫 size 的
2.4 shape 畫圓環
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="ring"
android:useLevel="false">
<solid android:color="@color/colorPrimary"></solid>
</shape>
畫圓環時前面說過外環和內圓默認是 3:9 的關系,我們不改就是這樣的
蛋疼呢,shape 的圓環也是幾個注意要點:
- 注意必須寫 useLevel="false" ,否則不顯示
- thickness 、 innerRadius 只支持實際單位,不支持諸如 30% 這樣的相對參數,畢竟后面還有thicknessRatio 、innerRadiusRatio 這樣的比例參數呢,另外 thickness 、 innerRadius 要是設置了實際大小,比如 50dp,那么不論附著的外層 view 有多大,圓環都只能按照 50dp 顯示,除非我們作為 drawable 使用,否則不要寫這 thickness 、 innerRadius 2個參數
2.5 shape 畫漸變色
用上面圓環的例子來一個
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="ring"
android:useLevel="false">
<gradient
android:type="sweep"
android:centerColor="@android:color/white"
android:centerX="0.5"
android:centerY="0.5"
android:endColor="@android:color/holo_blue_light"
android:startColor="@android:color/holo_red_light"/>
</shape>
是不是挺好看的,早先系統默認的 loading 那個轉圈的就是用 shape 寫的
需要注意的是:
- 加了漸變色,就不能在寫填充色了,要不就覆蓋了,什么也看不出來了
2.6 shape 畫單邊框
就是利用 layer-list 圖層來做,2圖,底下一層是邊框顏色圖層,上面是背景色圖層,通過移動上面背景色圖層來把下面邊框顏色圖層讓出一部分出來,形成邊框
效果是這樣子的:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<solid android:color="@android:color/holo_red_light"></solid>
</shape>
</item>
<item android:bottom="1dp">
<shape>
<solid android:color="@android:color/white"></solid>
</shape>
</item>
</layer-list>
這里是利用 item 的 位移屬性來做的,top 就是向上移動,top = 1dp,就是給下面圖層讓出 1dp 出來,這就成了邊框。
當然這是單邊框,我們還可以給其他邊設置位移,形成任意數量邊框的圖
試試3個邊框的:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<solid android:color="@android:color/holo_red_light"></solid>
</shape>
</item>
<item android:bottom="1dp" android:left="1dp" android:right="1dp">
<shape>
<solid android:color="@android:color/white"></solid>
</shape>
</item>
</layer-list>
上面是設置的正數,我們還可以設置負數,思路和上面本質上都是一樣,區別是2個圖層需要倒過來,具體就不寫代碼了。
2.7 shape 畫角標圖
經典的顯示消息數量的角標圖
效果圖:
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<stroke android:width="1dp" android:color="@android:color/white"></stroke>
<solid android:color="@android:color/holo_orange_dark"></solid>
</shape>
當然為了顯示精確還是推薦使用自定義 view 來做,因為不同設備子的大小有差異,可能會出現文字除了背景圖的問題,自己有用 canvas 就沒有這個問題,還能根據文字的多少決定是圓形的還是長條圓角的,有的啃爹產品讓顯示 999+ ,明顯圓形就放不下4個字了,能放下4個字圓也會太大顯得突兀
2.8 shape 畫陰影
shape 畫陰影和單邊框一個原理,都是移動圖層,形成視覺差,我找到一個圖解,看著比較容易理解:
然后咱自己畫一個,可以選擇給一邊記上邊框,這樣顯得更顯眼一點
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:top="2dp" android:left="2dp">
<shape>
<solid android:color="@android:color/darker_gray"></solid>
</shape>
</item>
<item android:right="2dp" android:bottom="2dp">
<shape>
<stroke android:width="1dp" android:color="#f2f2f2"></stroke>
<solid android:color="@android:color/white"></solid>
</shape>
</item>
</layer-list>
以前有個很流行的交互效果沒,就是按鈕點下后出陰影,就是把做了2張 shape 圖,一個有陰影,一個不帶,然后放到 selector 里面。說實話以前我都不知道是怎么實現的,現在回過頭再來看,真是簡簡單單啊,說明系統知識一定要全面啊,要不有時候費死勁都不知道怎么實現啊