概述
Android把任何可繪制在屏幕上的圖形圖像都稱為drawable 資源,你可以通過類似getDrawable(int)的API來獲取drawable資源,你也可以通過類似android:drawable 、 android:icon的屬性將drawable 資源應用到其他的XML資源中。Drawable表示的是一種可以在Canvas上進行繪制的抽象的概念、它的種類有很多,它們都表示一種圖像的概念,通過圖片或者顏色可以構造出各式各樣的圖像的效果,也可以通過繼承Drawable類自定義Drawable來構建圖像的效果。
我們經常會接觸到的drawable: state list drawable、shape drawable、 layer list drawable、ripple drawable、inset drawable以及nine patch drawable。前5個drawable通常定義在XML布局文件中,因此我們統一將它們歸屬為XML drawable類別。這6中常用的drawable與Drawable類之間的關系如下圖所示:
下面我會講解上面提到的六種常用的Drawable和Color State List Resource,最后再講解一下自定義Drawable。
預備知識
1 關于Image Dithering(圖像抖動)的介紹
Image Dithering(圖像抖動) 是 Dithering(抖動)在數字圖片處理中的一種應用。一般情況下是指一種使數字圖片在降低色深的情況下,呈現最佳展現的技術。比如將24bit色深的圖像使用8bit色深來展現。
例圖:
圖像抖動技術常用的算法是 Floyd–Steinberg dithering.
編程應用: 在Android應用開發中,使用 android:dither 屬性來設置圖像是否應用抖動處理。
幾種常用的XML drawable
1 shape drawable
使用XML文件來定義幾何圖形,可以理解為通過顏色來構造的幾何圖形,它既可以是純色的幾何圖形,也可以是具有漸變效果的幾何圖形。對應GradientDrawable類。
file location:
res/drawable/filename.xml
The filename is used as the resource ID.
compiled resource datatype:
Resource pointer to a GradientDrawable.
resource reference:
In Java: R.drawable.filename
In XML: @[package:]drawable/filename
語法:
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape=["rectangle" | "oval" | "line" | "ring"] >
<corners
android:radius="integer"
android:topLeftRadius="integer"
android:topRightRadius="integer"
android:bottomLeftRadius="integer"
android:bottomRightRadius="integer" />
<gradient
android:angle="integer"
android:centerX="float"
android:centerY="float"
android:centerColor="integer"
android:endColor="color"
android:gradientRadius="integer"
android:startColor="color"
android:type=["linear" | "radial" | "sweep"]
android:useLevel=["true" | "false"] />
<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>
1.1 <shape>節點元素
該節點元素一定被用作根元素。
<shape>節點元素的常用屬性:
1> android:shape
定義shape drawable的類型,可用的有效值如下:
Value | Desciption |
---|---|
"rectangle" | A rectangle that fills the containing View. This is the default shape. |
"oval" | An oval shape that fits the dimensions of the containing View. |
"line" | A horizontal line that spans the width of the containing View. This shape requires the <stroke> element to define the width of the line. |
"ring" | A ring shape. |
line和ring這兩個類型必須要通過stroke節點元素來指定線的寬度和顏色等信息,否者無法達到預期的顯示效果。
2> 只有當android:shape="ring"時,以下的屬性才被使用:
android:innerRadius
Dimension 內環的半徑,可直接使用尺寸值或者引用尺寸資源。
android:innerRadiusRatio
Float 內環半徑與環寬度的比例。例如,如果android:innerRadiusRatio="5",則內環半徑等于drawable寬度的5分之1,這個值會被android:innerRadius屬性值重寫。默認值為3。
android:thickness
Dimension 環的厚度,可直接使用尺寸值或者引用尺寸資源。
android:thicknessRatio
Float 環的厚度與環寬度的比例。例如,如果android:thicknessRatio="2",則環的厚度等于drawable的寬度除以2。這個值會被android:innerRadius屬性值重寫。默認值是9。
android:useLevel
Boolean 一般都應該使用false,否者有可能無法達到預期的顯示效果,除非它被當做LevelListDrawable來使用。
1.2 <corners>節點元素
用來為shape drawable創建圓角,僅適用于shape drawable是矩形時,即只有<shape>節點中android:shape屬性為rectangle時。
<corners>節點元素的常用屬性:
android:radius
Dimension 所有圓角的半徑,可直接使用尺寸值或者引用尺寸資源。每一個圓角的半徑值會被如下的屬性值重寫。
android:topLeftRadius
Dimension 左上圓角的半徑,可直接使用尺寸值或者引用尺寸資源。
android:topRightRadius
Dimension 右上圓角的半徑,可直接使用尺寸值或者引用尺寸資源。
android:bottomLeftRadius
Dimension 左下圓角的半徑,可直接使用尺寸值或者引用尺寸資源。
android:bottomRightRadius
Dimension 右下圓角的半徑,可直接使用尺寸值或者引用尺寸資源。
注意:圓角的半徑必須大于1,否則沒有圓角。如果你想要指定某個角不是圓角,一個解決方法是使用android:radius屬性去給圓角半徑設置一個大于1的默認值,然后重寫你不想是圓角的對應屬性值為0dp。
1.3 <gradient>節點元素
為shape drawable指定一種漸變顏色。它與<solid>節點元素是互相排斥的,其中solid表示純色填充,而gradient則表示漸變效果。
<gradient>節點元素的常用屬性:
android:angle
Integer 顏色漸變的方向,0 is left to right, 90 is bottom to top.必須是45的倍數. Default is 0.
android:type
Keyword 漸變圖案的類型,可用的有效值如下:
android:startColor
Color顏色漸變的開始顏色,為十六進制顏色值值或者引用Color資源。
android:endColor
Color顏色漸變的結束顏色,為十六進制顏色值值或者引用Color資源。
android:centerColor
Color顏色漸變的中間顏色,為十六進制顏色值或者引用Color資源。
android:centerX
Float (0 - 1.0) 相對X的漸變位置,默認值為0.5。在type為linear時而且只有centerColor被設置時該屬性才會起作用。
android:centerY
Float (0 - 1.0) 相對Y的漸變位置,默認值為0.5。在type為linear時而且只有centerColor被設置時該屬性才會起作用。
android:gradientRadius
Float 漸變的半徑,單位應該是像素點. 僅當android:type="radial"時有效.
android:useLevel
Boolean 一般為false,當Drawable作為LevelListDrawable使用時為true.
舉例說明:
例一:
顏色從透明到不透明漸變。
運行結果如下:
例二:
運行結果如下:
例三:
運行結果如下:
例四:
運行結果如下:
例五:
運行結果如下:
1.4 <solid>節點元素
這個節點元素表示純色填充,通過android:color即可指定shape drawable中填充的顏色。
1.5 <stroke>節點元素
描邊。
<stroke>節點元素的常用屬性:
android:width="2dp" 描邊的寬度。
android:color 描邊的顏色。
我們還可以把描邊弄成虛線的形式,設置方式為:
android:dashWidth="5dp" 表示'-'這樣一個橫線的寬度.
android:dashGap="3dp" 表示'-'之間隔開的距離。
1.6 <padding>節點元素
用來設置包含該shape drawable的View的padding
<padding>節點元素的常用屬性:
android:left
Dimension Left padding,可直接使用尺寸值或者引用尺寸資源。
android:top
Dimension Top padding,可直接使用尺寸值或者引用尺寸資源。
android:right
Dimension Right padding,可直接使用尺寸值或者引用尺寸資源。
android:bottom
Dimension Bottom padding,可直接使用尺寸值或者引用尺寸資源。
1.7 <size>節點元素
shape drawable的固有大小。
<size>節點元素的常用屬性:
android:height
Dimension shape drawable的固有高度,可直接使用尺寸值或者引用尺寸資源。
android:width
Dimension shape drawable的固有寬度,可直接使用尺寸值或者引用尺寸資源。
上面的兩個屬性表示的是shape drawable的固有寬高,但是一般來說它并不是shape drawable最終顯示的寬高,這個有點抽象,但是我們要明白,對于shape drawable來說它并沒有寬高的概念,作為View的背景shape drawable會自適應View的寬高。我們知道Drawable的兩個方法getIntrisicWidth和getIntrinsicHeight表示的是Drawable的固有寬高,對于有些Drawable比如BitmapDrawable,它的固有寬高就是圖片的尺寸;而對于shape drawable來說,默認情況下他是沒有固有寬高的概念的,這個時候方法getIntrisicWidthhe和getIntrinsicHeight會返回-1,但是如果通過<size>標簽來指定寬高信息,那么這個時候shape drawable就有了所謂的固有寬高;因此,總結來說,<size>標簽設置的寬高就是shape drawable的固有寬高,但是作為View的背景時,shape drawable還會被拉伸或者縮小為View的大小。
2 inset drawable
當一個View希望自己的背景比自己的實際區域小的時候,這非常有用。對應InsetDrawable類。
file location:
res/drawable/filename.xml
The filename is used as the resource ID.
compiled resource datatype:
Resource pointer to a InsetDrawable.
resource reference:
In Java: R.drawable.filename
In XML: @[package:]drawable/filename
語法:
<?xml version="1.0" encoding="utf-8"?>
<inset
xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/drawable_resource"
android:insetTop="dimension"
android:insetRight="dimension"
android:insetBottom="dimension"
android:insetLeft="dimension" />
2.1 <inset>節點元素:
<inset>節點元素的常用屬性:
android:drawable:
Drawable resource. Required. Reference to a drawable resource to be inset.
android:insetTop
Dimension. inset drawable與使用該inset drawable的View的頂部距離, as a dimension value or dimension resource
android:insetRight
Dimension. inset drawable與使用該inset drawable的View的右邊距離, as a dimension value or dimension resource
android:insetBottom
Dimension. inset drawable與使用該inset drawable的View的底部距離, as a dimension value or dimension resource
android:insetLeft
Dimension. inset drawable與使用該inset drawable的View的左邊距離, as a dimension value or dimension resource
3 layer list drawable
layer list drawable是一個管理著一個Drawable對象數組的LayerDrawable類的對象,數組中的每一個Drawable對象按照下標的順序進行繪制,因此數組中的最后一個Drawable對象被繪制在最上面。
每一個Drawable是通過一個<item>元素來表示,每一個<item>元素都定義在<layer-list> 元素中。
FILE LOCATION:
res/drawable/filename.xml
The filename is used as the resource ID.
COMPILED RESOURCE DATATYPE:
Resource pointer to a LayerDrawable.
RESOURCE REFERENCE:
In Java: R.drawable.filename
In XML: @[package:]drawable/filename
語法:
<?xml version="1.0" encoding="utf-8"?>
<layer-list
xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:drawable="@[package:]drawable/drawable_resource"
android:id="@[+][package:]id/resource_name"
android:top="dimension"
android:right="dimension"
android:bottom="dimension"
android:left="dimension" />
</layer-list>
一個layer list drawable中可以包含多個item,每個item表示一個Drawable。item的結構比較簡單,比較常用的屬性有android:top、android:right、android:bottom和android:left,他們分別表示Drawable相對于View的上下左右的偏移量,單位為像素。另外,我們可以通過android:drawable來引用一個已有的Drawable資源,當然也可以在item中自定義Drawable。
4 ripple drawable
該drawable是在api21(L)時被引進的,可以實現觸摸反饋的效果(波浪的效果)。對應RippleDrawable類,繼承LayerDrawable(即繼承layer list drawable的所有特點)。
4.1 針對ImageView的background屬性
下面三個例子是針對ImageView的background屬性的:
對于三個例子的總結(針對background屬性):
波浪最大區域是該View的外接圓
例一 If no child layers or mask is specified and the ripple is set as a View background , the ripple will be drawn atop the first available parent background within the View's hierarchy. In this case, the drawing region may extend outside of the Drawable bounds.
我的翻譯 :在ripple中沒有item子節點或者包含有一個id被指定為@android:id/mask的item并且將該ripple drawable設置為View的background屬性值時,波浪動畫將會繪制在視圖層級結構中第一個可見的該View的父視圖之上,在這種情況下,波浪動畫可能會越過View區域,波浪動畫最大區域是該View區域的外接圓,但是不會超過第一個可見父視圖的區域。
舉例代碼如下:
/TestDrawable/res/layout/fragment_ripple.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_margin="30dp"
android:background="@drawable/beauty2" >
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@drawable/iv_background"
android:clickable="true"
android:layout_marginTop="30dp"
android:padding="30dp"
android:src="@drawable/beauty4" />
</LinearLayout>
</LinearLayout>
/TestDrawable/res/drawable/iv_background1.xml
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#ff00ff00" >
</ripple>
運行結果如下:
例二
在例一的基礎上在ripple根節點上添加一個item項,相當于ripple drawable當前擁有一個Drawable圖層并且當將該ripple drawable設置為View的background屬性值時該Drawable圖層可以限制波浪動畫不會越過View區域,波浪動畫將被繪制在該View區域上,在這種情況下,波浪動畫不會越過View區域,波浪最大區域是該View區域的外接圓,但是波浪動畫不會越過View區域。
舉例代碼如下:
/TestDrawable/res/layout/fragment_ripple.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_margin="30dp"
android:background="@drawable/beauty2" >
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@drawable/iv_background2"
android:clickable="true"
android:layout_marginTop="30dp"
android:padding="30dp"
android:src="@drawable/beauty4" />
</LinearLayout>
</LinearLayout>
/TestDrawable/res/drawable/iv_background2.xml
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#ff00ff00" >
<item android:drawable="@android:color/black"/>
</ripple>
運行結果如下:
例三
在例二中添加的item節點中添加android:id="@android:id/mask",相當于當將該ripple drawable設置為View的background屬性值時該item節點定義的Drawable圖層僅僅是用來限制波浪動畫不會越過View區域。波浪動畫將被繪制在該View區域上,在這種情況下,波浪動畫不會越過View區域,波浪最大區域是該View區域的外接圓,但是波浪動畫不會超過View區域。
舉例代碼如下:
/TestDrawable/res/layout/fragment_ripple.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="30dp"
android:background="@drawable/beauty2"
android:orientation="vertical" >
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@drawable/iv_background3"
android:clickable="true"
android:layout_marginTop="30dp"
android:padding="30dp"
android:src="@drawable/beauty4" />
</LinearLayout>
</LinearLayout>
/TestDrawable/res/drawable/iv_background3.xml
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#ff00ff00" >
<item android:id="@android:id/mask"
android:drawable="@android:color/black"/>
</ripple>
運行結果如下:
4.2 針對ImageView的src屬性的
下面三個例子是針對ImageView的src屬性的:
對于三個例子的總結(針對src屬性):
波浪最大區域是該View內容的外接圓
例一:
在ripple中沒有item子節點或者包含有一個id被指定為@android:id/mask的item并且將該ripple drawable設置為View的src屬性值時,波浪動畫將會繪制在該View區域上,在這種情況下,波浪動畫可能會越過View內容區域,波浪動畫最大區域是該View內容區域的外接圓,但是不會超過View區域。
舉例代碼如下:
/TestDrawable/res/layout/fragment_ripple.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="30dp"
android:background="@drawable/beauty2"
android:orientation="vertical" >
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@drawable/beauty4"
android:clickable="true"
android:layout_marginTop="30dp"
android:padding="30dp"
android:src="@drawable/iv_background1" />
</LinearLayout>
</LinearLayout>
/TestDrawable/res/drawable/iv_background1.xml
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#ff00ff00" >
</ripple>
運行結果如下:
例二:
在例一的基礎上在ripple根節點上添加一個item項,相當于ripple drawable當前擁有一個Drawable圖層并且當將該ripple drawable設置為View的src屬性值時該Drawable圖層可以限制波浪動畫不會越過View內容區域,波浪動畫將被繪制在該View內容區域上,在這種情況下,波浪動畫不會越過View內容區域,波浪最大區域是該View內容區域的外接圓,但是波浪動畫不會越過View內容區域。
舉例代碼如下:
/TestDrawable/res/layout/fragment_ripple.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="30dp"
android:background="@drawable/beauty2"
android:orientation="vertical" >
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@drawable/beauty4"
android:clickable="true"
android:layout_marginTop="30dp"
android:padding="30dp"
android:src="@drawable/iv_background2" />
</LinearLayout>
</LinearLayout>
/TestDrawable/res/drawable/iv_background2.xml
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#ff00ff00" >
<item android:drawable="@android:color/black"/>
</ripple>
運行結果如下:
例三
在例二中添加的item節點中添加android:id="@android:id/mask",相當于當將該ripple drawable設置為View的src屬性值時該item節點定義的Drawable圖層僅僅是用來限制波浪動畫不會越過View內容區域。波浪動畫將被繪制在該View內容區域上,在這種情況下,波浪動畫不會越過View內容區域,波浪最大區域是該View內容區域的外接圓,但是波浪動畫不會超過View內容區域。
舉例代碼如下:
/TestDrawable/res/layout/fragment_ripple.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="30dp"
android:background="@drawable/beauty2"
android:orientation="vertical" >
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@drawable/beauty4"
android:clickable="true"
android:layout_marginTop="30dp"
android:padding="30dp"
android:src="@drawable/iv_background3" />
</LinearLayout>
</LinearLayout>
/TestDrawable/res/drawable/iv_background3.xml
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#ff00ff00" >
<item android:id="@android:id/mask"
android:drawable="@android:color/black"/>
</ripple>
運行結果如下:
5 state list drawable
state list drawable是一個管理著一個Drawable對象集合的StateListDrawable類的對象。集合中的每一個Drawable對象對應View的一種狀態,這樣系統會根據View的狀態來選擇合適的Drawable。
FILE LOCATION:
res/drawable/filename.xml
The filename is used as the resource ID.
COMPILED RESOURCE DATATYPE:
Resource pointer to a StateListDrawable.
RESOURCE REFERENCE:
In Java: R.drawable.filename
In XML: @[package:]drawable/filename
語法:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
android:constantSize=["true" | "false"]
android:dither=["true" | "false"]
android:variablePadding=["true" | "false"] >
<item
android:drawable="@[package:]drawable/drawable_resource"
android:state_pressed=["true" | "false"]
android:state_focused=["true" | "false"]
android:state_hovered=["true" | "false"]
android:state_selected=["true" | "false"]
android:state_checkable=["true" | "false"]
android:state_checked=["true" | "false"]
android:state_enabled=["true" | "false"]
android:state_activated=["true" | "false"]
android:state_window_focused=["true" | "false"] />
</selector>
5.1 <selector>節點元素:
Required 該節點元素一定被作為根元素。包含一個或多個<item>元素。
<selector>節點元素的常用屬性:
android:constantSize
state list drawable的固有大小是否不隨著View狀態的改變而改變,因為View狀態會導致
state list drawable切換到具體的Drawable,而不同的Drawable具有不同的固有大小。
true表示state list drawable的固有大小保持不變,這時state list drawable的固有大小是
內部所有Drawable的固有大小的最大值,false則會隨著View狀態的改變而改變。
此選項的默認值是false。
android:dither
是否開啟抖動效果,開啟此選項可以讓圖片在低質量的屏幕上仍然獲得較好的顯示效果。
此選項默認值為true。
android:variablePadding
state list drawable的padding表示是否隨著View狀態的改變而改變,true表示會隨著View狀態的改變而改變,false表示state list drawable的padding是內部所有Drawable的padding的最大值。此選項默認值為false,并且不建議開啟此選項。
5.2 <item>節點元素:
通過屬性來描述在某個狀態下使用某個drawable。必須是<selector>節點的子節點。
<item>節點元素的常用屬性:
android:drawable:
Drawable resource. Required.引用一個drawable resource.
android:state_pressed
Boolean. 如果屬性值為true,則使用該state list drawable的組件被按下時該item被使用;如果屬性值為false,則使用該state list drawable的組件沒有被按下時該item被使用;
android:state_focused
Boolean. 如果屬性值為true,則使用該state list drawable的組件獲取input focus時該item被使用;如果屬性值為false,則使用該state list drawable的組件沒有獲取input focus時該item被使用;
android:state_selected
Boolean. 如果屬性值為true,則使用該state list drawable的組件被選擇時該item被使用;如果屬性值為false,則使用該state list drawable的組件沒有被選擇時該item被使用。例如當在幾個tab間切換時,被選中的tab和沒有被選中的tab的背景顏色設置為不同的
github上的PagerSlidingTabStrip應該利用到該屬性。
android:state_checkable
Boolean. 如果屬性值為true,"true" if this item should be used when the object is checkable; "false" if this item should be used when the object is not checkable. (Only useful if the object can transition between a checkable and non-checkable widget.)
android:state_checked
Boolean. 如果屬性值為true,則使用該state list drawable的組件被checked時該item被使用;如果屬性值為false,則使用該state list drawable的組件沒有被checked時該item被使用。
android:state_activated
Boolean. "true" if this item should be used when the object is activated as the persistent selection (such as to "highlight" the previously selected list item in a persistent navigation view); "false" if it should be used when the object is not activated.
Introduced in API level 11
注意:Android系統會從上到下進行查找,直到查找到第一條匹配的item,并且如果state list drawable中的某一項沒有設置狀態屬性,則該項會被應用每一次,因此默認Item(沒有設置狀態屬性)應該放到最后一項。
nine patch drawable
9-patch圖像是一種特殊格式的文件,因為Android知道圖像的哪些部分可以拉伸縮放,哪些部分不可以。經適當處理后,可保證背景圖的邊角與工具創建的圖像保持一致性。
為什么要叫做9-patch呢? 9-patch可將圖像分成3× 3的網格,即由 9部分或9 patch組成的網格。網格角落的patch不會被縮放,邊緣部分的4個patch只按一個維度縮放,而中間部分則同時按兩個維度縮放,如下圖所示:
9-patch圖像和普通的png圖像基本相同,但以下兩點除外: 9-patch圖像文件名是以.9.png結尾的,圖像邊緣具有一個像素寬度的邊框,頂部以及左邊框黑線用來標記圖像的可伸縮區域,底部以及右邊框黑線用于標記9-patch圖像的可填充內容區域。 可填充內容區域是內容(通常是文字)繪制的地方。若不設置可填充內容區域,則默認與可拉伸區域保持一致。對應NinePatchDrawable類。可通過如下兩種方式使用9-path文件。
1 直接使用本地的9-path資源文件
file location:
res/drawable/filename.9.png
The filename is used as the resource ID.
compiled resource datatype:
Resource pointer to a NinePatchDrawable.
resource reference:
In Java: R.drawable.filename
In XML: @[package:]drawable/filename
舉例如下:
<Button
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:background="@drawable/myninepatch" />
2 通過xml文件間接使用本地9-path資源文件
file location:
res/drawable/filename.xml
The filename is used as the resource ID.
compiled resource datatype:
Resource pointer to a NinePatchDrawable.
resource reference:
In Java: R.drawable.filename
In XML: @[package:]drawable/filename
語法:
<?xml version="1.0" encoding="utf-8"?>
<nine-patch
xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@[package:]drawable/drawable_resource"
android:dither=["true" | "false"] />
< nine-patch>節點元素:
< nine-patch>節點元素的常用屬性:
android:src:
Drawable resource. Required.引用一個Nine-Patch File
android:dither
當位圖不具有與屏幕相同的像素結構時,是否啟用或禁用抖動效果(關于抖動效果的說明可以參考預備知識部分)。
舉例如下:
<?xml version="1.0" encoding="utf-8"?>
<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/myninepatch"
android:dither="false" />
自定義Drawable
Drawable的使用方式很單一,一種方式用是作View背景的(也是大多數情況下),另一種方式是作為ImageView的內容顯示。在我的另一篇文章Android自定義View中再講解View的繪制過程中提到過繪制View的第一步就是繪制背景,下面看一下View類中繪制背景的源碼:
private void drawBackground(Canvas canvas) {
final Drawable background = mBackground;
if (background == null) {
return;
}
setBackgroundBounds();
// Attempt to use a display list if requested.
if (canvas.isHardwareAccelerated() && mAttachInfo != null
&& mAttachInfo.mHardwareRenderer != null) {
mBackgroundRenderNode = getDrawableRenderNode(background, mBackgroundRenderNode);
final RenderNode renderNode = mBackgroundRenderNode;
if (renderNode != null && renderNode.isValid()) {
setBackgroundRenderNodeProperties(renderNode);
((DisplayListCanvas) canvas).drawRenderNode(renderNode);
return;
}
}
final int scrollX = mScrollX;
final int scrollY = mScrollY;
if ((scrollX | scrollY) == 0) {
background.draw(canvas);
} else {
canvas.translate(scrollX, scrollY);
background.draw(canvas);
canvas.translate(-scrollX, -scrollY);
}
}
void setBackgroundBounds() {
if (mBackgroundSizeChanged && mBackground != null) {
mBackground.setBounds(0, 0, mRight - mLeft, mBottom - mTop);
mBackgroundSizeChanged = false;
rebuildOutline();
}
}
通過上面的源碼可以很容易的看出View背景的繪制過程:由于Drawable是沒有大小的概念的,所以在繪制背景之前先通過setBackgroundBounds方法設置Drawable的邊界,然后調用Drawable的draw方法繪制背景。
所以在自定義Drawable時要實現Drawable類中的draw方法,下面我實現了一個顯示圓形的自定義Drawable(CicleDrawable),并且CicleDrawable的大小會隨著View的變化而變化(有上面setBackgroundBounds方法的源碼得知)。CicleDrawable的實現代碼如下:
package com.cytmxk.test.drawable.custom;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
/**
* Created by chenyang on 16/8/13.
*/
public class CicleDrawable extends Drawable {
private Paint paint;
public CicleDrawable() {
this.paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(0xFF00FF00);
}
@Override
public void draw(Canvas canvas) {
final Rect r = getBounds();
canvas.drawCircle(r.exactCenterX(), r.exactCenterY(), Math.min(r.exactCenterX(), r.exactCenterY()), paint);
}
@Override
public void setAlpha(int alpha) {
paint.setAlpha(alpha);
invalidateSelf();
}
@Override
public void setColorFilter(ColorFilter colorFilter) {
paint.setColorFilter(colorFilter);
invalidateSelf();
}
@Override
public int getOpacity() {
// not sure, so be safe
return PixelFormat.TRANSLUCENT;
}
}
上面的代碼我是參考ShapeDrawable類的源碼,所以說源碼是一個很好的學習資料,有些技術細節我們不清楚,我們就可以查看源碼中類似功能的實現,這樣就會有針對性的解決一些問題。
Color State List Resource
Color State List Resource是一個管理著一個color集合的ColorStateList對象,每一個color對應著一個狀態,因此當你將其作為color值供View 對象使用時,Android系統會根據View對象的當前狀態來選擇合適的color。例如,一個按鈕控件可以存在于幾種不同的狀態(pressed, focused, or niether),使用Color State List Resource可以為這些狀態提供不同的顏色。
file location:
res/color/filename.xml
The filename will be used as the resource ID.
compiled resource datatype:
Resource pointer to a ColorStateList.
resource reference:
In Java: R.color.filename
In XML: @[package:]color/filename
語法:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:color="hex_color"
android:state_pressed=["true" | "false"]
android:state_focused=["true" | "false"]
android:state_selected=["true" | "false"]
android:state_checkable=["true" | "false"]
android:state_checked=["true" | "false"]
android:state_enabled=["true" | "false"]
android:state_window_focused=["true" | "false"] />
</selector>
1 <selector>節點元素:
Required 該節點元素一定被作為根元素。包含一個或多個<item>元素。
2 <item>節點元素:
通過屬性來描述在某個狀態下使用某個color值。必須是<selector>節點的子節點。
<item>節點元素的常用屬性:
android:color:
16進制的color值. Required. 16進制的color值是由一個RGB值和可選的透明值組成。16進制的color值必須以字符'#'開始并且是下面格式的一種:
#RGB
#ARGB
#RRGGBB
#AARRGGBB
android:state_pressed
Boolean. 如果屬性值為true,則使用該Color State List的組件被按下時該item被使用;如果屬性值為false,則使用該Color State List的組件沒有被按下時該item被使用;
android:state_focused
Boolean. 如果屬性值為true,則使用該Color State List的組件獲取input focus時該item被使用;如果屬性值為false,則使用該Color State List的組件沒有獲取input focus時該item被使用;
android:state_selected
Boolean. 如果屬性值為true,則使用該Color State List的組件被選擇時該item被使用;如果屬性值為false,則使用該Color State List的組件沒有被選擇時該item被使用。例如當在幾個tab間切換時,被選中的tab和沒有被選中的tab的背景顏色設置為不同的
github上的PagerSlidingTabStrip應該利用到該屬性。
android:state_checkable
Boolean. "true" if this item should be used when the object is checkable; "false" if this item should be used when the object is not checkable. (Only useful if the object can transition between a checkable and non-checkable widget.)
android:state_checked
Boolean. 如果屬性值為true,則使用該Color State List的組件被checked時該item被使用;如果屬性值為false,則使用該Color State List的組件沒有被checked時該item被使用。
android:state_activated
Boolean. "true" if this item should be used when the object is activated as the persistent selection (such as to "highlight" the previously selected list item in a persistent navigation view); "false" if it should be used when the object is not activated.
Introduced in API level 11
注意:Android系統會從上到下進行查找,直到查找到第一條匹配的item,并且如果Color State List Resource中的某一項沒有設置狀態屬性,則該項會被應用每一次,因此默認Item(沒有設置狀態屬性)應該放到最后一項。
舉例如下:
XML file saved at res/color/button_text.xml:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:color="#ffff0000"/> <!-- pressed -->
<item android:state_focused="true"
android:color="#ff0000ff"/> <!-- focused -->
<item android:color="#ff000000"/> <!-- default -->
</selector>
This layout XML will apply the color list to a View:
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/button_text"
android:textColor="@color/button_text" />