構(gòu)造方法
View的構(gòu)造方法有如下幾個灵奖,其中前三個方法是API 1即引入,這也是最常使用的構(gòu)造方法。但是 View(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)是API 21才引入,使用時需要注意兼容性問題。
/**
* Code中創(chuàng)建View時使用的構(gòu)造方法
* Simple constructor to use when creating a view from code.
*/
View(Context context)
/**
* 繪制Xml中View時使用的構(gòu)造方法
* Constructor that is called when inflating a view from XML.
*/
View(Context context,AttributeSet attrs)
/**
* Perform inflation from XML and apply a class-specific base style from a theme attribute.
*/
View(Context context, AttributeSet attrs, int defStyleAttr)
/**
* Perform inflation from XML and apply a class-specific base style from a theme attribute or style resource.
*/
View(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
View(Context context, AttributeSet attrs, int defStyleAttr)
日常開發(fā)中玩祟,使用較多的是前兩種構(gòu)造方法,相信開發(fā)者們一定比較熟悉屿聋。今天重點要介紹的是第三種構(gòu)造方法View(Context context, AttributeSet attrs, int defStyleAttr)中的第三個參數(shù)defStyleAttr卵凑。
參考官方文檔的參數(shù)詳解如下庆聘,乍一看無法理解defStyleAttr和defStyleRes的含義,只能看到二者的作用是為View提供一些屬性的默認值勺卢。
參數(shù)詳解:
context Context: The Context the view is running in, through which it can access the current theme, resources, etc.
attrs AttributeSet: The attributes of the XML tag that is inflating the view.This value may be null.
defStyleAttr int: An attribute in the current theme that contains a reference to a style resource that supplies default values for the view. Can be 0 to not look for defaults.
defStyleRes int: A resource identifier of a style resource that supplies default values for the view, used only if defStyleAttr is 0 or can not be found in the theme. Can be 0 to not look for defaults.
參考View的實現(xiàn)源碼伙判,上述幾個參數(shù)的作用是傳遞給context.obtainStyledAttributes來獲取TypedArray,進而解析出Attribute黑忱。后續(xù)開始關(guān)鍵參數(shù)的介紹:
public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
this(context);
final TypedArray a = context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.View, defStyleAttr, defStyleRes);
<!-- ...snipped for brevity... -->
}
AttributeSet參數(shù)
首先來理解XML中Attributes的定義宴抚,以ImageView為例:
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/icon"
/>
上述XML定義中的layout_width,layout_height和src就組成了AttributeSet對象甫煞,但是這些attribute定義是來自于哪里呢菇曲?其實系統(tǒng)在attrs.xml中通過<declare-styleable>聲明了這些attributes。源碼定義如下:
<declare-styleable name="ImageView">
<!-- Sets a drawable as the content of this ImageView. -->
<attr name="src" format="reference|color" />
<!-- ...snipped for brevity... -->
</declare-styleable>
系統(tǒng)針對<declare-styleable>定義在R.java中生成了R.styleable.[name]數(shù)組抚吠,并對內(nèi)每個attribute定義了一個R.styleable.[name]_[attribute]常量常潮。上述例子生成的就是R.styleable.ImageView和R.styleable.ImageView_src。
其中R.styleable.[name]是所有attribute資源的數(shù)組楷力,通過R.styleable.[name]_[attribute]作為數(shù)組的下標來查找指定資源喊式。
在自定義View構(gòu)造時,開發(fā)者經(jīng)常通過AttributeSet(XML中定義的attributes)+R.styleable.[name](標志哪些attributes需要提取和attribute的排序)可解析后獲取TypedArray對象萧朝。這是因為AttributeSet直接解析復(fù)雜度較大岔留,系統(tǒng)才提供Theme.obtainStyledAttributes獲取TypedArray以訪問屬性。
簡單理解检柬,上面的src的處理過程類似下面代碼献联,當然實際源碼復(fù)雜的多。
public ImageView(Context context, AttributeSet attrs) {
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ImageView, 0, 0);
Drawable src = ta.getDrawable(R.styleable.ImageView_src);
setImageDrawable(src);
ta.recycle();
}
defStyleAttr參數(shù)
開發(fā)者除了在XML中直接設(shè)置View的屬性值何址,eg以上章節(jié)中ImageView的src里逆,還可以通過theme來設(shè)置屬性值。
An attribute in the current theme that contains a reference to a style resource that supplies defaults values for the TypedArray.
再來看一次defStyleAttr的文檔說明用爪,書讀百遍原押,其義自現(xiàn)。這次以TextView為例來進行展示项钮。
public TextView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.textViewStyle);
}
參考TextView的構(gòu)造方法班眯,defStyleAttr入?yún)⑹褂昧薘.attr.textViewStyle希停。在系統(tǒng)源碼的attrs.xml中找到textViewStyle的定義:
<resources>
<declare-styleable name="Theme">
<!-- ...snip... -->
<!-- Default TextView style. -->
<attr name="textViewStyle" format="reference" />
<!-- ...etc... -->
</declare-styleable>
</resource>
我們使用declare-styleable來定義theme中可使用的attributes烁巫。這里指出textViewStyle是一個reference(對資源的引用),在這里應(yīng)該是對style的引用宠能。
查看源碼中的themes.xml中Android的默認主題亚隙。開發(fā)者可通過對Application或Activity設(shè)置theme來使之生效。
<resources>
<style name="Theme">
<!-- ...snip... -->
<item name="textViewStyle">@style/Widget.TextView</item>
<!-- ...etc... -->
</style>
</resource>
上述textViewStyle指向的Widget.TextView定義在styles.xml中违崇,有興趣的同學可以自行查看阿弃。
也是通過類似TextView的方法诊霹,Android系統(tǒng)為很多原生控件提供了默認屬性值。相信大家也可以理解渣淳,defStyleAttr參數(shù)是當前theme下定義的一個style資源的屬性脾还,并為最后生成的TypedArray資源數(shù)組提供了默認值的功能。
最后一個參數(shù)defStyleRes相對簡單一些入愧,是一個style資源的引用來為View提供屬性默認值鄙漏。但是優(yōu)先級較低。
obtainStyledAttibutes方法中幾個參數(shù)的優(yōu)先級如下:
- 1棺蛛、 Any value defined in the AttributeSet.
- 2怔蚌、The style resource defined in the AttributeSet (i.e. style=@style/blah).
- 3、The default style attribute specified by defStyleAttr.
- 4旁赊、The default style resource specified by defStyleResource (if there was no defStyleAttr).
- 5桦踊、Values in the theme.
自定義View構(gòu)造方法實現(xiàn)
在編寫自定義View時,一般需要實現(xiàn)至少前兩個構(gòu)造方法终畅。但是如果需要實現(xiàn)所有構(gòu)造方法籍胯,注意不要使用this(...)進行級聯(lián)調(diào)用。推薦的使用方法如下:
public MyView(Context context) {
super(context);
init(context, null, 0);
}
public MyView(Context context, AttributeSet attrs) {
super(context,attrs);
init(context, attrs, 0);
}
public MyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs, defStyle);
}
private void init(Context context, AttributeSet attrs, int defStyle) {
// do additional work
}
原因是自定義View的父控件可能在構(gòu)造方法中設(shè)置了默認的defStyle声离。以TextView(Button等也可)的構(gòu)造方法為例芒炼,如果使用this級聯(lián)調(diào)用,而不是super(context)术徊,控件就丟失了應(yīng)用當前theme下R.attr.textViewStyle中定義的默認屬性本刽。
public TextView(Context context) {
this(context, null);
}
public TextView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.textViewStyle);
}
public TextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
公共Attribute定義
一個module的Attribute定義中不能出現(xiàn)同名屬性,即使同名屬性位于多個attrs.xml文件赠涮。這時候可參考系統(tǒng)公共屬性的定義子寓,解決方法如下:
<resources>
<!-- 公共屬性 -->
<attr name="useSkin" format="boolean"/>
<declare-styleable name="CustomHeaderLoadingLayout">
<attr name="headerBackground" format="reference"/>
<attr name="loadingViewType">
<enum name="colored" value="0"/>
<enum name="white" value="1"/>
</attr>
<attr name="showTip" format="boolean"/>
<attr name="tipTextColor" format="color|reference"/>
<attr name="useSkin" />
</declare-styleable>
<declare-styleable name="DragBubbleView">
<attr name="bubbleRadius" format="dimension" />
<attr name="bubbleColor" format="color" />
<attr name="bubbleTextPadding" format="dimension" />
<attr name="bubbleTextSize" format="dimension" />
<attr name="bubbleTextColor" format="color" />
<attr name="useSkin"/>
</declare-styleable>
</resources>
其實只要保證只有一處屬性定義語句<attr name="useSkin" format="boolean"/>(也可在某個declare-styleable內(nèi)定義),可在多個地方進行屬性的引用 <attr name="useSkin"/>
參考文檔:
https://blog.danlew.net/2016/07/19/a-deep-dive-into-android-view-constructors/
https://stackoverflow.com/questions/9195713/do-i-need-all-three-constructors-for-an-android-custom-view/34301725#34301725
Android 深入理解Android中的自定義屬性
Android View構(gòu)造方法第三參數(shù)使用方法詳解