【Android】attr、style和theme

一、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的系統主題總結

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,563評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,694評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,672評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,965評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,690評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,019評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,013評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,188評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,718評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,438評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,667評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,149評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,845評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,252評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,590評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,384評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內容