Android 中View的四個構(gòu)造函數(shù)

I often see confusion around Android View constructors. Why are there four of them? What does each parameter do? Which constructors do I need to implement?

我經(jīng)常對View的構(gòu)造函數(shù)感到很困惑暮屡,為什么會有四個醋旦?每個參數(shù)表示什么妨托?我應(yīng)該需要實現(xiàn)那些構(gòu)造函數(shù)瞳秽?

If you just want quick, practical advice, here's a few good guidelines:
如果你只想知道快速钾腺,并且實用的建議模软。如下

  • Use View(Context) for creating Views in code.
    Override View(Context, AttributeSet) when inflating Views from XML.
    在代碼中使用View(Context)來創(chuàng)建View雁仲,如果要用在XML文件中就需要重寫 View(Context, AttributeSet)構(gòu)造函數(shù)。
  • Ignore the rest because you probably won't need them.
    忽略其他兩個構(gòu)造函數(shù)咆畏,你可能不需要他們。

For those still with me - let's dive in.

Constructor parameters
At most, there can be four constructor parameters. A brief summary:
基本上吴裤,存在四個構(gòu)造函數(shù)參數(shù)旧找,一個簡潔的總結(jié)

  1. Context - Used all over the place in Views.
    上下文
  2. AttributeSet - The XML attributes (when inflating from XML).
    XML屬性
  3. int defStyleAttr - A default style to apply to the View (defined in the theme).
    一個默認(rèn)的風(fēng)格應(yīng)用到View,在theme中定義麦牺。
  4. int defStyleResource - A default style to apply to the View, if defStyleAttr is unused.
    一個默認(rèn)的風(fēng)格應(yīng)用到View钮蛛,如果defStyleAttr沒有使用。

Besides the Context, the other parameters are only used to configure the initial state of the View based on XML attributes (from layout, styles and themes).

除了Context剖膳,其他參數(shù)都是通過XML來配置View的初始狀態(tài)魏颓。(來自于layout,styles,themes)

Attributes
Let's start by talking about how you define valid XML attributes. Here's a basic ImageView in XML:

<ImageView  
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:src="@drawable/icon"
  />

Have you ever wondered where layout_width, layout_height and src come from? It's not out of thin air; you actually declare these attributes explicitly as something the system should handle via <declare-styleable>

你是否好奇layout_width, layout_height and src從哪里來吱晒,它并不是無中生有甸饱;它在系統(tǒng)的<declare-styleable中聲明了

For example, here's where src is defined:

<declare-styleable name="ImageView">  
  <!-- Sets a drawable as the content of this ImageView. -->
  <attr name="src" format="reference|color" />

  <!-- ...snipped for brevity... -->

</declare-styleable>  

Each declare-styleable generates one R.styleable.[name] plus an additional R.styleable.[name]_[attribute] for each attribute. For example, the above generates R.styleable.ImageView and R.styleable.ImageView_src.

每一個declare-styleable都會生成一個R.styleable.[name],節(jié)點(diǎn)里面的每個屬性(attribute)都會生成一個R.styleable.[name]_[attribute]的資源仑濒,例如叹话,上面代碼,會生成一個R.styleable.ImageViewR.styleable.ImageView_src

What are these resources? The base R.styleable.[name] is an array of all the attribute resources, which the system uses to lookup attribute values. Each R.styleable.[name]_[attribute] is just an index into that array, so that you can retrieve all the attributes at once, then lookup each value individually.

這些資源是什么墩瞳?R.styleable.[name]是個數(shù)組驼壶,里面包含了所有的屬性資源,系統(tǒng)用它來尋找屬性所對應(yīng)的值(也就是資源)喉酌。每一個R.styleable.[name]_[attribute] 只是上面數(shù)組里面的索引(index),通過它就可以獲得所有屬性的值热凹。

If you think of it like a cursor, you can consider R.styleable.[name] as list of columns to query and each R.styleable.[name]_[attribute] as a column index.

你可以把它想象成一個cursor,把R.styleable.[name]當(dāng)成要查詢字段的集合泪电,R.styleable.[name]_[attribute]就是這些字段的索引般妙。

For more on declare-styleable, here's the official documentation on creating your own.

想要了解更多有關(guān)declare-styleable 可以去查看官方文檔

AttributeSet

The XML we wrote above is given to the View as an AttributeSet.

Usually you don't access AttributeSet directly, but instead use Theme.obtainStyledAttributes(). That's because the raw attributes often need to resolve references and apply styles. For example, if you define style=@style/MyStyle in your XML, this method resolves MyStyle and adds its attributes to the mix. In the end, obtainStyledAttributes() returns a TypedArray which you can use to access the attributes.

通常你不會直接去訪問AttributeSet歪架,而是通過使用Theme.obtainStyledAttributes()方法去訪問股冗。這是因為這些attributes需要去解析引用和應(yīng)用樣式。例如和蚪,如果你在你的XML中定義了style=@style/MyStyle止状,這個方法可以解析MyStyle并且把它的attributes添加到屬性集合中烹棉。最后obtainStyledAttributes() 返回一個TypedArray,通過TypedArray可以訪問這些attributes怯疤。

Greatly simplified, the process looks like this:
通常來說浆洗,處理過程如下:

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();
}

In this case, we're passing two parameters to obtainStyledAttributes(). The first is AttributeSet attrs, the attributes from XML. The second is the array R.styleable.ImageView, which tells the method which attributes we want to extract (and in what order).

在這里,我們傳了兩個參數(shù)給obtainStyledAttributes()函數(shù)集峦,第一參數(shù)就是AttributeSet attrs伏社,是所有來源于XML的attributes。第二個參數(shù)就是R.styleable.ImageView數(shù)組塔淤,它告訴了這個方法那些attributes我們想要去提日(比如src屬性,而不是sra其他屬性)高蜂。

With the TypedArray we get back, we can now access the individual attributes. We need to use R.styleable.ImageView_src so that we correctly index the attribute in the array.
通過使用返回的TypedArray對象聪黎,我們可以去訪問這些獨(dú)立的attributes。我們需要使用R.styleable.ImageView_src作為index來正確的提取該屬性(src屬性)所對應(yīng)的值(圖片的引用)
(Recycling TypedArrays is important, too, so I left it in the sample above.)
回收TypedArrays是非常重要的备恤,所以我在上面的代碼中寫上了稿饰。

Normally you extract multiple attributes at once. Indeed, the actual ImageView implementation is far more complex than what's shown above (since ImageView itself has many more attributes it cares about).
通常你會一次性提取多個attributes。確實露泊,實際上ImageView的實現(xiàn)比上面代碼要復(fù)雜的多喉镰。

Theme Attributes

A sidenote, for completeness: The AttributeSet is not the only place we got our values from when using obtainStyledAttributes() in the last section. Attributes can also exist in the theme.

AttributeSet并不是obtainStyledAttributes()獲取值得唯一來源,Attributes(屬性)也可以來源于Theme惭笑。

This rarely plays a role for View inflation because your theme shouldn't be setting attributes like src, but it can play a role if you use obtainStyledAttributes() for retrieving theme attributes (which is useful but is outside the scope of this article).
如果你打算從theme中獲取的屬性來初始化View侣姆,那么這種情況是很少見的,畢竟我們通常不會在theme中設(shè)置src這樣的屬性脖咐,但是如果你是想使用obtainStyledAttributes()來檢索theme的屬性铺敌,它確實有點(diǎn)作用。不過這不在本文的討論范圍內(nèi)屁擅。

Default Style Attribute

You may have noticed that I used 0 for the last two parameters in obtainStyledAttributes(). They are actually two resource references - defStyleAttr and defStyleRes. I'm going to focus on the first one here.

你可能已經(jīng)注意到在obtainStyledAttributes()函數(shù)中我使用了0來作為后面兩個參數(shù)的值偿凭。這兩個參數(shù)實際上是兩個資源的引用--defStyleAttr 和 defStyleRes。后面我將重點(diǎn)放在第一個參數(shù)(defStyleAttr )上派歌。

defStyleAttr is, by far, the most confusing parameter for obtainStyledAttributes(). According to the documentation it is:

defStyleAttr是obtainStyledAttributes()函數(shù)中最讓人感到困惑的參數(shù)弯囊。下面是官方文檔的說明。

An attribute in the current theme that contains a reference to a style resource that supplies defaults values for the TypedArray.

Whew, that's a mouthful. In plain English, it's a way to be able to define a base style for all Views of a certain type. For example, you can set textViewStyle in your theme if you want to modify all your app's TextViews at once. If this didn't exist, you'd have to manually style every TextView instead.

它是一種可以讓同一類型的所有View具有基本的樣式胶果。例如匾嘱,你如果想要一次性的改變你App中所有TextView的樣式,你可以在theme中設(shè)置textViewStyle屬性早抠。如果theme沒有設(shè)置的話霎烙,你就必須手動的改變每個TextView。

Let's walk through how it actually works, using TextView as an example.

First, it's an attribute (in this case, R.attr.textViewStyle). Here's where the Android platform defines textViewStyle:
首先,textViewStyle是一個屬性悬垃,對應(yīng)索引就是 R.attr.textViewStyle游昼, 下面就是textViewStyle屬性的定義。

<resources>  
  <declare-styleable name="Theme">

    <!-- ...snip... -->

    <!-- Default TextView style. -->
    <attr name="textViewStyle" format="reference" />

    <!-- ...etc... -->

  </declare-styleable>
</resource>  

Again, we're using declare-styleable, but this time to define attributes that can exist in the theme. Here, we're saying that textViewStyle is a reference - that is, its value is just a reference to a resource. In this case, it should be a reference to a style.

再一次我們使用了declare-styleable尝蠕,但是這次我們定義的屬性可以在Theme中使用烘豌。在這里textViewStyle值的類型是一個引用類型。所以它對應(yīng)的值就是資源的引用看彼。本文中它引用了一個style廊佩。

Next we have to set textViewStyle in the current theme. The default Android theme looks like this:
接下來我們在當(dāng)前theme中使用textViewStyle屬性,默認(rèn)的Android主題類似下面的代碼:

 <resources>  
  <style name="Theme">
    <!-- ...snip... -->

    <item name="textViewStyle">@style/Widget.TextView</item>

    <!-- ...etc... -->

  </style>
</resource>  

Then the theme has to be set for your Application or Activity, typically via the manifest:
然后要按如下的方式在manifest中設(shè)置給你的應(yīng)用或者Activity靖榕。

  <activity  
  android:name=".MyActivity"
  android:theme="@style/Theme"
  />

Now we can use it in obtainStyledAttributes():
現(xiàn)在我們可以在obtainStyledAttributes()方法中使用它了:

 TypedArray ta = theme.obtainStyledAttributes(attrs, R.styleable.TextView, R.attr.textViewStyle, 0);  

The end result is that any attributes not defined by the AttributeSet are filled in with the style that textViewStyle references.
結(jié)果就是那些沒有在AttributeSet中定義的屬性标锄,都會通過textViewStyle引用來填充這些屬性的值。

Phew! Unless you're being hardcore, you don't need to know all these implementation details. It's mostly there so that that the Android framework can let you define base styles for various Views in your theme.
具體細(xì)節(jié)不用了解的太清楚茁计。大多數(shù)情況下鸯绿,Android框架可以讓你在主題中為各種View定義基本樣式。

Default Style Resource

defStyleRes is much simpler than its sibling. It is just a style resource (i.e. @style/Widget.TextView). No complex indirection through the theme.

這個就更簡單了簸淀,它就是一個style資源。

The attributes from the style in defStyleRes are applied only if defStyleAttr is undefined (either as 0 or it isn't set in the theme).

defStyleRes所引用的style中的屬性毒返,只有在defStyleAttr沒有用到的情況下才會被使用租幕。

Precedence 優(yōu)先級

We've now got a bunch of ways to derive the value for an attribute via obtainStyledAttributes(). Here's their order of precedence, from highest to lowest:
現(xiàn)在通過obtainStyledAttributes()方法我們有幾種方式去獲取屬性的值,那么這些的途徑的優(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).
    Values in the theme.

In other words, any attributes you set directly in XML will be used first.
But there are all sorts of other places those attributes can be retrieved from if you don't set them yourself.
也就是首先使用你在XML文件中設(shè)置的屬性拧簸。其次才從其他地方獲取那些沒有XML中設(shè)置的屬性的值

View constructors
This article was supposed to be about View constructors, right?

There are four of them total, each one adding a parameter:

View(Context)

View(Context, AttributeSet)

View(Context, AttributeSet, defStyleAttr)

View(Context, AttributeSet, defStyleAttr, defStyleRes)
An important note: the last one was added in API 21, so you unless you've got minSdkVersion 21, you should avoid it for now. (If you want to use defStyleRes just call obtainStyledAttributes() yourself since it's always been supported.)

最后一個構(gòu)造函數(shù)是在API21的時候才被添加進(jìn)去劲绪。所以除非你的minSdkVersion為21,否則不要使用它盆赤。

They cascade, so if you call one, you end up calling them all (via super). The cascading also means you only need to override the constructors you use. Generally, this means that you only need to implement the first two (one for code constructor, the other for XML inflation).

他們是串聯(lián)的贾富。所以你可以只在帶兩個參數(shù)的構(gòu)造函數(shù)中實現(xiàn)邏輯,在另外一個構(gòu)造函數(shù)中使用this()來調(diào)用帶兩個參數(shù)的構(gòu)造函數(shù)即可牺六。

I usually setup my custom Views like so:
我通常會按如下方式來使用颤枪。


SomeView(Context context) {  
  this(context, null);
}

SomeView(Context context, AttributeSet attrs) {  
  // Call super() so that the View sets itself up properly
  super(context, attrs);

  // ...Setup View and handle all attributes here...
}

Within the two-arg constructor you can use obtainStyledAttributes() any way you want. A quick way to implement a default style is to just provide defStyleRes to it; that way you don't need to go through the pain in the butt that is defStyleAttr (which is more of a framework tool and not usually necessary for a single app).
你可以在帶兩個參數(shù)的構(gòu)造函數(shù)中以你想要的方式來使用obtainStyledAttributes() 函數(shù),一種快速的方法就是提供一個默認(rèn)的defStyleRes來這個函數(shù)淑际,這樣我們就可以避免使用defStyleAttr(他需要在Theme聲明屬性的值)所帶來的痛苦畏纲。

Anyways, I hope this helps not only your understanding of View constructors but also how attributes are retrieved during View construction!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市春缕,隨后出現(xiàn)的幾起案子盗胀,更是在濱河造成了極大的恐慌,老刑警劉巖锄贼,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件票灰,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)屑迂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門浸策,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人屈糊,你說我怎么就攤上這事的榛。” “怎么了逻锐?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵夫晌,是天一觀的道長。 經(jīng)常有香客問我昧诱,道長晓淀,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任盏档,我火速辦了婚禮凶掰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蜈亩。我一直安慰自己懦窘,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布稚配。 她就那樣靜靜地躺著畅涂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪道川。 梳的紋絲不亂的頭發(fā)上午衰,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天,我揣著相機(jī)與錄音冒萄,去河邊找鬼臊岸。 笑死,一個胖子當(dāng)著我的面吹牛尊流,可吹牛的內(nèi)容都是我干的帅戒。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼崖技,長吁一口氣:“原來是場噩夢啊……” “哼蜘澜!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起响疚,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤鄙信,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后忿晕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體装诡,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了鸦采。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宾巍。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡麦乞,死狀恐怖框冀,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情睦擂,我是刑警寧澤锣吼,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布选浑,位于F島的核電站,受9級特大地震影響玄叠,放射性物質(zhì)發(fā)生泄漏古徒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一读恃、第九天 我趴在偏房一處隱蔽的房頂上張望隧膘。 院中可真熱鬧,春花似錦寺惫、人聲如沸疹吃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽互墓。三九已至,卻和暖如春蒋搜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背判莉。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工豆挽, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人券盅。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓帮哈,卻偏偏與公主長得像,于是被迫代替她去往敵國和親锰镀。 傳聞我的和親對象是個殘疾皇子娘侍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345

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