相信這個詞對于Android開發者來說十分熟悉了,那么你對他到底有多了解呢?
回憶起我剛開始接觸Android的時候對這三個詞有一些迷惑,有些時候只知道一些基本的使用,總之是有迷惑把。不能說的很清楚!
今天就來仔細說說這個詞。
本來寫的時候沒想多會牽扯這么多內容,因為在寫的過程,考慮到很細,想要寫的盡可能全面,讓“0基礎的朋友”也可以看懂,所有寫著寫著就很長了,建議收藏后慢慢看!耐心看到最后!
Attr基本概念
Attr :單詞的意思是屬性的意思(但是這里的屬性和xml控件中的屬性不是一個意思,不要混淆!Attr說是屬性只是說的是它的單詞的意思是屬性),我們是通過Attr文件來定義我們控件中所使用的屬性的,這樣說可能大家會有一迷惑,那么來舉個栗子:
比如我們在控件中最多使用的 layout_width 屬性
這個屬性就是在Attr里面定義的,那么如何來查找這個屬性呢?看圖片
View屬性
看到上面的圖片,我們可以看到在Android的sdk中給我們建立了一個attr文件,
這里面就是定義了我們在控件中用到的屬性。我們再到Android的SDK給我們定義的 attr 文件中去看看
上面的屬性是為view這個控件定義的屬性,看注釋介紹的很清楚,這是為View和他的子類提供的屬性組,也就是說這里面的屬性都可以用在view和它的子類中,使用。看到這里面的屬性是不是很眼熟啊,這里面的屬性是不是我們在寫布局的時候都有用到過啊。
TextView屬性
再來看看Android SDK為TextView定義的屬性組
等等,我們用到的控件在這里都有對應的屬性進行聲明。看到這里是不是明白了attr的意思了,attr其實就是一個文件,這里面定義了我們的控件中所使用的屬性。
具體的說一下attr的一些知識
如何定義attr?
我們首先來看看Android的官方屬性是如何進行定義了
我們看到首先聲明了一組屬性,取了一個名字叫 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的值設置為fill_parent或者match_parent或wrap_content或者自己填寫大小。
而textStyle 我們的取值就可以是多個了,就不再多介紹了。
好了,下面我們可以自己來定義屬性了。
重點來介紹format里面的一些值
fraction:百分數
例子:
使用:
app:xshow = “70%”
reference : 指定某一資源ID
例子:
使用 : app:backgroundresourece = “@drawable/id”
別的格式基本上就是見名知意,就不再介紹了。
屬性的取值
在某些情況下,我們可能想讓某個屬性取另一個屬性的值,這樣說可能不太好懂。看例子!
上圖是我自己定義的一個屬性,在我的布局中有一個TextView,我想讓Text的內容取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()
我們可以看到這個方法是存在在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中定義一組屬性
其中的 attr_defStyle 屬性名,就是我定義的defStyleAttr用obtainStyledAttributes中作為參數。
然后自己定義style和Theme
我在上面的注釋中已經寫得很清楚了,就不多解釋了。
然后再自定義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資源引用。
再來看看布局頁面
在布局文件中給它設置了text1屬性,和style,我們來看一看程序運行的結果
看到這個運行結果就得出結論了,關于屬性的取值是由順序的
1.優先取在布局中給定的值
2.在布局中設置的style中的值
3.從defStyleAttr和defStyleRes中取值,注意如果 defStyleAttr有值,則不再去defStyleResult中的值,就算defStyleAttr有的屬性沒有賦值。(具體看上面的打印結果)
4.Theme中設置的屬性
注意 defStyleAttr的值一定要在Theme中設置才有效果,就拿上面的例子說,如果你沒有在Theme中給R.attr.attr_defStyle賦值,而是直接在布局文件中賦值,這樣做是沒有效果的。
做了上面的介紹,我們再來看看系統是怎么做的,隨便看一個button控件
我們看到 button 的構造方法的defStyleAttr傳的是com.android.internal.R.attr.buttonStyle屬性,這個屬性我們在系統的attr中找到
這就是系統定義的默認屬性buttonStyle
我們再來看看系統Theme是怎么給它附的值
這里給了一個指引,指向了一個style
所以我們的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估計篇幅也很大。寫起來發現牽扯的知識點太多了,什么都想給大家介紹一下。
水平有限有什么問題可以指正批評