看完後徹底搞清楚Android中的 Attr 、 Style 、Theme

sydMobile發表於2018-05-02

相信這三個詞對於Android開發者來說是十分熟悉了,那麼你對他們到底有多瞭解呢?

回憶起我剛開始接觸Android的時候對這三個詞有一些迷惑,有些時候只知道一些基本的使用,總之是有迷惑把。不能說的很清楚!

今天就來仔細說說這三個詞,總的說來 attr、style、theme都是用來表達樣式風格的,只是範圍不一樣。下面我們來具體的一個一個的說明。

本來寫的時候沒想多會牽扯這麼多內容,因為在寫的過程,考慮到很細,想要寫的儘可能全面,讓“0基礎的朋友”也可以看懂,所有寫著寫著就很長了,建議收藏後慢慢看!耐心看到最後!

也歡迎大家關注我的公眾號:Android開發者家園
你可以通過公眾號新增我為好友,一起交流!

有什麼問題也可以加我好友交流

360截圖20180417174125295.jpg

Attr基本概念

Attr :單詞的意思是屬性的意思(但是這裡的屬性和xml控制元件中的屬性不是一個意思,不要混淆!Attr說是屬性只是說的是它的單詞的意思是屬性),我們是通過Attr檔案來定義我們控制元件中所使用的屬性的,這樣說可能大家會有一迷惑,那麼來舉個栗子:

比如我們在控制元件中最多使用的 layout_width 屬性

layout_width

layout_width

這個屬性就是在Attr裡面定義的,那麼如何來查詢這個屬性呢?看圖片

Attribute位置

Attribute位置

View屬性

看到上面的圖片,我們可以看到在Android的sdk中給我們建立了一個attr檔案, 這裡面就是定義了我們在控制元件中用到的屬性。我們再到Android的SDK給我們定義的 attr 檔案中去看看

圖片view

view

view

view

上面的屬性是為view這個控制元件定義的屬性,看註釋介紹的很清楚,這是為View和他的子類提供的屬性組,也就是說這裡面的屬性都可以用在view和它的子類中,使用。看到這裡面的屬性是不是很眼熟啊,這裡面的屬性是不是我們在寫佈局的時候都有用到過啊。

TextView屬性

再來看看Android SDK為TextView定義的屬性組

TextView 屬性

等等,我們用到的控制元件在這裡都有對應的屬性進行宣告。看到這裡是不是明白了attr的意思了,attr其實就是一個檔案,這裡面定義了我們的控制元件中所使用的屬性。

具體的說一下attr的一些知識

如何定義attr?

我們首先來看看Android的官方屬性是如何進行定義了

TextView屬性定義

我們看到首先宣告瞭一組屬性,取了一個名字叫 TextView,然後在這裡面定義了一系列的屬性。

我在這裡總結了屬性的定義格式:


 1   <declare-styleable name="TextView">
         2  <attr  name ="屬性1" format = "這個屬性的取值格式">
            3  <enum name="取值1" value="程式中對應的值"/>
               <enum name="取值1" value="程式中對應的值"/>
               <enum name="取值1" value="程式中對應的值"/>
               <enum name="取值1" value="程式中對應的值"/>
            4  <flag name="取值1" value="程式中對應的值" />
               <flag name="取值2" value="程式中對應的值" />
               <flag name="取值3" value="程式中對應的值" />
    </declare-styleable>
複製程式碼

其中3和4是可以省略的, format也是可以省略的(我們之所以自定義屬性,一般就是在自定義View中使用,如果省略了format的話只是在佈局中我們使用這個屬性的時候沒有提示,只要在佈局中填的屬性內容和你的 java 檔案的取值對應就沒問題,不過還是建議format要定義好,這樣更清晰不容易亂)3就是我們提前給這個屬性設定了幾個值,可以直接在這幾個值中取。與4的區別就是:flag可以在佈局檔案中這樣使用 取值1|取值2 也就是說可以取多個值。
例子:

layout_width定義

這裡就是我們常用的layout_width的定義方式:看到我們可以將layout_width的值設定為fill_parent或者match_parent或wrap_content或者自己填寫大小。

而textStyle 我們的取值就可以是多個了,就不再多介紹了。 好了,下面我們可以自己來定義屬性了。

自定義屬性

重點來介紹format裡面的一些值
fraction:百分數
例子: 使用:
app:xshow = "70%"
reference : 指定某一資源ID
例子:
使用 : app:backgroundresourece = "@drawable/id"
別的格式基本上就是見名知意,就不再介紹了。

屬性的取值

在某些情況下,我們可能想讓某個屬性取另一個屬性的值,這樣說可能不太好懂。看例子!

test_attr

上圖是我自己定義的一個屬性,在我的佈局中有一個TextView,我想讓Text的內容取test_attr的內容,怎麼辦呢?

textView中使用test_attr屬性

沒錯就是 ?attr/屬性名字 這樣就是取 test_attr這個屬性的值,如果test_attr是 android裡面的attr值呢?那麼引入方式就是 :?android:attr/屬性名字 或者 省去attr
以上的操作都有一個前提:

attr的值只有在theme中被賦值才有效(否則這樣取值的結果就是程式報錯,注意在有些主題中有些屬性給了預設值,這個時候引用就沒有錯,但是如果沒有預設值,而你又沒有在主題中給定義上那樣就會出錯了),也就是說必須在清單檔案中的 Application或者 Activity中設定 Theme,並且Theme指向的屬性有值才可以引用attr的值,在單獨的控制元件中使用 android:Theme 或者 app:theme新增樣式是沒有用的!

obtainStyledAttributes詳細說明

在自定義View中獲取我們自定義的attr的值,一般大家都會這樣寫:

TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.屬性組名稱, defStyleAttr, defStyleRes);
a.getXX(R.styleable.XX_xxx);
a.recycle();
複製程式碼

關於方法obainStyledAttributes()

obainStyledAttributes

我們可以看到這個方法是存在在Context中的方法,最終呼叫的是getTheme()裡面的方法,所以我們有的時候看到 context.obainStyledAttributes和context.getTheme().obainStyledAttributes是其實一樣的。

下面來仔細說說這個方法:

我們可以看到

      public final TypedArray obtainStyledAttributes(AttributeSet set, @StyleableRes int[] attrs) {
	        return getTheme().obtainStyledAttributes(set, attrs, 0,0);
	  }  

複製程式碼

最終是呼叫的是這個

    public TypedArray obtainStyledAttributes(AttributeSet set,@StyleableRes int[] attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {

			return mThemeImpl.obtainStyledAttributes(this, set, attrs, defStyleAttr, defStyleRes);
	}
複製程式碼

我們就來說一說這個方法簡單來說這個方法就是返回一個TypedArray物件(理解為用來存放屬性值的陣列),裡面的引數
set:一個和xml中的標籤關聯的存放屬性的集合,int[] 就是我們要在xml中讀取的屬性。

defStyleAttr:當前主題中的一個屬性,其中包含對為TypedArray提供預設值的樣式資源的引用。可以為0以不尋找預設值。

defStyleRes:是具體的style資源。為TypedArray提供預設值的樣式資源的資源識別符號,僅當defStyleAttr為0或在主題中找不到時才使用。 可以為0以不尋找預設值。

這麼說起來可能有點迷糊,來一個例子保證你立馬領悟!

首先我在attrs中定義一組屬性

圖片myview1定義屬性

其中的 attr_defStyle 屬性名,就是我定義的defStyleAttr用obtainStyledAttributes中作為引數。

然後自己定義style和Theme

Theme

圖片 stylemy

我在上面的註釋中已經寫得很清楚了,就不多解釋了。

然後再自定義View

圖片自定義view

其中第一個構造方法一般就是Java程式碼中new 控制元件的使用,第二個構造方法就是在解析xml中控制元件生成view的時候呼叫了。

可以看到我在 第二個構造方法中是這樣寫的
this(context, attrs, R.attr.attr_defStyle);
其中 R.attr.attr_defStyle 就是定義的 defStyleAttr,在自己定義的Theme中給它附了值(這樣就解釋了前面說的,它是當前主題中的一個屬性,包含了對style(style_attr_defStyleAttr)的引用)

R.style.style_defStyleRes就很好解釋了,就是一個style資源引用。

再來看看佈局頁面

xml佈局檔案

在佈局檔案中給它設定了text1屬性,和style,我們來看一看程式執行的結果

預設屬性全部全了

看到這個執行結果就得出結論了,關於屬性的取值是由順序的

1.優先取在佈局中給定的值
2.在佈局中設定的style中的值
3.從defStyleAttr和defStyleRes中取值,注意如果 defStyleAttr有值,則不再去defStyleResult中的值,就算defStyleAttr有的屬性沒有賦值。(具體看上面的列印結果)
4.Theme中設定的屬性

注意 defStyleAttr的值一定要在Theme中設定才有效果,就拿上面的例子說,如果你沒有在Theme中給R.attr.attr_defStyle賦值,而是直接在佈局檔案中賦值,這樣做是沒有效果的。

做了上面的介紹,我們再來看看系統是怎麼做的,隨便看一個button控制元件

button構造方法

我們看到 button 的構造方法的defStyleAttr傳的是com.android.internal.R.attr.buttonStyle屬性,這個屬性我們在系統的attr中找到

buttonstyle

這就是系統定義的預設屬性buttonStyle

我們再來看看系統Theme是怎麼給它附的值

buttonTheme

這裡給了一個指引,指向了一個style

buttonstyle1

所以我們的button就在不同的主題中有了預設的樣式。看看系統的定義是不是和我們定義的很相像啊! 現在清楚了嗎?建議還是多看看原始碼。

原創不易,希望大家能夠尊重原創!

小知識點

<style name="MyStyle" parent="Widget.Button">
   <item name="background">@drawable/btn_default_holo_light</item>
   <item name="textAppearance">?attr/textAppearanceMediumInverse</item>
   <item name="textColor">@color/primary_text_holo_light</item>
   <item name="minHeight">48dip</item>
   <item name="minWidth">64dip</item>
</style>

複製程式碼

其中parent可以理解為 MyStyle 繼承自 Widget.Button這種繼承一般是繼承系統的,而自己繼承自己的style則是

<style name = "MyStyle.H">
   <item -----></item>
</style>

複製程式碼

是的這裡使用. 表示H 繼承自MyStyle

好了關於Attr的介紹就到這裡,本來想一篇文章全部介紹完,結果寫著寫著就寫多了,主要是考慮到許多基礎問題,想要大家都能看的懂就寫多了。剩下的style和theme估計篇幅也很大。寫起來發現牽扯的知識點太多了,什麼都想給大家介紹一下。

文章最早釋出於我的微信公眾號 Android開發者家園 中,歡迎大家掃描下面二維碼關注微信公眾獲取更多知識內容。
本文為sydMobile原創文章,可以隨意轉載,但請務必註明出處!

歡迎大家關注我的微信公眾號,和我交流分享

有什麼問題也可以加我好友交流

360截圖20180417174125295.jpg

相關文章