[譯]控件屬性的各類聲明方式的比較

理解如何聲明具備特定風格的TextViewhtml

TextView提供了各類各樣的屬性,和不一樣的方式去聲明它們。咱們能夠在xml中直接定義View的屬性,也能夠經過style的方式去設置TextView的屬性,還能夠經過設置themeandroid:textAppearance的方式來設置一個控件的屬性。因此,咱們究竟該在什麼樣的情境下使用它們?若是咱們混合使用,會發生什麼?java

本文概述了設置TextView的屬性的各類方法,查看了這些方法它們的範圍和優先級,以及什麼時候應該使用何種技術。android

1.結論

你應該把整篇文章讀完,這裏是結論app

注意這裏提到的各類設置屬性方式的優先級順序。若是對某個TextView聲明瞭屬性可是沒有看到預期的結果,多是你應用的樣式被一個比它更高優先級的樣式所覆蓋函數

下圖是樣式的優先級:佈局

關於TextView樣式的一些建議字體

  • 能夠在主題theme中使用textViewStyle來爲每個TextView指定默認的樣式
  • 能夠設置一些TextAppearance的樣式,而後在view中直接使用
  • 也能夠爲TextAppearance不支持的屬性,建立style,而後xml或代碼中使用
  • 直接在View中指定一些非公共的屬性

2.直接指定屬性和利用style

儘管能夠直接在xml佈局中指定TextView的屬性,但這種方法很容易犯錯而且冗餘,由於你不得不爲應用中的全部TextView添加一些相同的屬性。有沒有一個簡便的方法呢?優化

咱們能夠利用style來爲TextView指定樣式。這提升了代碼的重複利用率而且易於更改。this

因此,建議若是有相同的樣式應用於多個視圖,那麼能夠爲TextView建立style。google

在View上設置style,幕後的狀況是什麼?寫過自定義View的同窗有可能見過對context.obtainStyledAttributes(AttributeSet, int[], int, int)的調用。 這就是Android視圖系統將layout佈局中指定的屬性傳遞給View的方式。 本質上,能夠將AttributeSet參數視爲在layout中指定的XML參數的映射。 若是此AttributeSet指定一種style,則首先讀取該style,而後在此之上應用在xml中直接指定的屬性。 這樣,咱們得出了第一個優先權規則。

View > Style

相較於style,直接在View中指定的屬性,好比textColor,優先級會更高。須要注意的一點是,當你在xml中同時指定style和具體的屬性的時候,style中定義的其餘的屬性並不會失效。

雖然style很是有用,但確實有其侷限性。其中之一是,只能將一種style應用於一個View(不一樣於CSS,能夠在其中應用多個類)。可是,TextView有其竅門,它提供了TextAppearance屬性,其功能相似於style。若是經過TextAppearance提供文本style,則style屬性能夠自由用於其餘樣式,這聽起來頗有用。讓咱們仔細看看什麼是TextAppearance及其工做方式。

TextAppearance

關於TextAppearance沒有什麼神奇的地方。如下代碼來自TextView的源碼

TypedArray a = theme.obtainStyledAttributes(attrs, com.android.internal.R.styleable.TextViewAppearance, defStyleAttr, defStyleRes);
TypedArray appearance = null;
int ap = a.getResourceId(com.android.internal.R.styleable.TextViewAppearance_textAppearance, -1);
a.recycle();
if (ap != -1) {
  appearance = theme.obtainStyledAttributes(ap, com.android.internal.R.styleable.TextAppearance);
}
if (appearance != null) {
  readTextAppearance(context, appearance, attributes, false);
  appearance.recycle();
}
// a little later
a = theme.obtainStyledAttributes(attrs, com.android.internal.R.styleable.TextView, defStyleAttr, defStyleRes);
readTextAppearance(context, a, attributes, true);
複製代碼

咱們能夠看到,在TextView源碼內部,它首先看你是否在xml中指定了android:textAppearance屬性。若是是,那麼加載這個style而後應用其中的全部屬性。以後,它將加載在View中聲明的style和全部聲明的屬性,所以,咱們獲得了咱們的第二個優先級規則。

View > Style > TextAppearance

由於android:textAppearance這條屬性會被最早檢查,因此定義在view中的style和全部直接定義在View的屬性將會覆蓋textAppearance

還有一點要注意的是,TextAppearance僅僅支持TextView所提供的屬性中的一部分。主要是由於這一行代碼

obtainStyledAttributes(ap, android.R.styleable.TextAppearance);
複製代碼

咱們以前看過了obtainStyledAttributes的4個參數的重載,這個2個參數的重載略有不一樣。 而是依據xml或代碼中聲明的textAppearance的style(由第一個id參數標識),並將其過濾爲僅顯示在第二個參數style所聲明的屬性。 這樣,android.R.styleable.TextAppearance定義了TextAppearance所可以表示的全部屬性的範圍。 具體查看android.R.styleable.TextAppearance,咱們能夠看到TextAppearance支持TextView支持的許多屬性,但不是所有。

如下是,TextAppearance支持的屬性

<attr name="textColor" />
<attr name="textSize" />
<attr name="textStyle" />
<attr name="typeface" />
<attr name="fontFamily" />
<attr name="textColorHighlight" />
<attr name="textColorHint" />
<attr name="textColorLink" />
<attr name="textAllCaps" format="boolean" />
<attr name="shadowColor" format="color" />
<attr name="shadowDx" format="float" />
<attr name="shadowDy" format="float" />
<attr name="shadowRadius" format="float" />
<attr name="elegantTextHeight" format="boolean" />
<attr name="letterSpacing" format="float" />
<attr name="fontFeatureSettings" format="string" />
複製代碼

3.默認Style

以前在查看Android視圖系統如何解析屬性(context.obtainStyledAttributes)時,咱們實際上簡化了一些事情。 實際上View內部會將調用theme.obtainStyledAttributes來指定默認的style和主題。查看參考後,咱們優化了View屬性的優先級,並指定了另外兩種添加屬性的位置:View的默認style和theme。

先讓咱們看一下默認的style。 什麼是默認的style? 爲了回答這個問題,這裏借用Button的一些屬性來作一個說明,TextView也是相似的。 將<Button>放到佈局中時,它看起來像這樣。

爲何會是這樣的樣式呢?讓咱們看一下Button的源碼

public class Button extends TextView {
  public Button(Context context) {
    this(context, null);
  }
  public Button(Context context, AttributeSet attrs) {
    this(context, attrs, com.android.internal.R.attr.buttonStyle);
  }
  public Button(Context context, AttributeSet attrs, int defStyleAttr) {
    this(context, attrs, defStyleAttr, 0);
  }
  public Button(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
  }
}
複製代碼

能夠點擊這裏查看Button的源碼。

那麼問題來了,Button的background的屬性,大寫文本,ripple的屬性等都是從哪裏來的呢?咱們能夠看到在擁有兩個參數的構造函數中,最後一個參數,它指定叫com.android.internal.R.attr.buttonStyle的defaultStyleAttr。這是默認樣式,從本質上講是一個間接點,容許你指定默認使用的樣式。它不直接指向樣式,而是讓您指向主題中的一個,在解析屬性時將對其進行檢查。這正是您一般從中繼承的全部主題所作的事情,以便爲常見的小部件提供默認的外觀。例如,以Material主題爲例,它定義了<item name =「 buttonStyle」>@style/Widget.Material.Light.Button</item>,正是這種樣式提供了全部屬性,theme.obtainStyledAttributes將提供這些屬性。若是您未指定其餘任何內容。

讓咱們回到TextView,TextView還提供了一種默認樣式:textViewStyle。 若是要對應用程序中的每一個TextView應用某種樣式,則能夠很是方便。 假設咱們想要始終將默認行距倍數設置爲1.2。 那麼能夠經過設置style或者TextAppearance來作到這一點。

然而一個更好的方法多是爲應用程序中的全部TextView指定本身的默認樣式。 您能夠經過爲本身的textViewStyle設置樣式(從platfom或MaterialComponents / AppCompat的默認樣式繼承而來)來實現。

<style name="Theme.MyApp" parent="@style/Theme.MaterialComponents.Light"> ... <item name="android:textViewStyle">@style/Widget.MyApp.TextView</item></style>
<style name="Widget.MyApp.TextView" parent="@android:style/Widget.Material.TextView"> <item name="android:textAppearance">@style/TextAppearance.MyApp.Body</item> <item name="android:lineSpacingMultiplier">@dimen/text_line_spacing</item> </style>
複製代碼

所以把默認的Style考慮在內,咱們的優先規則變爲:

View > Style > Default Style > TextAppearance

須要注意的一點是,儘管默認的style覆蓋了TextAppearance,但它是會被以後的好比style和直接在View中定義的屬性所覆蓋。

默認的style能夠很是方便。

若是你的自定義View繼承了一個控件而且你沒有指定你本身的默認的style,請確保在構造函數中使用父類的默認樣式(不要只傳遞0)。 例如,若是你要擴展AppCompatTextView並編寫本身的兩個參數的構造函數,請確保將android.R.attr.textViewStyle做爲defaultStyleAttr傳遞,不然你將失去父級的行爲。

4.Theme

以前提到過,有一種提供style信息的方法。咱們能夠看到,在TextView源碼中, theme.obtainStyledAttributes將會提供theme中指定的一些屬性。 也就是說,咱們能夠在theme中設置諸如android:textColor之類的屬性。 一般,將theme屬性和style屬性混合使用是一個比較糟糕的想法,也就是說,一般不要將那些你在View中直接定義的屬性設置在theme或者style中(反之亦然),可是有一些罕見的例外狀況。

例如,若是你嘗試在整個app中更改字體。 你可使用上面的任何一種技術,可是在任何地方手動設置style/textAppearance都是重複的,而且容易出錯,默認的style僅在窗口小部件級別有效; 若是是繼承TextView的子類,則可能會覆蓋此行爲,例如一個自定義View繼承了Button,而且定義了本身的android:buttonStyle,但不會選擇您自定義的android:textViewStyle。 所以,你能夠在theme中指定字體:

<style name="Theme.MyApp" parent="@style/Theme.MaterialComponents.Light"> ... <item name="android:fontFamily">@font/space_mono</item> </style>
複製代碼

如今,除非有更高優先級內容指定了View的fontFamily這個屬性,咱們將在全部的地方看到View的字體都是咱們想要的字體

View > Style > Default Style > Theme > TextAppearance

須要注意的是,在上面的經過theme全局設定字體的例子中,你可能但願Toolbar將會是咱們指定的字體,由於在Toolbar內部包含了一個TextView。 可是,Toolbar這個類自己定義了一個默認的style,該style包含一個titleTextAppearance,在其內部指定了一個屬性叫android:fontFamily,並直接在標題TextView上設置此style,從而覆蓋了咱們在Theme級別設置的字體。 Theme級別的style頗有用,但很容易被覆蓋,所以須要檢查是否和咱們想要獲得的效果一致。

爲了完整起見,咱們把經過代碼的方式手動設置style和span也囊括進來。所以,咱們的優先級變成了這樣:

Span > 代碼中手動設置 > xml中直接設置View的屬性 > Style > Default Style > Theme > TextAppearance

5.總結

雖然有各類各樣的方式能夠設置一個控件的屬性,可是瞭解這些方式之間的差別和侷限可讓咱們找到最適合當前需求的方式,同時也能夠減小咱們的重複代碼,下降出錯的可能性。

這篇文章是對國外的文章的翻譯,若是有什麼翻譯的很差或者不正確的地方歡迎評論指正

原文:What’s your text’s appearance?

相關文章
相關標籤/搜索