一、Attr
屬性,風格樣式的最小單元;
Attr 的定義
在自定義 View 的時候,在 res/attrs.xml 文件中聲明屬性,而Android 系統的屬性也是以同樣的方式定義的。
比如 layout_width 屬性對應到框架中的 attr 如下:
<declare-styleable name="ViewGroup_Layout">
<attr name="layout_width" format="dimension">
<enum name="fill_parent" value="-1" />
<enum name="match_parent" value="-1" />
<enum name="wrap_content" value="-2" />
</attr>
...
</declare-styleable>
attr 的 format 有以下幾種格式,可以進行或運算:
color、reference、boolean、dimension、enum、flag、float、fraction、integer、string
這里著重說一下 enum 是枚舉值,而 flag 可以進行或運算,屬性值可以疊加使用,
reference 用在一些可以設置引用值的情況,引用 res 資源
fraction 是百分數的意思
二、Style
風格,它是一系列Attr的集合,用以定義一個View的樣式;
style 是定義在 res/styles.xml 文件中的,在控件中使用時只需要style="@style/style_name"
就可以使用樣式了。
我們在自定義 View 時通過
·obtainStyledAttributes( AttributeSet set, @StyleableRes int[] attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes)
方法獲取自定義的 attr ,其中第一個參數表示是 xml 文件解析后得到的屬性集合,attrs 是 View 聲明的屬性集,后兩個用作指定默認的 Style,表示如果 set 中沒有你想獲得的屬性,但如果你指定了默認 Style,它會去從該默認的 Style 里面找你想要的屬性。defStyleAttr 和 defStyleRes 功能一樣,指定的資源形式不同,前者表示一個默認的指向一個 style 風格的 attr 屬性,而后者你可以直接傳入一個 style 風格的 id。
三、Theme
主題,它與Style作用一樣,都是一系列屬性的集合,不同于Style作用于一個單獨View,而它是作用于Activity上或是整個應用;與 Window 有關的屬性作用于 Window,與 View 有關的屬性作用于 Activity 的所有 View 或者整個應用的所有 View,如果想要改變 Window 的屬性,那么繼承相應 Theme,重寫屬性值,如果想要改變 View 相關樣式,在 Theme 中重寫屬性作用于整個 Activity 或整個應用,或者定義 style 作用于單個 View。
Theme 的實質也是 Style,Theme 的定義格式與 Style 的基本一致,Theme 需要設置到 AndroidManifest.xml 的 <application> 或者 <activity> 標簽下,設置后,被設置的 Activity 或整個應用下所有的 View 都可以使用該 <style> 里面的屬性了。
style 除過有 name 屬性以外還可以有 parent 屬性,一般在 style 中使用 parent 字段的繼承適用于繼承系統平臺現有定義的 style,想要繼承自己實現的 style 一般不會通過parent字段來實現,而是通過指定格式的 name 字段來實現。
Theme 中以 windowXXX 開頭的屬性就不適用于 View,但是不會報錯,只是 View 會忽略這些不適合自己的屬性,應用適合自己的屬性。
四、Theme 兼容性處理
Google官方提供Android Support Library package來保證高版本SDK的向下兼容。
- v4 Support Library
此包用在API lever 4(即Android 1.6)及更高版本之上。它包含了較多的內容,使用非常廣泛,例如:Fragment,NotificationCompat,LoadBroadcastManager,ViewPager,PageTabStrip,Loader,FileProvider 等。 - v7 Support Libraries
此包是針對API level 7(即Android 2.1)及以上版本而設計的,但是v7是要依賴v4這個包的,v7支持了Action Bar以及一些Theme的兼容。
v7 appcompat library 是包含在 v7 Support Libraries里面的一個包,正是此包增加了Action Bar 用戶界面的設計模式,并加入了對material design 的支持,是我們使用最多的一個兼容包。
Android 系統有三個基本主題:
- android:Theme
- android:Theme.Holo
- android:Theme.Material
- Theme.AppCompat
android:Theme是所有主題的超級父類。所有的主題都是直接繼承或者間接繼承它。
android:Theme.Holo 從 Api 11開始可以使用。android:Theme.Material 從 Api 21開始可以使用。
如果要在不同版本的系統上用各自的主題,比如在4.0之下的系統用android:Theme,4.0至5.0的系統用Holo主題,5.0及之后的系統使用Material Design,那我們需要建不同的value-vX目錄。在各自的目錄中的style繼承相應的系統主題。在運行是系統就會根據平臺版本使用相應的主題。
如果只創建一個 value 文件的話,那么系統的版本可能會不支持 value 中繼承的主題,系統就會根據 App 指定的 targetSdkVersion 自動設置主題,如果設置的 targetSdkVersion 還是高于系統的版本,系統就設置為支持的最高系統 sdk 版本的主題。
具體是怎么選擇的呢?我們來看 Resources 類中的源碼
public static int selectDefaultTheme(int curTheme, int targetSdkVersion) {
return selectSystemTheme(curTheme, targetSdkVersion,
com.android.internal.R.style.Theme,
com.android.internal.R.style.Theme_Holo,
com.android.internal.R.style.Theme_DeviceDefault,
com.android.internal.R.style.Theme_DeviceDefault_Light_DarkActionBar);
}
/** @hide */
public static int selectSystemTheme(int curTheme, int targetSdkVersion, int orig, int holo,
int dark, int deviceDefault) {
if (curTheme != 0) {
return curTheme;
}
if (targetSdkVersion < Build.VERSION_CODES.HONEYCOMB) {
return orig;
}
if (targetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
return holo;
}
if (targetSdkVersion < Build.VERSION_CODES.CUR_DEVELOPMENT) {
return dark;
}
return deviceDefault;
}
我們可以看到當 targetSdkVersion 低于11時,選擇 Theme 主題、當 targetSdkVersion 低于 14 時,選擇 Theme.Holo 主題,當 targetSdkVersion 高于 14 時,選擇 Theme.DeviceDefault,而 Theme.DeviceDefault 繼承自 Theme.Material。
如果我們想在 5.0 以下的系統中使用 Material 主題,只需要讓我們的系統主題繼承Theme.AppCompat即可。當然需要導入support-v7 appcompat 包,可以在sdk\extras\android\support\v7\appcompat 查看到。使用了 Theme.AppCompat 之后,就不受 targetSdkVersion 影響了。
AppCompatActivity(包括 ActionBarActivity)的子 Activity 必須使用 Theme.AppCompat 或 Theme.AppCompat 的子主題,如果不是,編譯時不會給出任何警告,但運行時會拋出異常。
五、Android 系統預制的 Theme 和 Style
我們想要知道系統的 Theme 有哪些屬性可以使用,系統為我們提供了哪些 Style 可以使用,可以到 sdk\platforms\android-24\data\res\values 文件夾下去查看 styles.xml 和 theme.xml 文件中去查看,以 Theme 為例,一個完整的主題需要定義顏色、Text 樣式、Button 樣式、List 屬性、Window 屬性、Dialog 屬性、AlertDialog 屬性、Panel 屬性、ScrollBar 屬性、文字選中屬性、Widget 樣式、Preference (設置類界面)樣式、Search Widget 樣式、ActionBar 樣式等。我們可以發現這些屬性的值有些是具體的值,有些是引用的 android:color、android:drawable 等系統資源,有些是引用的系統中所定義的樣式 android:style,有些則是直接引用系統主題中所定義的其它屬性?android:attr
1、我們可以根據這些屬性修改系統提供的控件樣式和窗體樣式;
2、我們可以引用系統主題的屬性值;
六、Android應用資源拓展語法
Theme 和 Style 的實質都屬于 res 資源,和其它資源類型如 drawable、layout、dimen 等的引用方式是一樣的。
Android 中資源在Java文件中引用的語法定義如下:
[包名.]R.<資源類型.><資源名>
//當資源在當前應用中則包名可以省略,當為系統的資源則可換為 android.
Android 資源在XML文件中引用的語法定義如下:
@[包名:]<資源類型/><資源名>
//當資源在當前應用中則包名可以省略,當為系統的資源則換為 android.
Android系統預制資源在XML文件中引用的特殊語法定義如下:
//可以引用系統所有資源,public & private
@*android:type/name
//只能引用系統public的資源
@android:type/name
Android在XML文件中引用當前主題屬性的語法定義如下:
資源值允許引用當前主題中的屬性的值,這個屬性值只能在style資源和XML中使用,隨著當前主題的切換該值也在變換
?[<包名>:][<資源類型>/]<資源名>
//如果是本應用中的attr使用,則可以省去<package_name>部分。
Android在XML文件中創建或者引用資源語法定義如下:
//在R.java的type內部類中添加一條靜態常量id資源標識符,如果標示符(包括系統資源)已經存在則表示引用該標示符。
@+type/name
//在R.java中尋找已經定義的標識符,如果找不到則提示失敗錯誤,一般在xml中定義有先后關系。
@type/name
Android在XML文件中xmlns語法定義如下:
xmlns:namespace-prefix=http://schemas.android.com/apk/res/應用程序包路徑
參考:
Attr、Style和Theme詳解
Android主題和樣式之系統篇(上)
Android主題和樣式之系統篇(下)
Android開發之Theme、Style探索及源碼淺析
Android關于Theme.AppCompat相關問題的深入分析
Android的系統主題總結