天天看點

Android資源管理中的Theme和Style-------之總述(一)

        我們知道Android的每個View都會有許多不同的屬性,同樣的一個View,我們給它的屬性賦予不同的值,這個View就會有不同的效果。甚至可以說,Android的界面程式設計,很大程度上就是給不同的View屬性指派的過程。我們通過改變View的屬性值,來改變一個View的形态。比如我們通過改變layout_width和layout_height屬性的值來改變一個View的大小,通過過改變Text屬性的值來改變一TextView的内容,通過改變gravity屬性的值來改變一個View内部的布局方式等等。

        不過,正如現實世界是複雜的一樣,在實際應用當中,一個簡簡單單的TextView就有不下60個屬性,這還不包括從它的父類(View.java)繼承過來的。但是我們比較關心、經常改動的屬性也就那麼幾個,如果每次使用一個View都要我一一為這些屬性指派,我想我會瘋掉的。那該怎麼辦呢?

        我們就給每一個View的所有屬性都指定一個預設值吧,這些預設值放到哪兒呢?放到一個叫做theme的集合裡!這個集合當中的每一個元素都是一個key-value對兒。key表示View的屬性,value表示屬性的值,或者說是android中的某一種資源。比如我們給一個TextView的textColor屬性指定的值為android:color/white,這裡android:color/white就是一種資源。不過,随着應用越來越複雜,有時候光有一套甚至幾套預設值,或者說是theme還不夠,比如我們想改變某個TextView的所有屬性當中的某40個屬性的值,其它的還用預設值,你不能每次都叫我一個一個屬性去改變吧。于是,我就把這40個屬性和它們所對應的值放在一個集合裡,這個集合就叫style。

        其實,個人的了解,Theme和style并無本質上的差別,從資源管理的角度來看,它們都表示一種從View的某個屬性到Android的某項具體資源的映射關系。它們的差別在于,style是theme的一個子集,它不一定是全部的映射關系,可以是某一部分。theme是大而全的,style是短小靈活的,它們互相配合,使得我們可以友善,但又不會有遺漏地管理Android中View的屬性到資源的映射關系。

        我們知道Android裡面有一種叫做attr的資源,這種資源向上對應于View的各種屬性,向下對應于它們的具體值,也就是Android當中的某一項具體資源,比如一個顔色值或者一個字元串等。但嚴格來說,我覺得attr又說不上是一種Android資源,因為資源是被使用的,是使用的客體,但attr是使用者,是使用的主體。而且它的存在是和theme與style緊密結合的,在Android資源管理子產品中,Theme和AssetManager之間泾渭分明,而這個分界線就是attr。具體就是,一個attr可以引用另外一個attr,也就是在給一個attr指派的時候,可以給它賦另外一個attr。但是這種引用關系的層級不管有多深(android中attr的引用最多支援20級),一個attr經過theme解析後,Res_value的Type絕對不會是TYPE_ATTRIBUTE,我們看下面的這個屬性解析的流程圖:

Android資源管理中的Theme和Style-------之總述(一)

        圖中藍色的theme表示

ResTable::Theme

類,左邊五邊形中的ATTR表示Res_value的類型是attr(Res_value::TYPE_ATTRIBUTE);右邊五邊形中的RT表示

ResTable

類,REF表示Res_value的類型是資源的一個引用(Res_value::TYPE_REFERENCE)。當我們去擷取theme或者style中某個屬性的值的時候,我們會先從theme裡查找,找到後如果我們發現它的值的類型是Res_value::TYPE_ATTRIBUTE,也就是說它的值是另外一個屬性,而非最終結果,那麼我們就會根據這個屬性再次查找,如此循環往複,直到我們得到的值的類型不再是Res_value::TYPE_ATTRIBUTE為止。但這時候,我們得到的還有可能不是最終結果,因為這個值有可能是某項資源的引用(Res_value::TYPE_REFERENCE),而不是具體的值,那麼我們還需要在ResTable中循環往複地查找,直到它的值不再是Res_value::TYPE_REFERENCE類型的,而是最終值為止。在這個過程中,我們可以清晰地看到,

ResTable::Theme

類負責Res_value::TYPE_ATTRIBUTE類型的值的解析,它解析完後再抛給ResTable,此時它的值要麼是最終值,要麼是Res_value::TYPE_REFERENCE。如果是後者,那麼ResTable将負責把它的最終值解析出來。

        隻是這麼說可能比較抽象,舉個例子:

        在styles.xml檔案中

<style name="myStyle">
    <item name="myTextColor1">@?attr/myTextColor2</item>
    <item name="myTextColor2">@?attr/myTextColor3</item>
    <item name="myTextColor3">@?attr/myTextColor4</item>
    <item name="myTextColor4">@color/green1</item>
</style>
           

        而在colors.xml中

<resources>
    <color name="green1">@color/green2<\/color>
    <color name="green2">@color/green3<\/color>
    <color name="green3">#55008800<\/color>
</resources>
           

        假設我們要去擷取myTextColor1屬性的值,那麼

ResTable::Theme

類會第一次解析myTextColor1屬性;然後發現它的值是另外一個attr,也就是myTextColor2,那麼它會繼續解析myTextColor2;然後發現myTextColor2的值也是一個attr,myTextColor3,那麼它繼續解析myTextColor3;然後發現myTextColor3的值還是一個attr,myTextColor4,那麼它繼續解析myTextColor4;解析完myTextColor4,發現它的值不再是一個attr了,

ResTable::Theme

的工作才算完成了,然後它會把這個值交給

ResTable

類去進一步解析。

ResTable

發現這個值也不是一個具體的顔色值,而是一個資源的引用(其實就是green1在R檔案中的id),那麼它會去解析這個引用green1;解析完green1後,發現它的值還是一個資源的引用green2,那麼那麼它會去解析這個引用green2;解析完green2,

ResTable

類發現它的值還是一個資源的引用green3,那麼那麼它會去解析這個引用green3;解析完green3,

ResTable

類發現它是一個具體的顔色值#55008800,于是完成資源解析的任務,最終我們得到了屬性myTextColor1的最終值為#55008800。在這個過程中

ResTable::Theme

負責attr的解析,

ResTable

負責引用的解析,分工非常明确。

        我們在了解theme(或者style)和AssetManager(或者ResTable)的時候,要特别注意它們的差別:前者表示的是一種屬性到具體資源(或者說是使用者到被使用者)的映射關系;後者則是純粹表示各種資源。下一篇我們具體介紹theme和style的實作。