根据构建时的 Android 版本更改主题和样式

Changing themes and styles based on the Android version on build

我知道我可以为不同的 API 级别添加不同的 XML,例如为 values-v21 和 values-v19 设置不同的样式。我想了解的是构建系统实际上如何处理这些不同的值?因此,例如,如果我的大部分样式在所有 API 中都是通用的,并且一种样式中的一项在 21 和其他样式之间发生变化,我是否:

1) 将整个styles.xml复制到v21中,并更改我需要更改的一个值

2) 只添加v21

下改成styles.xml的那个样式

3) 只添加在 21 岁以下改变的那一种款式的那一件

这很令人困惑,我找不到任何关于构建过程如何处理合并样式的文档。

规则很明确:

  1. 虽然运行Androidselects the best-matching style
  2. 如果所选样式是子样式,Android merges 其项与父样式最匹配

    如果您通过引用提供可变项,只需定义其值以匹配所选 api 版本。

    <style name="SomeStyle">
        <item name="someColor">@color/some_color</item>
    </style>
    

对于 API 21,您可以在 color-v21 文件夹中放置 some_color.xml,对于所有其他 api,您可以在 color 文件夹中放置此文件的通用版本级别。

示例:

您希望非 v21 具有以下样式API

    <style name="FinalStyle">
        <item name="commonText">It\'s a common text</item>
        <item name="specificDrawable">@drawable/icon</item>
        <item name="specificColor">@color/primary_color</item>
        <item name="specificText">non-v21</item>
    </style>

以及 v21 的以下样式 API

    <style name="FinalStyle">
        <item name="commonText">It\'s a common text</item>
        <item name="specificDrawable">@drawable/icon</item>
        <item name="specificColor">@color/secondary_color</item>
        <item name="specificText">v21</item>
    </style>

v21/non-v21API具体参数不同,通用参数通用

怎么做?

  • res/values/styles.xml

    <style name="BaseStyle">
        <item name="commonText">It\'s a common text</item>
        <item name="specificDrawable">@drawable/icon</item>
    </style>
    
    <style name="FinalStyle" parent="BaseStyle">
        <item name="specificColor">@color/primary_color</item>
        <item name="specificText">non-v21</item>
    </style>
    
  • res/values-v21/styles.xml

    <style name="FinalStyle" parent="BaseStyle">
        <item name="specificColor">@color/secondary_color</item>
        <item name="specificText">v21</item>
    </style>
    
  • res/drawable/icon.png 常用图标

  • res/drawable-v21/icon.png v21 图标

当Android在FinalStyle中搜索v21时,它从res/values-v21中选择FinalStyle定义作为最佳匹配样式,并与BaseStyle合并。在这个例子中还有另一个最佳匹配资源搜索,当 Android 搜索 @drawable/icon.

您只能为所有设备版本维护一个 styles.xml(默认)文件。

查看我对 How to remove repeating of similar styles in v19/styles.xml and v21/styles.xml files 的回答

这是写给遇到这个问题但仍然和我一样困惑的人的,即使在大量阅读和反复试验之后也是如此。希望这对您有所帮助。

文件夹结构如@Dmitry 所述。

res/values/styles.xml

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">

    <style name="AppBase" parent="Theme.MaterialComponents.NoActionBar">
        <!-- simple: overrides colorPrimary in parent theme -->
        <item name="colorPrimary">@color/brand_blue</item>
        <item name="colorSecondary">@color/brand_grey</item>
        <!-- sets the attributes in materialButtonStyle with style: myMaterialButton -->
        <!-- the materialButtonStyle attribute is what actually changes the button settings -->
        <item name="materialButtonStyle">@style/myMaterialButton</item>
    </style>

    <!-- this style consists of common 'attributes' among all API versions -->
    <!-- you can choose to add a parent to inherit an additional style -->
    <!-- unlike the materialButtonStyle attribute, this parent is not necessary to change the button settings -->
    <style name="myMaterialButton" parent="Widget.MaterialComponents.Button">
        <item name="cornerRadius">60dp</item>
        <item name="android:paddingVertical" tools:targetApi="26">20dp</item>
    </style>

    <!-- this will add on and override AppBase and should include any changes that differ from other API versions -->
    <style name="AppBaseChanges" parent="AppBase">
        <!-- to inherit myMaterialButton, you don't have to include it in here, since it's in AppBase -->
        <!-- however, if you want to extend myMaterialButton, create a new style as its child -->
        <item name="materialButtonStyle">@style/myMaterialButtonAPI_All</item>
    </style>

    <!-- make sure the parent is myMaterialButton to inherit/override its settings -->
    <!-- this will be picked for all APIs lower than other styles like this -->
    <style name="myMaterialButtonAPI_All" parent="myMaterialButton">
        <item name="backgroundTint">?attr/colorPrimary</item>
    </style>
</resources>

res/values-v2/styles.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <!-- restate the same declaration as the other xml file-->
    <style name="AppBaseChanges" parent="AppBase">
        <!-- use a different name (...API_2) for the overriding style -->
        <item name="materialButtonStyle">@style/myMaterialButtonAPI_2</item>
    </style>

    <style name="myMaterialButtonAPI_2" parent="myMaterialButton">
        <item name="backgroundTint">?attr/colorSecondary</item>
    </style>
</resources>

将清单主题设置为 AppBaseChanges。该应用只会选择一种 AppBaseChanges 样式来应用更改,因此请务必仔细覆盖正确的样式,以确保您从较低级别的版本继承。

出于某种原因,AndroidStudio 在所有预览主题方面都做得不好,所以在您认为它不起作用之前,请重新启动应用程序以查看更改。还有一些情况我不知道为什么它没有更新设置并且找不到它覆盖主题的位置。在这些情况下,您可以进一步挖掘,或者避免麻烦并直接将相关样式应用到视图。

这是上述示例主题的优先顺序。越高的样式,它的优先级越高,将覆盖较低的样式。

  1. myMaterialButtonAPI_AllmyMaterialButtonAPI_2
  2. AppBaseChanges(只选了一个)
  3. myMaterialButton
  4. Widget.MaterialComponents.Button
  5. AppBase
  6. Theme.MaterialComponents.NoActionBar