本文將要著重講解的Android資源大致可以分為三類:
1. values資源
- string 字串資源
- color 顏色資源
- dimen 尺寸資源
- array 陣列資源
- style 樣式資源
- theme 主題資源
2. drawable資源
- 圖片資源
- StateListDrawable資源
- LayerDrawable資源
- ShapeDrawable資源
- ClipDrawable資源
3.ColorStateList資源
--以下:正文部分-- Android的設計哲學為:設計與表現分離。 這樣有利於程式的解耦。所以我們才可以在XML檔案中定義各種資源型別,並在其他的xml檔案或java程式碼中進行引用。
1.1 String資源:
字串資源所對應的xml檔案位於/res/values/
目錄下。
其預設名為strings.xml
對應於R類中的內部類的名稱:R.string
檔案的根元素為resources
:
定義:
<resources>
<string name="app_name">Hello World</string>
<string name="button_name">Hello World</string>
<string name="text_name">Hi there</string>
</resources>
複製程式碼
引用:
一般我們都是在同一個包下的其他xml檔案中引用字串資源: 比如在TextView中引用之前定義的字串:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/text_name" />
複製程式碼
android:text="@string/text_name"
所表達的
正是引用同一包下字串資原始檔中名為text_name的字串資源。
當然,如果是引用不同包下的資源,可則只需在@和string之間加上包名。
事實上,在xml程式碼中使用資源的通用完整語法格式正是:
@[<package_name>:]<resource_type>/<resource_name>
其中中括號代表選填,尖括號代表必填。
1.2 Color資源:
與字串資源類似,我們可以事先在xml檔案中定義,並在之後對其進行引用。
顏色資源所對應的xml檔案位於/res/values/
目錄下。
其預設名為colors.xml
對應於R類中的內部類的名稱:R.color
檔案的根元素為resources
:
定義:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorGray">#aaa</color>
<color name="colorWhite">#ffffff</color>
<color name="colorBlack">#454647</color>
</resources>
複製程式碼
引用:
<TextView
...
android:textColor="@color/colorWhite"/>
複製程式碼
方法與對string資源的引用大同小異,不再贅述。
1.3 dimen資源:
dimen是dimension的縮寫,表示尺寸。如果我們的佈局中有多個view需要指定相同的尺寸,那麼我們可以事先在dimen資源中對該尺寸進行定義,之後便可以很方便地複用。
dimen資源所對應的xml檔案位於/res/values/
目錄下。
其預設名為dimens.xml
對應於R類中的內部類的名稱:R.dimen
檔案的根元素為resources
:
定義:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="text_view_height">60dp</dimen>
<dimen name="text_view_width">60dp</dimen>
</resources>
複製程式碼
引用:
<TextView
android:layout_width="@dimen/text_view_height"
android:layout_height="@dimen/text_view_width" />
複製程式碼
1.4 array資源:
陣列資源所對應的xml檔案位於/res/values/
目錄下。
其預設名為arrays.xml
對應於R類中的內部類的名稱:R.array
檔案的根元素為resources
:
不同的是,arrays.xml檔案中可以定義三種不同型別的子元素:
- 普通型別的陣列,比如Drawable陣列,用
<array.../>
來表示。 - 字串型別的陣列,用
<string-array.../>
來表示。 - 整型陣列,用
<integer-array.../>
來表示。
1.4.1 typedArray定義:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--陣列名-->
<array name="color_array">
<item>@color/colorPrimary</item>
<item>@color/colorAccent</item>
<item>@color/colorPrimaryDark</item>
<item>@color/colorBlack</item>
<item>@color/colorCyan</item>
<item>@color/colorGreen</item>
</array>
</resources>
複製程式碼
以上在/res/values/arrays.xml
中定義了一個普通型別的陣列。這種型別的陣列也叫做TypedArray,其中的陣列項可以定義Drawable物件等。
在陣列的每一項中都引用了/res/values/colors/
中定義的顏色資源。
接下來可以在java程式碼中對該陣列中的資源加以運用。比如我們可以在佈局檔案中定義一個文字框,再定義一個按鈕,點選按鈕實現文字框背景色的輪播:
public class MainActivity extends AppCompatActivity {
int counter = 0;
TextView tv;
Button bn;
TypedArray typedArray;
//注意:呼叫typedArray的getColor()方法時
//如果不加這個@SuppressWarning標籤就會報錯
@SuppressWarnings("ResourceType")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.text_view);
bn = (Button) findViewById(R.id.bn);
//通過getResources()方法獲取到Resources,並將引用賦給res
Resources res = getResources();
//向obtainTypedArray()方法中,傳入R.array.color_array
//返回一個TypedArray物件,命名typedArray
//裡面儲存的是<array name="color_array">陣列中的顏色資源
typedArray = res.obtainTypedArray(R.array.color_array);
bn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
int color = typedArray.getColor(counter%typedArray.length(), 0);
tv.setBackgroundColor(color);
counter++;
}
});
}
}
複製程式碼
效果
注:TypedArray在自定義view時也有應用,限於篇幅,本文不深入講解。
1.4.2 string-array定義:
<string-array name="list_items">
<item>Android</item>
<item>Ios</item>
<item>Swift</item>
<item>Java</item>
<item>C##</item>
<item>@string/text_content</item>
</string-array>
複製程式碼
方法是類似的。只不過根元素寫的是string-array
。
其中的字串既可以直接定義值(前5項),也可以引用事先定義好的字串(最後一項)。
應用
比如我們可以在佈局檔案中定義一個ListView,然後在其entries
屬性中引用該陣列:
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:entries="@array/list_items"/>
複製程式碼
效果
1.4.3 integer-array定義:
與string-array的定義類似,只是將string資源變成了integer型別的資源。
<integer-array name="int_array">
<item>2</item>
<item>4</item>
<item>8</item>
<item>16</item>
</integer-array>
複製程式碼
應用 簡單起見,我們同樣也可以在ListView中對該陣列進行引用:
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:entries="@array/int_array"/>
複製程式碼
效果
1.5 style資源:
style資源指的是Android的樣式資源。
同樣在/res/values/
目錄下定義
style資原始檔的根元素也是resources
。
resources
下可以包含多個<style.../>
子元素,每個style
子元素可以定義一個樣式,style
標籤可以指定兩個屬性:
name:
指定樣式的名稱;parent:
指定該樣式所繼承的父樣式。與java中的繼承類似:當繼承某個父樣式時,該樣式將會獲得父樣式中定義的全部樣式。同樣地,當前樣式也可以覆蓋父樣式中指定的格式。<style.../>
元素內可以包含多個<item.../>
子元素,每個都可以定義一個格式項。
<style name="樣式名" parent="@style/事先定義好的樣式名">
<!--可以包含多個item子項-->
<item>...</itme>
</style>
複製程式碼
舉例:
<style name="style1">
<item name="android:text">Button</item>
<item name="android:textAllCaps">false</item>
</style>
<style name="style2" parent="@style/style1">
<item name="android:background">#666</item>
<!--覆蓋父樣式中指定的屬性-->
<item name="android:textAllCaps">true</item>
</style>
複製程式碼
我們可以為兩個button分別指定定義的style1和style2:
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/style1"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/style2"/>
複製程式碼
效果:
可以看到,第二個button所引用的style2的parent屬性指定的是style1。 所以style2繼承了style1屬性。但是style2中也重寫了style1的“textAllCaps”屬性,所以第二個button所顯示的text預設為大寫。 當然,style2繼承了style1後,也可以定義自己屬性,如以上的<item name="android:background">#666</item>
。
如此一來,就可以事先定義好一組樣式的集合,然後將該style一次性應用給某個元件。
1.6 theme資源:
theme資源與style資源類似。
同樣在/res/values/
目錄下定義,根元素同樣是resource
,同樣用<style.../>
來定義。
區別在於:主題應該作用於整個應用中的所有Activity或者作用於某個指定的Activity。且主題影響的應該是視窗的標題、邊框等屬性:
<style name="my_theme">
<item name="android:windowFullscreen">true</item>
</style>
複製程式碼
使用該主題:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme(R.style.my_theme);
...
複製程式碼
預設情況下,活動的頂部:
設定
<item name="android:windowFullscreen">true</item>
複製程式碼
之後,活動的頂部:
如果想要讓應用中的所有視窗都應用剛才定義的my_theme
主題,則只需要在清單檔案中的<application.../>
元素下新增android:theme="@style/my_theme">
即可。
--以上:第一部分--
2.1 圖片資源:
圖片資源可謂是最簡單的drawable資源。只需要把Android認可的圖片資源(.png,.jpg,*.gif)放在/res/drawable-xxx
目錄下即可。Android SDK在編譯應用時會自動載入圖片資源,並在R類中生成對該資源的索引。如此,圖片資源就和values資源一樣,可以通過
@[<package_name>:]drawable/檔名
的方式在xml程式碼中被訪問了。
如果想要在java程式碼中訪問到實際的圖片Drawable物件,而不是R類中int型別的索引,可以利用Resources類提供的```Drawable getDrawable(int id)方法。該方法可以根據R類中的id獲取到實際的Drawable物件。
2.2 StateListDrawable資源 顧名思義,StateList就是一個state(狀態)的集合。它可以用來組織多個Drawable物件,並讓使用了該StateListDrawable的元件根據自身不同的狀態來自動切換至相應的Drawable。 定義:
- 在Drawable資料夾下,右鍵new一個新的drawable resource file
- 根元素為
selector
,可以理解為狀態選擇器 - 根元素下可以包含多個
<item.../>
並可以為其指定如下屬性:
- android:color 或 android:drawable: 指定顏色或者drawable物件
- android:state_xxx: 指定一個特定的狀態
舉例: 比如我們想讓一個button在按下時候和未被按下的時候的背景顏色不同,可以這樣寫:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!--true說明按下了-->
<item android:state_pressed="true" android:drawable="@color/colorCyan"/>
<item android:state_pressed="false" android:drawable="@color/colorPrimary"/>
</selector>
複製程式碼
在button中引用這個StateListDrawable:
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:background="@drawable/bn_state_list"
android:textColor="@color/colorWhite"
android:text="按下會變換背景色"
/>
複製程式碼
效果:
當然,以上只是StateListDrawable所支援的其中兩個狀態。 完整的狀態列表如下:屬性值 | 含義 |
---|---|
android:state_active | 表示是否處於啟用狀態 |
android:state_checkable | 表示是否處於可勾選狀態 |
android:state_checked | 表示是否處於已勾選狀態 |
android:state_enabled | 表示是否處於可用狀態 |
android:state_first | 表示是否處於開始狀態 |
android:state_focused | 表示是否處於已得到焦點狀態 |
android:state_last | 表示是否處於結束狀態 |
android:state_middle | 表示是否處於中間狀態 |
android:state_pressed | 表示是否處於被按下狀態 |
android:state_selected | 表示是否處於被選中狀態 |
android:state_window_focused | 表示視窗是否處於已得到焦點狀態 |
2.3 LayerDrawable資源
LayerDrawable顧名思義,就表現得和圖層差不多。可以在根元素layer-list
中定義多個drawable物件,並且像幀佈局那樣將各個物件堆疊起來。最後定義的物件處於最上面。
相同的時,根元素下同樣可以包含多個<item.../>
子項,並可以在其中定義drawable物件的引用。同時還可以設定top
,bottom
,right
以及left
屬性來設定堆疊時,drawable物件向各個方向的偏移量(offset)。
不同的是,<item.../>
中的各個子項除了指定偏移量之外,還可以指定id屬性。另外,根據官方說法:
預設情況下,所有可繪製項都會縮放以適應包含檢視的大小。因此,將影象放在圖層列表中的不同位置可能會增大檢視的大小,並且有些影象會相應地縮放。為避免縮放列表中的專案,請在 元素內使用 元素指定可繪製物件,並且對某些不縮放的專案(例如 "center")定義重力。例如,以下 定義縮放以適應其容器檢視的專案:
<item android:drawable="@drawable/image" />
比如我們要讓兩個圖示堆疊在一起並且在ImageView中顯示,可以這樣寫:
先定義一個layer_drawable.xml
檔案:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!--設定偏移量-->
<item
android:right="20dp"
android:top="20dp">
<bitmap android:src="@drawable/ic_launcher"
android:gravity="center"/>
</item>
<item>
<bitmap android:src="@drawable/ic_launcher_round"
android:gravity="center"/>
</item>
</layer-list>
複製程式碼
然後在ImageView中引用:
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="@drawable/layer_drawable"/>
複製程式碼
效果:
2.4 ShapeDrawable資源
簡單來說,Android的ShapeDrawable讓我們可以不用做圖就能實現各種簡單的幾何圖形,並能控制圓角、填充顏色、邊框、內邊距、半徑等各種屬性。這樣我們在為某個元件(比如TextView)指定背景時,就方便多了。
定義:
ShapeDrawable的根元素是<shape.../>
。 其中android:shape="
屬性有4中值可以選:line, rectangle, oval, ring。
舉例:
下面分別定義了兩個ShapeDrawable:
shape1.xml
" class="hljs "><shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<!--設定內邊距-->
<padding
android:bottom="4dp"
android:left="4dp"
android:right="4dp"
android:top="4dp" />
<!--設定填充顏色-->
<solid android:color="@color/colorBlack" />
<!--設定邊框-->
<stroke
android:width="2dp"
android:color="@color/colorAccent" />
<!--設定圓角矩形-->
<corners android:radius="8dp" />
</shape>
複製程式碼
和shape2.xml
:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<!--設定填充漸變-->
<gradient
android:angle="45"
android:endColor="@color/colorGreen"
android:startColor="@color/colorCyan"
android:type="linear" />
<stroke
android:width="2dp"
android:color="@color/colorPrimaryDark" />
<size
android:width="200dp"
android:height="100dp" />
</shape>
複製程式碼
然後將他們分別設定為兩個TextView元件的背景,效果如下:
當然,不止是TextView可以用ShapeDrawable作為背景,支援將drawable物件作為背景的所有元件都可以。其中各項屬性的名稱可謂見名知意,不再贅述。2.5 ClipDrawable資源:
ClipDrawable表示從其他點陣圖(注意是點陣圖)上clip(擷取)的一個圖片片段。
定義時的根元素是<clip.../>
。
總共可以指定三個屬性:
android:drawable
: 指定擷取的源點陣圖檔案;android:clipOrientation
: 指定擷取方向,可以指定水平(horizontal)擷取或者垂直(vertical)擷取;android:gravity
: 指定擷取時的對齊方式;可選的值為: top, bottom, left, right, center_vertical, fill_vertical, center_horizontal, fill_horizontal, center, fill, clip_vertical, clip_horizontal。 呼叫ClipDrawable物件的setLevel(int level)方法可以設定擷取區域的大小。level的範圍在[0,10000]。也就是說,當level=0時,一點都不擷取;當level=10000時,擷取整張圖片。
舉例:
比如我們可以藉助ClipDrawable和Timer類打造一個簡單的進度顯示圓:
先定義
my_clip.xml
:
<?xml version="1.0" encoding="utf-8"?>
<clip xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/circle"
android:clipOrientation="vertical"
android:gravity="bottom">
</clip>
複製程式碼
再在ImageView中引用my_clip
:
<ImageView
android:id="@+id/show_image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="@drawable/my_clip"/>
<Button
android:id="@+id/bn_show_progress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="點按以顯示進度"/>
複製程式碼
最後再java程式碼中進行設定:
public class MainActivity extends AppCompatActivity {
ImageView showImage;
Button bn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
showImage = (ImageView) findViewById(R.id.show_image);
bn = (Button) findViewById(R.id.bn_show_progress);
//獲取圖片所顯示的ClipDrawable物件
final ClipDrawable circle = (ClipDrawable) showImage.getDrawable();
final Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//如果訊息是本程式傳送的:
if (msg.what == 0x123) {
//修改ClipDrawable的level值
circle.setLevel(circle.getLevel() + 200);
int currentLevel = circle.getLevel();
}
}
};
//設定button的監聽器
bn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//設定bn不可被點選
bn.setEnabled(false);
final Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
Message msg = new Message();
msg.what = 0x123;
//傳送訊息,通知應用修改ClipDrawable物件的level值
handler.sendMessage(msg);
//取消定時器
if (circle.getLevel() >= 10000)
timer.cancel();
}
}, 0, 200);
}
});
}
}
複製程式碼
效果:
--以上:第二部分--3.1 ColorStateList資源
ColorStateList在好多書上都沒提到,但是卻是十分有用。
前面有提到StateListDrawable,它會根據不同的狀態來引用不同的drawable物件。但是改變的往往是背景色,對於文字顏色就愛莫能助了。
比如,我們想要讓一個button在被設定成enabled="false"
之後,背景色變為黑色,這很簡單:
<Button
android:id="@+id/bn_left"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="bn_left"
android:textAllCaps="false" />
<Button
android:id="@+id/bn_right"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="bn_right"
android:textAllCaps="false" />
複製程式碼
並且我們定義一個StaleListDrawable命名為bn_state_list
,使引用它的按鈕在不可使用時背景色變黑:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false" android:drawable="@color/colorBlack"/>
<item android:state_enabled="true" android:drawable="@color/colorCyan"/>
</selector>
複製程式碼
接下來在java程式碼中設定bn_right的監聽器,讓它被按下時,bn_left的enabled的屬性被設定為"false",也就是不可使用的狀態。 此時,我們會發現,非常尷尬的一幕發生了:
當左邊按鈕的背景色變黑之後,它上面文字的顏色卻沒有隨之改變,使用者體驗肯定會大打折扣。
這個時候ColorStateList就能派上用場了:
不同的是,這次我們不再在drawable資料夾上右擊新建了,而是再建立一個color資料夾,並在裡面新建名為button_text_color.xml
的檔案:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/colorWhite" android:state_enabled="false"/>
<item android:color="@color/colorBlack" android:state_enabled="true"/>
</selector>
複製程式碼
可以看到我們的根元素同樣是和StateListDrawable一樣的selector(選擇器),並且我們為按鈕的不同狀態指定了不同的文字顏色。接下來只需要引用這個檔案了:
<Button
...
android:background="@drawable/bn_state_list"
android:textColor="@color/button_text_color"
.../>
複製程式碼
可以看到,background和textColor引用的是不同的檔案。而使我們能隨狀態改變按鈕文字顏色的正是android:textColor="@color/button_text_color"
。
效果:
看別人寫總是很簡單,自己總結一遍才發現: 在寫程式碼的道路上,總有刁民想害朕。所以說,還是得親“歷”親為啊。 篇幅有限,水平有限。文中如有紕漏,歡迎批評指正。 諸君共勉 : )