样式和主题

尝试 Compose 方式
Jetpack Compose 是 Android 推荐的 UI 工具包。了解如何在 Compose 中使用主题。

Android 上的样式和主题允许您将应用设计的细节与 UI 结构和行为分开,类似于网页设计中的样式表。

样式 是一个属性集合,用于指定单个 View 的外观。样式可以指定属性,例如字体颜色、字体大小、背景颜色等等。

主题 是一个属性集合,它应用于整个应用、活动或视图层次结构,而不仅仅是单个视图。应用主题时,应用或活动中的每个视图都会应用它支持的主题的每个属性。主题还可以将样式应用于非视图元素,例如状态栏和窗口背景。

样式和主题在 样式资源文件 中声明,该文件位于 res/values/ 中,通常名为 styles.xml

图 1. 应用于同一活动的两个主题:Theme.AppCompat(左)和 Theme.AppCompat.Light(右)。

主题与样式

主题和样式有很多相似之处,但它们用于不同的目的。主题和样式具有相同的基本结构——将属性映射到资源的键值对。

样式 为特定类型的视图指定属性。例如,一个样式可以指定按钮的属性。您在样式中指定的每个属性都是您可以在布局文件中设置的属性。将所有属性提取到样式中可以轻松地在多个小部件中使用和维护它们。

主题 定义了一组命名资源,可以由样式、布局、小部件等引用。主题将语义名称(如 colorPrimary)分配给 Android 资源。

样式和主题旨在协同工作。例如,您可能有一个样式,它指定按钮的一部分是 colorPrimary,而另一部分是 colorSecondary。这些颜色的实际定义是在主题中提供的。当设备进入夜间模式时,您的应用可以从其“浅色”主题切换到其“深色”主题,更改所有这些资源名称的值。您无需更改样式,因为样式使用的是语义名称,而不是特定的颜色定义。

有关主题和样式如何协同工作的更多信息,请参阅博客文章 Android 样式:主题与样式

创建和应用样式

要创建新样式,请打开项目中的 res/values/styles.xml 文件。对于要创建的每个样式,请执行以下步骤

  1. 添加一个 <style> 元素,其名称唯一标识该样式。
  2. 为要定义的每个样式属性添加一个 <item> 元素。每个项目中的 name 指定您在布局中用作 XML 属性的属性。<item> 元素中的值是该属性的值。

例如,假设您定义了以下样式

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="GreenText" parent="TextAppearance.AppCompat">
        <item name="android:textColor">#00FF00</item>
    </style>
</resources>

您可以将样式应用于视图,如下所示

<TextView
    style="@style/GreenText"
    ... />

如果视图接受,则样式中指定的每个属性都将应用于该视图。视图会忽略它不接受的任何属性。

但是,您通常不会将样式应用于单个视图,而是将 样式作为主题 应用于整个应用、活动或视图集合,如本指南的另一部分所述。

扩展和自定义样式

创建自己的样式时,始终从框架或支持库中的现有样式继承,以便您保持与平台 UI 样式的兼容性。要扩展样式,请使用 parent 属性指定要扩展的样式。然后,您可以覆盖继承的样式属性并添加新属性。

例如,您可以继承 Android 平台的默认文本外观并对其进行修改,如下所示

<style name="GreenText" parent="@android:style/TextAppearance">
    <item name="android:textColor">#00FF00</item>
</style>

但是,始终从 Android 支持库继承核心应用样式。支持库中的样式通过针对每个版本可用的 UI 属性优化每个样式来提供兼容性。支持库样式的名称通常与平台中的样式类似,但包含 AppCompat

要从库或您自己的项目继承样式,请声明父样式名称包括前例中显示的 @android:style/ 部分。例如,以下示例从支持库继承文本外观样式

<style name="GreenText" parent="TextAppearance.AppCompat">
    <item name="android:textColor">#00FF00</item>
</style>

您还可以通过使用点表示法扩展样式名称来继承样式(平台样式除外),而不是使用 parent 属性。也就是说,在要继承的样式名称之前加上您要继承的样式名称,并用句点分隔。您通常只在扩展自己的样式时才执行此操作,而不是扩展其他库的样式。例如,以下样式继承了前面示例中 GreenText 的所有样式,然后增加了文本大小

<style name="GreenText.Large">
    <item name="android:textSize">22dp</item>
</style>

您可以通过继续添加更多名称来无限期地继承样式。

要查找可以使用 <item> 标记声明的属性,请参考各种类引用中的“XML 属性”表。所有视图都支持来自基础 View 类的 XML 属性,许多视图还添加了自己的特殊属性。例如,TextView XML 属性 包含 android:inputType 属性,您可以将其应用于接收输入的文本视图,例如 EditText 小部件。

将样式应用为主题

您可以通过与创建样式相同的方式创建主题。不同之处在于如何应用它:您不是使用视图上的 style 属性应用样式,而是使用 android:theme 属性在 AndroidManifest.xml 文件中的 <application> 标记或 <activity> 标记上应用主题。

例如,以下是如何将 Android 支持库的 Material Design“深色”主题应用于整个应用

<manifest ... >
    <application android:theme="@style/Theme.AppCompat" ... >
    </application>
</manifest>

以下是如何将“浅色”主题仅应用于一个活动

<manifest ... >
    <application ... >
        <activity android:theme="@style/Theme.AppCompat.Light" ... >
        </activity>
    </application>
</manifest>

应用或活动中的每个视图都会应用它支持的给定主题中定义的样式。如果视图仅支持样式中声明的某些属性,那么它仅应用这些属性,并忽略它不支持的属性。

从 Android 5.0(API 级别 21)和 Android 支持库 v22.1 开始,您还可以将 android:theme 属性指定给布局文件中的视图。这将修改该视图及其所有子视图的主题,这对于更改界面特定部分的主题调色板很有用。

前面的示例演示了如何应用 Android 支持库提供的主题,例如 Theme.AppCompat。但是,您通常希望自定义主题以适合您的应用品牌。最好的方法是扩展支持库中的这些样式并覆盖一些属性,如下一节所述。

样式层次结构

Android 提供了多种方法来在整个 Android 应用中设置属性。例如,您可以在布局中直接设置属性,将样式应用于视图,将主题应用于布局,甚至以编程方式设置属性。

在选择如何设置应用的样式时,请注意 Android 的样式层次结构。一般来说,尽可能使用主题和样式以确保一致性。如果您在多个地方指定相同的属性,则以下列表确定最终应用的属性。该列表按从最高优先级到最低优先级的顺序排序。

  1. 使用文本跨度对从 TextView 派生的类应用字符或段落级样式。
  2. 以编程方式应用属性。
  3. 直接将单个属性应用于视图。
  4. 将样式应用于视图。
  5. 默认样式。
  6. 将主题应用于视图集合、活动或整个应用程序。
  7. 应用某些特定于视图的样式,例如在 TextAppearance 上设置 TextView

图 2. 来自 span 的样式会覆盖来自 textAppearance 的样式。

TextAppearance

样式的一个限制是,您只能将一个样式应用于一个 View。但是,在 TextView 中,您还可以指定一个 TextAppearance 属性,该属性的功能类似于样式,如下例所示

<TextView
    ...
    android:textAppearance="@android:style/TextAppearance.Material.Headline"
    android:text="This text is styled via textAppearance!" />

TextAppearance 允许您定义特定于文本的样式,同时将 View 的样式保留用于其他用途。但是请注意,如果您在 View 上或在样式中直接定义任何文本属性,则这些值将覆盖 TextAppearance 值。

TextAppearance 支持 TextView 提供的样式属性的子集。有关完整属性列表,请参阅 TextAppearance

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

自定义默认主题

当您使用 Android Studio 创建项目时,它会默认将 Material Design 主题应用于您的应用,如您项目中的 styles.xml 文件中所定义。这个 AppTheme 样式扩展了来自 Support Library 的主题,并包括对关键 UI 元素(例如 应用栏浮动操作按钮)使用的颜色属性的覆盖,如果使用。因此,您可以通过更新提供的颜色快速自定义应用程序的颜色设计。

例如,您的 styles.xml 文件类似于以下内容

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
</style>

样式值实际上是对项目 res/values/colors.xml 文件中定义的其他 颜色资源 的引用。这就是您编辑以更改颜色的文件。查看 Material Design 颜色概述,以使用动态颜色和额外的自定义颜色改善用户体验。

一旦您知道自己的颜色,请更新 res/values/colors.xml 中的值

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--   Color for the app bar and other primary UI elements. -->
    <color name="colorPrimary">#3F51B5</color>

    <!--   A darker variant of the primary color, used for
           the status bar (on Android 5.0+) and contextual app bars. -->
    <color name="colorPrimaryDark">#303F9F</color>

    <!--   a secondary color for controls like checkboxes and text fields. -->
    <color name="colorAccent">#FF4081</color>
</resources>

然后,您可以覆盖您想要的任何其他样式。例如,您可以更改活动背景颜色,如下所示

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    ...
    <item name="android:windowBackground">@color/activityBackground</item>
</style>

有关您可以在主题中使用的属性列表,请参阅 R.styleable.Theme 中的属性表。在为布局中的视图添加样式时,您还可以通过查看视图类引用中的“XML 属性”表来查找属性。例如,所有视图都支持 来自基本 View 类别的 XML 属性

大多数属性应用于特定类型的视图,有些属性应用于所有视图。但是,在 R.styleable.Theme 中列出的一些主题属性适用于活动窗口,而不是布局中的视图。例如, windowBackground 会更改窗口背景,而 windowEnterTransition 会定义一个在活动启动时使用的过渡动画。有关更多详细信息,请参阅 使用动画启动活动

Android Support Library 还提供其他属性,您可以使用这些属性来自定义从 Theme.AppCompat 扩展的主题,例如前面示例中显示的 colorPrimary 属性。这些属性最好在 库的 attrs.xml 文件 中查看。

Support Library 还提供了其他主题,您可能希望扩展这些主题,而不是前面示例中显示的主题。查看可用主题的最佳位置是 库的 themes.xml 文件

添加特定于版本的样式

如果 Android 的新版本添加了您想要使用的主题属性,则可以在保持与旧版本兼容的同时将这些属性添加到您的主题中。您所需要做的只是将另一个 styles.xml 文件保存在包含 资源版本限定符values 目录中

res/values/styles.xml        # themes for all versions
res/values-v21/styles.xml    # themes for API level 21+ only

由于 values/styles.xml 文件中的样式适用于所有版本,因此您在 values-v21/styles.xml 中的主题可以继承它们。这意味着您可以通过从“基本”主题开始,然后在您特定于版本的样式中扩展它来避免重复样式。

例如,要为 Android 5.0(API 级别 21)及更高版本声明窗口过渡,您需要使用新的属性。因此,您在 res/values/styles.xml 中的基本主题可能如下所示

<resources>
    <!-- Base set of styles that apply to all versions. -->
    <style name="BaseAppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="colorPrimary">@color/primaryColor</item>
        <item name="colorPrimaryDark">@color/primaryTextColor</item>
        <item name="colorAccent">@color/secondaryColor</item>
    </style>

    <!-- Declare the theme name that's actually applied in the manifest file. -->
    <style name="AppTheme" parent="BaseAppTheme" />
</resources>

然后,在 res/values-v21/styles.xml 中添加特定于版本的样式,如下所示

<resources>
    <!-- extend the base theme to add styles available only with API level 21+ -->
    <style name="AppTheme" parent="BaseAppTheme">
        <item name="android:windowActivityTransitions">true</item>
        <item name="android:windowEnterTransition">@android:transition/slide_right</item>
        <item name="android:windowExitTransition">@android:transition/slide_left</item>
    </style>
</resources>

现在您可以在清单文件中应用 AppTheme,系统将为每个系统版本选择可用的样式。

有关为不同设备使用备用资源的更多信息,请参阅 提供备用资源

自定义小部件样式

框架和 Support Library 中的每个小部件都有一个默认样式。例如,当您使用来自 Support Library 的主题为您的应用程序设置样式时, Button 的实例将使用 Widget.AppCompat.Button 样式进行设置样式。如果您想将不同的窗口小部件样式应用于按钮,则可以使用布局文件中的 style 属性来执行此操作。例如,以下内容将应用库的无边框按钮样式

<Button
    style="@style/Widget.AppCompat.Button.Borderless"
    ... />

如果您想将此样式应用于所有按钮,则可以在主题的 buttonStyle 中声明它,如下所示

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="buttonStyle">@style/Widget.AppCompat.Button.Borderless</item>
    ...
</style>

您还可以扩展窗口小部件样式,就像 扩展任何其他样式 一样,然后在您的布局或主题中应用您的自定义窗口小部件样式。

其他资源

要了解有关主题和样式的更多信息,请参阅以下其他资源

博客文章