样式和主题

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

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

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

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

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

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

主题与样式

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

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

主题定义一组命名资源,样式、布局、小部件等都可以引用这些资源。主题为 Android 资源分配语义名称,例如colorPrimary

样式和主题旨在协同工作。例如,您可能有一个样式指定按钮的一部分是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属性应用样式,而是使用AndroidManifest.xml文件中的<application>标签或<activity>标签上的android:theme属性应用主题。

例如,以下是如何将 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 样式扩展了支持库中的主题,并包含对关键 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 支持库还提供其他属性,您可以使用这些属性自定义从 Theme.AppCompat 扩展的主题,例如前面示例中显示的 colorPrimary 属性。最好在 库的 attrs.xml 文件 中查看这些属性。

支持库中还提供其他一些主题,您可能希望扩展这些主题,而不是前面示例中显示的主题。查看可用主题的最佳位置是 库的 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,系统会为每个系统版本选择可用的样式。

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

自定义小部件样式

框架和支持库中的每个小部件都有一个默认样式。例如,当您使用支持库中的主题为您的应用设置样式时,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>

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

其他资源

要详细了解主题和样式,请参阅以下其他资源

博文