您的文字看起来像什么?

朋友们,星期五非常好。 我们希望与您分享一篇专为“ Android-developer”课程的学生准备的文章的翻译 高级课程 好好阅读。



如何在Android上以声明方式设置文本样式。


弗吉尼亚州Poltrack

Android应用程序中的TextView提供了一些用于设置文本样式的属性以及各种应用方式。 这些属性可以直接在布局中设置,将样式应用于视图或将主题应用于布局,或者根据需要设置textAppearance。 但是应该使用什么呢? 如果将它们结合在一起会发生什么?


什么以及何时使用?

本文介绍了各种用于文本声明式样式化的方法(即,当您在XML文件中定义样式时),并讨论了每种方法的范围和优先级。

tl;博士;


应该阅读整篇文章,但这是摘要。

记住各种样式方法的优先级顺序-如果您试图样式化某些文本而看不到预期的结果,则很可能您的更改被此层次结构中具有更高优先级的内容所覆盖:


文本样式方法的层次结构

我建议按照以下步骤进行样式设计:

  1. textViewStyle中的任何应用程序样式设置为主题的默认样式。
  2. 安装您的应用程序将使用的(少量) TextAppearance集(或使用/继承自MaterialComponent 样式 ),并直接从您的视图中引用它们
  3. 通过设置TextAppearance不支持的属性来创建style (这些属性本身将确定您的TextAppearance )。
  4. 直接在布局中执行任何独特的样式。

展现一些风格


您可以直接在布局中设置TextView属性,但是这种方法可能更加乏味且不安全。 想象一下,您正在尝试更新应用程序中所有TextViews的颜色。 与所有其他视图一样,您可以(并且应该!)使用样式来确保一致性,重用性和易于更新。 为此,建议您在可能要将同一样式应用于多个视图时,为文本创建样式。 这非常简单,并且在Android视图系统中得到了广泛的支持。

设置视图样式时,在幕后会发生什么? 如果您曾经编写过自定义视图,则可能会看到对context.obtainStyledAttributes(AttributeSet,int [],int,int)的调用。 因此,Android中的视图系统将布局中指定的属性传递给视图。 实际上, AttributeSet参数可以视为您在布局中指定的XML参数的映射。 如果AttributeSet设置了样式, 则先读取样式 ,然后将在视图中直接指定的属性应用于该样式 。 因此,我们来到了优先原则。

查看→样式

直接在视图中定义的属性始终“优先”并覆盖样式中定义的属性。 注意,样式和视图属性是结合使用的。 在样式中指定的视图中定义属性也不会取消整个样式。 还应该注意的是,您认为没有真正的方法可以确定样式的来源。 这是由视图系统在类似调用中一次为您确定的。 您不能同时选择两个选项。

尽管样式非常有用,但有其局限性。 其中之一是您只能将一种样式应用于视图(与之类似的CSS一样,您可以在其中应用多个类)。 但是, TextView有一个技巧;它提供TextAppearance属性,该属性的作用类似于style 。 如果通过TextAppearance文本设置TextAppearance ,则可以将style属性留给其他样式使用,这看起来很实用。 让我们仔细看看什么是TextAppearance及其工作方式。

文字外观


TextAppearance没有什么神奇的TextAppearance (例如,一种秘密模式,可以应用您不应该了解的多种样式!!!!), TextView可以使您免于不必要的工作。 让我们看一下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首先查看您是否指定了android:textAppearance ,如果已指定,它将加载此样式并应用此处列出的所有属性。 后来,他从视图中加载了所有属性(他记得,包括样式)并应用了它们。 因此,我们得出第二个优先级规则:

查看→样式→文字外观

由于首先检查了文本外观,所以直接在视图或样式中定义的任何属性都将覆盖它。

使用TextAppearance ,要记住另一个TextAppearance :它支持TextView提供的样式属性的子集 。 为了更好地理解我的意思,让我们回到这一行:

obtainStyledAttributes(ap, android.R.styleable.TextAppearance);

我们查看了带有4个参数的receiveStyledAttributes版本,此2个参数版本与此稍有不同。 她查看给定的样式(由第一个参数id定义),并仅根据第二个参数attrs数组中出现的样式中的属性对其进行过滤。 因此,可样式化的android.R.styleable.TextAppearance定义了TextAppearance的范围。 查看此定义,我们看到TextAppearance 支持 TextView 支持的许多( 但不是全部)属性。

 <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" /> 

TextAppearance支持的样式属性

以下是一些TextAppearance中不包含的TextView属性: lineHeight[Multiplier|Extra]linesbreakStrategyhyphenationFrequencyTextAppearance在字符级别而不是段落级别工作,因此不支持影响整个布局的属性。

因此, TextAppearance非常有用,它使我们能够定义一种面向文本样式化属性的style ,并使该style在视图中自由用于其他目的。 但是,它的范围有限并且位于优先级链的底部,因此请不要忘记这些限制。

合理的默认值


当我们查看Android视图如何解析属性( context.obtainStyledAttributes )时,实际上将其简化了一些。 她调用theme.obtainStyledAttributes (使用当前Theme Context 'a)。 当检查链接时 ,将显示我们先前检查的优先级顺序,并指示他正在寻找其他两个位置来解析属性:视图和主题的默认样式。

在确定特定属性的最终值时,将使用四个输入参数:

  1. 此AttributeSet中的所有属性值。
  2. 在AttributeSet中指定的样式资源(名为“样式”)。
  3. defStyleAttr和defstyleres指定的默认样式
  4. 此线程中的基本值。

根据主题文档设置优先顺序

我们将回到主题上,但让我们先看一下默认样式。 此默认样式是什么? 为了回答这个问题,我认为从TextView主题中退出一个小TextView并查看一个简单的Button会很有用。 当您在布局中插入< 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); } @Override public CharSequence getAccessibilityClassName() { return Button.class.getName(); } @Override public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) { if (getPointerIcon() == null && isClickable() && isEnabled()) { return PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_HAND); } return super.onResolvePointerIcon(event, pointerIndex); } } 

仅此而已! 这是全班(无评论)。 您可以在这里自己检查。 我会等。 那么背景,大写字母,涟漪等来自何处? 您可能已经错过了,但是所有这些都将在构造函数中使用2个参数进行定义。 从XML进行布局时调用的一种。 这是在com.android.internal.R.attr.buttonStyle中定义defaultStyleAttr的最后一个参数。 这是默认样式,它实际上是一个间接参考点,可让您指定默认样式。 它不直接指向样式,而是允许您指向主题中指定的一种,在解析属性时将对其进行检查。 这正是您通常从中继承的所有主题所做的工作,以提供标准小部件的外观。 例如,如果您查看材质主题,则它定义@style/Widget.Material.Light.Button ,正是这种风格提供了theme.obtainStyledAttributes如果您未指定其他任何内容时将theme.obtainStyledAttributes所有属性。

回到TextView ,它还提供了默认样式: textViewStyle 。 如果要将某些样式应用于应用程序中的每个TextView,这将非常方便。 假设您要将默认行距设置为1.2。 您可以使用style/TextAppearance做到这一点,并尝试在代码审查期间应用它(甚至可能在Lint中使用优雅的自定义规则),但是您需要保持警惕并确保招募新的团队成员,请谨慎进行重构等。

更好的方法是为应用程序中的所有TextView指定您自己的默认样式,并设置所需的行为。 您可以通过为textViewStyle设置自己的样式来做到这一点,该样式默认是从平台或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> 

考虑到这一点,我们的优先级规则采用以下形式:

查看->样式->默认样式-> TextAppearance

作为视图系统属性解析的一部分,此插槽在样式之后填充(因此,样式中的所有内容都会被应用的样式或视图属性默认取消),但仍会覆盖文本外观。 默认样式可以非常方便。 如果您决定编写自己的自定义视图,则它们可能是实现默认行为的强大方法,从而使自定义变得容易。

如果您继承窗口小部件并且未指定自己的默认样式,请确保在构造函数中使用默认的父类样式(不要仅传递0)。 例如,如果您继承自AppCompatTextView并使用2个参数编写自己的构造函数,请确保将android.R.attr.textViewStyle defaultStyleAttr传递( 如此处 ),否则您将失去父类的行为。

主题


如前所述,还有另一种(我保证)提供样式信息的方法。 另一个位置theme.obtainStyledAttributes将直接进入主题本身。 也就是说,如果您向主题添加样式属性,例如android:textColor ,则视图系统会将其选择作为最后的选择。 通常,将主题属性和样式属性混合在一起是一个坏主意,也就是说,通常不应该为主题设置直接应用于视图的内容(反之亦然),但是有一些罕见的例外。

例如,当您尝试在整个应用程序中更改字体时。 您可以使用上述方法之一,但是在任何地方手动调整文本的样式/外观都是单调且不安全的,默认样式仅在窗口小部件级别有效; 子类可以覆盖此行为,例如,按钮定义了自己的android:buttonStyle ,而您的android:textViewStyle不会被拾取。 相反,您可以在主题中指定字体:

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

现在,如果没有被具有更高优先级的东西所覆盖,那么任何支持该属性的视图都将对其进行拾取:

视图→样式→默认样式→主题→文本外观

同样,由于这是视图样式系统的一部分,因此它将覆盖以文本形式提供的所有内容,但是将被任何更特定的属性覆盖。

记住这个优先事项。 在我们的示例中,该字体带有用于整个应用程序的字体,您可以期望Toolbar选择该字体,因为它包含标题,即TextView 。 但是,工具栏类本身定义了一个默认样式,该样式包含titleTextAppearance ,该样式定义了android:fontFamily ,并将其直接设置在TextView标头中,从而覆盖了主题级别的值。 主题级别的样式可能有用,但易于覆盖,因此请确保正确应用它们。

奖励:未解决的问题


整篇文章致力于在视图级别进行文本的声明性设计,即,如何在填充过程中为整个TextView设置样式。 填充后应用的任何样式(例如, textView.setTextColor(…) )都将覆盖声明性样式。 TextView还通过Span支持较小的样式。 我将不讨论这个主题,因为Florina Muntenescu的文章对此进行了详细描述。

使用Spans制作出色的文字样式
跨度

为了完整起见,我会提到这一点,请记住,程序样式和跨度将是优先级最高的顺序:

跨度→设置器→视图→样式→默认样式→主题→文本外观

选择你的风格


尽管可以使用多种方式来设置文本样式,但是了解方法之间的差异及其局限性可以帮助您找到适合特定任务的正确工具,或者理解为什么一种方法优先于另一种方法。

有一个不错的文字样式!

我们邀请所有人参加该框架的免费网络研讨会 ,我们将熟悉DI框架Dagger 2:我们将学习Dagger2如何生成代码,我们将处理JSR 330注释和Dagger2构造,我们将学习如何在多模块应用程序中使用Dagger2并考虑使用Dagger Android Injector。

Source: https://habr.com/ru/post/zh-CN456110/


All Articles