In the previous articles in the Android Style System series, we covered the difference between theme backgrounds and styles, and why it’s a good idea to break down what you’re implementing by theme backgrounds and common theme background properties. Please click on the link to review:

  • Android style system | topic background and style
  • The theme of Android style system | common background attributes

This allows us to isolate changes in the theme background by creating fewer layouts or styles. In real development, you usually want to change the color based on the theme background, so you should always reference the color through the theme background attribute.

This means that you can consider the following Code to have Code smell:

<! Copyright 2019 Google LLC.spdx-license-Identifier: Apache-2.0 --> Copyright 2019 Google LLC.spdx-license-Identifier: Apache-2.0 -->
<View …
  android:background="@color/white"/>
Copy the code

Instead, you should use the theme background property, which allows you to change the color by theme, for example, providing a different value in a dark theme:

<! Copyright 2019 Google LLC.spdx-license-Identifier: Apache-2.0 --> Copyright 2019 Google LLC.spdx-license-Identifier: Apache-2.0 -->
<Viewandroid:background="? attr/colorSurface"/>
Copy the code

Even if you don’t currently support other themes (what, your app doesn’t support dark themes yet?) We still recommend that you do this, because it makes it much easier to adopt new themes.

Qualified Colors file

You can change the color by adding different values in different configurations (for example, define @color/foo in res/values/colors.xml and in alternative values in res/values-night/colors.xml), However, we still recommend that you use the theme background properties instead. The distinction between color hierarchies forces you to give the colors semantic names, in other words, you should not name the color @color/white while providing a dark variant for the dark mode, which would be very confusing. So, you might want to use a semantic name, such as @color/background. The problem with this approach is that it incorporates color declarations and concrete values, so it does not indicate that colors can or can vary with the topic background.

The @colors change also encourages you to create more colors. If you want to use a new semantically named color with the same value in a different context (that is, not a background color but the same color should be used), you still need to create a new entry in the Colors file. By using the topic background attribute, we can separate the declaration of semantic colors from the values that provide them, and make it clearer to the user that the colors vary with the topic background (because they use? Attr/syntax). By keeping the color declarations as literals, you can customize the color palettes your application uses and modify them at the topic background level, making color.xml smaller and easier to maintain.

The added benefit of this approach is that layouts/styles reference these colors for greater reusability. Since theme backgrounds can be overwritten or changed, this indirectly means that you don’t need to create other layouts or styles to change certain colors — you can use different theme backgrounds in the same layout.

Always use?

In some cases, you may not want to change the color to match the theme background. For example, as mentioned in the Material Design specification documentation, you might want to use the same type of color for both light and dark themes.

In this particular case, it is perfectly appropriate to refer directly to the color resource:

<! Copyright 2019 Google LLC.spdx-license-Identifier: Apache-2.0 --> Copyright 2019 Google LLC.spdx-license-Identifier: Apache-2.0 -->
<FloatingActionButtonapp:backgroundTint="@color/owl_pink_500"/>
Copy the code

Current Development

When using ColorStateLists, you may also not directly reference the theme background attribute in your layout/style.

<! Copyright 2019 Google LLC.spdx-license-Identifier: Apache-2.0 --> Copyright 2019 Google LLC.spdx-license-Identifier: Apache-2.0 -->
<Viewandroid:background="@color/primary_20"/>
Copy the code

If primary_20 is a ColorStateList, it might also be reasonable for it to reference the theme background property itself to get the color value (see below). ColorStateLists usually provide different colors for different states (pressed, disabled, etc.), but they also have another feature that can be used for theming. You can specify transparency values on selected colors:

<! Copyright 2019 Google LLC.spdx-license-Identifier: Apache-2.0 --> Copyright 2019 Google LLC.spdx-license-Identifier: Apache-2.0 -->
<selector... <item android:alpha="0.20" android:color="? attr/colorPrimary" />
</selector>
Copy the code

This monomial ColorStateList (that is, providing a single default color instead of a different color for each state) helps reduce the number of color resources you need to maintain. Instead of manually setting the alpha value for your primary color by defining a new color resource, it does so by changing the colorPrimary in the current theme background. If your original color changes, you only need to update it in one place; you don’t need to adjust all the places that have been updated.

While this technique is useful, there are a few caveats:

  1. If the specified color also has an alpha value, alpha is merged. For example, applying 50% alpha to 50% opaque white produces 25% white:
<! Copyright 2019 Google LLC.spdx-license-Identifier: Apache-2.0 --> Copyright 2019 Google LLC.spdx-license-Identifier: Apache-2.0 -->
<selector... <item android:alpha="0.50" android:color="#80ffffff" />
</selector>
Copy the code

Therefore, it is best to specify theme background colors as completely opaque, and then modify their alpha using ColorStateLists.

  1. Only the alpha component is added in the API 23, so if you are lower than the minimum SDK version, be sure to use to support this behavior AppCompatResources. GetColorStateList (and always use android: alpha namespace, Never use the APP :alpha namespace.

  2. In general, we use shorthand to set the color to Drawable, for example:

<! Copyright 2019 Google LLC.spdx-license-Identifier: Apache-2.0 --> Copyright 2019 Google LLC.spdx-license-Identifier: Apache-2.0 -->
<Viewandroid:background="@color/foo"/>
Copy the code

The background of the View is a Drawable, which translates the given color intensity into a ColorDrawable. But there is no way to convert a ColorStateList to a Drawable (API 29 used ColorStateListDrawable to solve this problem).

However, we can get around this limitation in a roundabout way:

<! Copyright 2019 Google LLC.spdx-license-Identifier: Apache-2.0 --> Copyright 2019 Google LLC.spdx-license-Identifier: Apache-2.0 -->
<Viewandroid:background="@drawable/a_solid_white_rectangle_shape_drawable"
  app:backgroundTint="@color/some_color_state_list"/>
Copy the code

Make sure your backgroundTint supports the desired state of your View, for example, if it needs to be changed if disabled.

To enforce

Even if you’ve convinced yourself to use the theme background attribute and ColorStateList, how do you use it in your code base or your team? You can try to be vigilant during Code Review, but it doesn’t scale very well. A better approach is to rely on tools to solve this problem.

This article, Making Android Lint Theme Aware, Outlines how to add Lint checking to find uses for directly referenced colors and covers all the suggestions mentioned in this article.

Indirect use

The method of splitting colors into theme backgrounds using the theme background attribute and ColorStateList makes your layouts and styles more flexible, improves code reuse and keeps your code base lean and easy to maintain.

We will cover more of the use of thematic backgrounds and their interactions in future articles, so please stay tuned for more.