View構(gòu)造方法詳解及obtainStyledAttributes參數(shù)解析

構(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ù)使用方法詳解

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末笋除,一起剝皮案震驚了整個濱河市斜友,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌垃它,老刑警劉巖鲜屏,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異国拇,居然都是意外死亡洛史,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門酱吝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來也殖,“玉大人,你說我怎么就攤上這事务热∫涫龋” “怎么了己儒?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長捆毫。 經(jīng)常有香客問我闪湾,道長,這世上最難降的妖魔是什么绩卤? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任响谓,我火速辦了婚禮,結(jié)果婚禮上省艳,老公的妹妹穿的比我還像新娘娘纷。我一直安慰自己,他們只是感情好跋炕,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布赖晶。 她就那樣靜靜地躺著,像睡著了一般辐烂。 火紅的嫁衣襯著肌膚如雪遏插。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天纠修,我揣著相機與錄音胳嘲,去河邊找鬼。 笑死扣草,一個胖子當著我的面吹牛了牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播辰妙,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼鹰祸,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了密浑?” 一聲冷哼從身側(cè)響起蛙婴,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎尔破,沒想到半個月后街图,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡懒构,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年餐济,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片痴脾。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡颤介,死狀恐怖梳星,靈堂內(nèi)的尸體忽然破棺而出赞赖,到底是詐尸還是另有隱情滚朵,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布前域,位于F島的核電站辕近,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏匿垄。R本人自食惡果不足惜移宅,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望椿疗。 院中可真熱鬧漏峰,春花似錦、人聲如沸届榄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽铝条。三九已至靖苇,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間班缰,已是汗流浹背贤壁。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留埠忘,地道東北人脾拆。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像莹妒,于是被迫代替她去往敵國和親假丧。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容