[译]控件属性的各类声明方式的比较

理解如何声明具备特定风格的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?

相关文章
相关标签/搜索