一、前言
在 Android 的開發(fā)過程中,Drawable 經常會被用到,一般會用 Drawable 為 View 設置一個顯示的效果。而在 Android 下,也提供了很多 Drawable 的默認實現,它們涉及到的內容非常的多,從屬性到方法,但是日常生活中,會用到的只有那么些方式。
本文就在工作中,Drawable 的常用方式,整理出一篇文章,會攜帶一些場景,如有缺漏的,可以在文末留言,本文采用精益編寫,如有必要,會一直長期更新。
二、什么是 Drawabe
Drawable 實際上是一個抽象類,主要用于將一個可繪制的資源,按要求繪制成圖形,顯示在屏幕之上。
Android 為了讓開發(fā)者更方便的使用 Drawable ,提供了很多 Drawable 的實現類,并提供對應的 xml 的屬性配置。
例如:
同時它也可以使用代碼的方式實現,它們的效果是一樣的。
實際上,不管是使用 xml 資源的方式,還是直接在邏輯中硬編碼的方式,它們最終顯示的效果都是一樣的,如下圖,一個藍色的方塊:
Android 中,為我們提供了非常多默認的 Drawable,正常來說,一般是足夠我們使用的,它們的關系如下圖:
基本上,從 XxxDrawable 這種類名,就可以看出來它對應的 Xml 資源的命名,唯一需要注意的是 <shape/> 的實現類是 GradientDrawable ,而不是 ShapeDrawable。
三、Drawable 的常用實現
3.1 圓角的按鈕
有些 App 中,按鈕的圓角,如果背景只是一個純色或者是簡單的規(guī)則漸變,是可以使用 <shape/> 來完成的,如前面示例一樣。
如果想要為其增加一個圓角,可以在 <shape/> 中使用 corners
屬性,它支持設置一個 android:radius
,用來設置四個角落的圓角角度,當然也提供了單獨的屬性去設置某個角的圓角角度,例如,android:topLeftRadius
就是設置左上的圓角的。
實現的效果如下:
3.2 帶邊框的圓角按鈕
想在圓角的 <shape/> 上,加一個邊框,可以使用 stroke
屬性,當然,不是設置 corners
屬性,它就不是一個圓角的效果了。
stroke 用于設置邊框,可以指定顏色和邊框的寬度。
可以看到這里指定了一個黃色的邊框,效果如下圖:
3.3 單邊邊框
在 <shape/> 中,如果使用 stroke
來設置邊框,它會在四周都加上邊框,但是有時候,我們只需要在底部或者單邊繪制一個邊框的效果,例如:一個列表頁里每一項的分割線。
這個時候,就可以使用 <layout-list/>,使用兩個 shape 來疊加實現。
這樣的一個 Drawable,如果作為背景的話,顯示效果就是在白色背景下,有一條 1px 的灰線。當然其他方向可以參考這個方案,截圖發(fā)現 1px 的線不太明顯,這里就不放圖了。
3.4 漸變的背景色
<shape/> 除了支持一個純色規(guī)則圖案,它還可以實現漸變的效果,用的最多的,就是線性漸變。
例如一些視頻 App 的 UI 設計,會將視頻名稱直接布局在視頻海報上,就可以使用線性漸變的 <shape/> 來實現簡單的背景效果,使其在白色的視頻海報上,顯示的效果依然能看得清楚文字。
例如一般的視頻App :
看到其實海報下面的文字,底部是有一個漸變的背景色的。
這種漸變的效果,可以使用 gradient
標簽來設置。
gradient
支持的屬性,基本上看名稱就可以知道意思,唯一需要注意的是 android:angle
這個屬性,用于設定漸變的角度,但是它不是任何值都支持的,只支持 45 的倍數。
效果如下圖:
3.5 海報的默認圖
通常在圖片加載的過程中,會為其定義一個默認圖片。一般的設計都是會將這個默認圖做的非常的簡潔,例如中間一個 App 主題的 Icon,然后其它地方純色鋪平。
類似下面這種效果:
這種默認的圖片,當然你可以使用一張等大的圖,但是這樣不利于適配,不同的 UI 設計尺寸,你需要提供不同的圖片。當然你也可以使用 9patch 的圖片,但是你會發(fā)現有些 density 為 2.75 這種奇葩的手機下,圖標會微微偏移,不在正中間。所以這里可以使用一個<layer-list/> 的 Drawable 來實現。
<layer-list/> 是一個帶層級效果的 Drawable ,有點類似于布局中的 FrameLayout 的效果。它支持設置多個 Drawable ,并將它們疊加在一起。
所以這樣的場景下,我們就需要一個純色的背景加一張小圖,即可實現默認圖的效果。
下面是 xml 的實現代碼:
下面是運行后的效果圖:
3.6 帶按下效果的按鈕
對一個按鈕,設計一個按下的效果。可以使用 <selector/> 這個 Drawable。它支持在不同的狀態(tài)下,顯示不同的 Drawable。
<selector> 同樣也支持設置多個 Drawable ,區(qū)別在于你需要額外的為每個 Drawable 設置不同的狀態(tài),如果不設置,則為默認顯示的狀態(tài)。
這些不同的狀態(tài),在 xml 里,都是以 android:state_Xxx 開頭來定義的,將其設置為 true 即可生效。
Android 為我們提供了非常多的狀態(tài),比較常用的有:
- state_pressed:按下的效果。
- state_checkable:是否可設置 checked 狀態(tài)的效果。
- state_selected:支持 selected 并且當前處于 selected 的效果。
- state_checked:支持 checkeable 并且當前處于 checked 的效果。
接下來讓我們看一個實際的按鈕例子,這里只為其設置按下的效果,xml 代碼如下:
實現效果如下:
3.7 一個帶按下效果的圓角按鈕
這個沒啥好說的,結合上面的效果就可以做到。你可以選擇將它們寫在不同的 Drawable 中,也可以在一個 xml 文件中,使用不同 item 來完成。Item 標簽是可以支持內部再嵌套一個 Drawable 的。
3.8 帶按下動畫
按下效果除了使用 <selector> 做一個變色的效果之外,在 Api Level 21 之后,可以嘗試使用 StateListAnimator 來實現一個 Material Design 的按下效果。
具體細節(jié)可以看看我之前的一篇文章:《利用 StateListAnimator 為你的點擊加個動畫吧!》
StateListAnimtor 使用起來非常的簡單:
- 在 res 中創(chuàng)建一個 animator 目錄。
- 在其中創(chuàng)建一個 xml 資源文件,就是一個 <selector/> 。
- 在 xml 資源中使用 <selector/> 中,定義我們 View 切換狀態(tài)時候的動畫,其實就是一個個 <objectAnimator/>。
- 最終將定義好的 animtor 通過 View 的
setStateListAnimator()
方法或者android:stateListAnimator
屬性,設置到 View 上。
舉個例子。
首先在 /res/animtor 目錄下,創(chuàng)建一個 btn_press_animator.xml
文件。
可以看到,和 StateListDrawable 一樣,它也是通過 android:state_xxx
屬性來定義不同的 Animator 的,如果存在多個 Animator ,可以使用 <set/> 標簽將其包裹起來。 這里只是簡單的在 state_pressed 的時候,做了一個縮小的動畫。
然后,定義一個 View,為其設置屬性 android:stateListAnimator="@animator/btn_press_animator"
。
來看看運行的效果:
[圖片上傳失敗...(image-e0ebc3-1512715336178)]
3.9 三角形的 Drawable
?假如有類似氣泡提示的效果,如下圖。
這樣的效果,除了使用 9patch 來實現之外,還可以拿兩個 Drawable 來拼接實現,這就需要一個圓角的矩形和一個三角的 Drawable。
三角 Drawable 的實現思路很清奇,是使用一個矩形的 shape ,通過使用 rotate 實現的旋轉,來達到尖角的效果。
這個例子,就是上圖的實現效果,是一個黃色的倒三角。如果想要的是一個正三角,只需要改變旋轉的角度就可以了。
3.10 Tint 著色的 Drawable
在 Drawable 中,如果使用的是 BitmapDrawable 或者 NinePatchDrawable(9patch) 的資源的話,可以使用 android:tint 屬性為其著色。
默認情況下,待著色的圖片資源,會將其所有有顏色地方,都著色成我們指定的顏色,但是會保留透明度。
效果圖如下,上面是原圖,下面是使用 tint 之后的效果:
前面這里給的是 tint 默認的效果,還可以通過 android:tintMode 指定成不同的著色效果,例如下面將 android:tintMode 指定成 screen 之后,效果就完全不一樣了。
3.11 鋪平的 Drawable
有時候,作為一些有規(guī)則的圖片的背景,可以使用一張很小的圖片,然后設定 android:tileMode 屬性,為其設定一個平鋪的效果。
[圖片上傳失敗...(image-916a9a-1512715336178)]
tileMode 可以指定多個屬性值,用于指定不同的效果,以下就是兩個比較常用的,上圖使用的是 repeat ,下圖使用的是 mirror。
3.12 狀態(tài)可控的層級 Drawable
如果想要一個 ImageView 上,根據不同的條件顯示不同的 Drawable ,可以使用 <level-list/> 。從名字上就可以看出它是一個帶層級的 Drawable 組合,和 <selector/> 很像,但是區(qū)別在于它的顯示狀態(tài)是我們邏輯可控的。最常見的例子是拿它實現一個開關的燈泡,兩態(tài)的圖片,一個表示開燈,一個表示關燈,相信大家應該都見過例子。
今天舉一個例子,給某個圖標加紅點的邏輯。當然我們也可以使用通過一個 FrameLayout 來實現,但是今天我們試試用 <level-list/> 來實現它。
這里使用一個 <level-list/> ,內部根據不同的 maxLevel 和 minLevel 來控制范圍,這里只有兩態(tài),所以直接讓它們取值一致。紅點使用 <layer-list> 做了一個層疊,在右上角上,直接畫了一個大小為 5dp 的紅色圓點。
最終我們可以通過 ImageView 的 setImageLevel()
方法來控制顯示的內容。
四、結語
到這里基本上涵蓋了 Drawable 大部分的使用場景,在實際例子中學東西是印象最深刻的。當然,Drawable 的使用不止這些,還有一些例如拿 ClipDrawable 來實現一個切割進度的效果之類的,這種還需要寫邏輯代碼,就不在本文的范圍內了,本文主要介紹一些靜態(tài)能實現的效果。
你只需要掌握最基本的規(guī)則,什么 Drawable 能實現什么效果,具體碰到實際需求的時候,再來細致的研究,基本上微調一下就可以使用。
如果你還又什么更有意思的 Drawable 使用技巧,歡迎在文末留言,我們一起討論一下,如有需要,會持續(xù)更新。
今天在承香墨影公眾號的后臺,回復『成長』。我會送你一些我整理的學習資料,包含:Android反編譯、算法、設計模式、虛擬機、Linux、Kotlin、Python、爬蟲、Web項目源碼。
推薦閱讀:
- 認真聊聊陰影那些事兒
- 利用 StateListAnimator 為你的點擊加個動畫吧!
- 好的代碼可以自己說話
- 關于如何編寫 Clean Code 的 6 個簡單技巧
- 手寫你的第一個 Dalvik 版的 HelloWorld !
[圖片上傳失敗...(image-b12cfc-1512715336178)]