preface

As an Android developer, I wonder if you have experienced this as well. As your project gets bigger and bigger, different shapes for rounded corners, different colors for transparency, and different sizes for shadow effects, they add more and more resource files

I think there are two reasons for this problem: one is the non-standard product design, the whole APP has no uniform design style; The second is the irregularities in coding during development

Android Dev Summit ’19 has a talk on Style and Theme, the video with Chinese subtitles is here

I’ve arranged the location of each topic for you

time content
02:14 Styling vs Theme
oneself Theme Overlay
from Color
caught Use and three techniques
24:00 The Material color
28:06 The Material layout
30:07 The Material shape
34:41 Dark Theme

In the comment section below the video, click the corresponding time to jump to the specified content

I have recently translated it into Chinese

  • Android Styling 1: Themes vs Styles

  • 【 原 文 】Android Styling attributes are common theme attributes

  • The benefits of using themes and theme attributes

  • 【 原 文 】 I think we are going to get some highlights done

This article summarizes the content of the videos and articles, describes how we can use theme and style to manage resource files more elegantly during development, and provides a number of practical tips

And provides the demo demo, the effect is as follows

See update 20200705 at the bottom of this article for details on the problem of the recreate black screen flicker

Understand the difference between Style and Theme

This is at 02.14 in the video

Style is a collection of View attributes. You can View Style as Map

, where key is the View Attribute and value is the Resource

Resource can be of the following types

In contrast, the key of Theme is “Theme property”. Obviously, colorPrimary in the image below is not a property of any View

A topic property is a bit like abstractions configuration into semantically named variables and tucking them into a Map for future use. A topic property is similar to a View property in that they are defined in attR in a similar way and their corresponding types, but there are still differences

When referencing topic properties, you can use? .attr syntax, where? Represents a search in the current topic

The advantages of the subject attribute

If our app needs to support regular and Pro versions with different primary colors, we just need to define two themes and configure different colorPrimary. Then we need to match the dark theme, so we just need to provide different values

If we have a Theme class that has an abstract property, colorPrimary, and four implementation classes that override the colorPrimary property, we get four variants. If we want to add new variants in the future, we just inherit from this abstract class and override the property. For those of you who have read the first translation, the scope of a topic is all the child nodes in the tree. In this way, we can easily implement the function of changing the main color of the program

To do this using styles, we first need to define four styles. Since the scope of styles is view-specific, we define four sets of styles for each View

summary

Simply put, Style and Theme have different scopes

Style only applies to a single View. Use the Style tag

A Theme applies to all child nodes in the tree and is used with the Theme tag

At any given moment, an application is running under a particular theme, such as when an activity is set to a particular theme

We should pay attention to the advantages of theme and style and use them flexibly

Theme Overlay

This part of the video is at 08.55

Topics are inherited. When multiple topics in the inheritance chain have the same property configured, the content at the bottom of the inheritance chain will take effect. In the figure below, if multiple topics declare colorPrimary, the content in theme.owl.pink will take effect (this is similar to Java’s inheritance).

Using this inheritance relationship, we can realize that part of the interface will use the blue theme under the pink theme

If we look at the inheritance relationship between the two topics, the parents of the two topics should be relatively similar, which seems wasteful because many properties are the same

Also, when you set one theme on top of another, you need to be careful not to overwrite what you want to keep

The Theme Overlay is a great way to solve this problem, and it’s not a new technology, it’s a skill

Next we only need to focus on what we want to change. Applying the declared theme in the following image will change only the values of colorPrimary and colorSecondary, leaving all other properties unchanged

Tip 1: Invert colors

The MaterialComponents provide a dark colored Theme Overlay

Use this Theme Overlay to make a part of a light Theme a dark Theme

Tip 2: Use the right Context

We know that topics are related to Context, and it’s important to use the right Context because of the inheritance of topics we mentioned above

Remember: Use the Context that is closest to the View

Better yet, use topic properties

Tip 3: Use a Theme Overlay in your code

If you want to use a Theme Overlay in your code, you can wrap it in a ContextThemeWrapper, which is what the Android: Theme tag does inside

Use Theme and Style

Color

This is at 12.36 of the video

The most important resource in an application is Color. Android defines Color in many ways, such as the Color tag, which is mainly made up of ARGB Color values

The Color tag can also refer to other Color tags. Note that you cannot refer to topic properties in the Color tag

So that’s static colors. Now let’s talk about stateful colors

The Color state list allows you to define different colors for different states. They can also be used as drawables, as in the following example, where a button has a Color when it is pressed or disabled

Now let’s look at how the Color state list is defined. We can see that the selector tag is used here. In this example, we have two items. The second item does not define any state, which means it is the default color. It is used if no other color can match the current state

There is a trick to sorting the Color state list in the order it is most likely to appear — or least likely to appear. Depending on the implementation principle behind it, the system will iterate through each item until it finds a match

The Android: Alpha tag is officially introduced in API 21 to set transparency, and in API 23 you can refer to theme properties. If you are using AppCompat, it is backwards compatible with API 14

Tip 1: Set transparency

We may encounter situations in development where we need different transparency for the same color. So we might copy the color values for different transparencies

You probably don’t want to change a color and then change the opacity of each of them. Here we can use a ColorStateList. We can use the default color function, only one item, and set the opacity here (0 to 1).

Tip 2: Convert ColorStateList to Drawable

We can pass the color directly to the View when we configure properties such as background. Internally, the system fills the color and wraps it as a ColorDrawable

But if you pass in a ColorStateList it won’t work, devices in API 28 and before will crash

This is because ColorStateListDrawable is stateless. In Android 10, the official addition of ColorStateListDrawable solves this problem

To get the same experience across all the apis, we can use a workaround, using backgroundTint

Here we set a rectangle with a solid color, followed by a backgroundTint pointing to a ColorStateList

The commonly used skill

This is at 17.35 in the video

Tip 1: Name your resources correctly

There are definitely resources in our project that are named this way, and they are named after the subject properties. This is how the default resource for a new Android Studio project is named

Your theme will look something like this, with the theme property pointing to the color resource of the same name

Doing so is not recommended

What we need is not a semantic name, but a literal name. We can name it with the brand Color, or we can name it according to the Color of the Material Color System

Tip 2: Use a uniform style name

You’ve probably seen styles like this, one called AppTheme and one called Toolbar, and you can tell by their names what they do

But if we add a third style, its purpose is not obvious, and we cannot distinguish between a theme and a style

We can agree on a naming convention for this

The first part is Style Type: Theme, Style, text Overlay, shape Overlay, and so on

The second part is Group name: the name of the application is usually used. In case of multiple modules, the name of the module can also be used

The third part is the sub-group name: this name is usually used for widgets, that is, the name of the View that uses the style

The fourth part is the Variant name: this is optional, it is the variable of the topic

Going back to the original example, this is what the modification to our agreed naming convention looks like

Here is an interesting thing to note, in the Android System,. Is a very magical symbol, and there is an implicit inheritance system based on it

The above Widget. The MyApp. The Toolbar. Blue actually inherited in the middle of the subject

This naming convention allows code review to visually determine if a style or theme is incorrectly used. In the figure below, it is clear that the style tag is being used, but a theme is being passed in

You can even use Lint to solve this problem, more on that

Tip 3: Split multiple files

A simple model

Classify resource type files into standard categories

  • theme.xml: Theme and Theme Overlay
  • type.xml: font, text appearance, text size, font file, etc
  • style.xml: Only Widget style
  • dimens.xml colors.xml strings.xml: Other types are classified as actual resource types

Complex patterns

Complex modes are grouped logically, such as shape related ones in shap.xml, and if you want to implement a full screen UI, you can control status/navigation bar colors, whether they are displayed, etc

This works well in the Android view of Android Studio. As shown in the figure below, you can clearly see the light theme and dark theme files

Material

Color system

This part of the video is at 24:00

The system is built on a large number of semantically named variables that are known as “subject properties.” The way it works is that the library displays the theme properties associated with the semantically named colors, and the developer is responsible for providing the values for those colors. In the library, build all the widgets with these colors

For the color system, developers need to know some common colors

ColorPrimary and colorSecondary are the main colors of the APP brand, Variant is the contrast color of the main color; ColorSurface is very useful, it is responsible for the color on the surface of certain controls; ColorError is a warning color for errors, so you don’t need to hard-code these colors when using them

The color system also provides colors named On, which are guaranteed to have colors that contrast with similarly named colors. For example, colorOnPrimary will always contrast with colorPrimary

You can configure these colors in your own theme. Note that you don’t need to configure all the colors here. If you inherit some Material themes, they will provide default colors for all the colors. The colorSurface defined in the Material Light theme is used

Then you can use the colors in a layout or style

skills

A useful trick is to combine these colors with a ColorStateList

For example, if we want to make a divider, instead of creating a new color called colorDivider, we can just take a color from the colorOnSurface, which will definitely contrast with the background color

And it responds to different themes. Under light themes, 20% colorOnSurface is a black with white. Under a dark theme, the colorOnSurface will turn white and the 20% colorOnSurface will provide a suitable contrast

Semantically named colors are useful because you can eliminate a lot of color definitions

Typesetting system

This is at 28:06 in the video

In design, it is common to use a fixed number of font sizes, such as heading 1, heading 2, text body, subheading, buttons, and so on

These are implemented as subject properties

You can then reference these topic properties in your application

Material Text is very powerful, it can set the line height, if you have a problem with setting the line height and it doesn’t work, use the Material component to solve this problem

The shape of system

This is at 30:07 in the video

Material uses Shape System, which provides subject properties for small, medium and large components. Note that if you want to set a Shape on a custom component, you might want to use MaterialShapeDrawable as its background, which can understand and implement the Shape

This is defined by ShapeAppearance, which, like TextAppearance, is a configuration for the shape system

It consists of several components

The first is the cornerFamily, which supports rounded and cut corners, the direction of rounded corners, and so on

ShapeAppearance also supports overlays, which allow you to change specific widgets

One thing to note when using overlays is that many Material components have their own ShapeAppearance overlay, such as BottomSheet, which removes the rounded corners at the bottom

skills

The shape system is implemented by MaterialShapeDrawable

A powerful feature of MaterialShapeDrawable is that it has an attribute called Interpolation

Use it to animate the shape system. If its value is 0, the configured shape will not take effect. If its value is 1, the shape system will be fully applied to the Drawable

Dark theme

This is at 34:41 in the video

The adaptation of the dark theme is very simple, you can set the current theme and get the current theme through the code

You can use the Material component’s DayNight so that the theme property’s color value changes when the dark theme is turned on/off

Tip 1: Extract topics

Many times just doing the above two steps doesn’t work well with a dark theme. For example, our app works with a light theme, with dark content on a light background

With an evening theme, it might look like this

And this is what we want

This is due to hard coding when setting the color

In fact, it is better to use the theme attribute in this case

If we want colorPrimary to use different colors under different themes, how do we set it?

You might define color values for dark themes in the values-night/colors. XML, but this is not recommended!

The best thing to do is to extract the common parts into the base theme and then configure different attributes for the light and dark themes on top of that

Tip 2: Use of ColorPrimary

Sometimes our colorPrimary is a light color, such as blue in the image below, but for a dark theme we want to use a darker color, such as? Attr /colorSurface, Material components have been converted for us internally, directly used? Attr/colorPrimarySurface can

Demo

Demo address is here, if you feel helpful, click a little star ~ 😉

20200705 update

Many people in the comments section felt that when recreating the activity, there was a black screen effect, and the toggle was very stiff. In order to solve this problem, I looked up the relevant material. Someone in Issuetracker mentioned the issue of the activity.create() method causing the black screen, but the official mark was “assigned” and did not provide a specific time for resolution

Therefore, we have to look for other approaches, and considering that constrconstration causes an activity to rebuild, we can consider doing something with animations, such as gently transitioning transparency

This is what it looked like before

The transition isn’t exactly perfect, but it’s better than the initial gruff switch

Another option is to use finish + startActivity and close the animation when restarting the activity without using recreate, but this will cause the original data to be lost, and even the ViewModel will not be the same instance, so I don’t like this approach very much

The use of View.getDrawingCache () mentioned in the comments section is covered in this article and won’t be covered here

About me

My name is Flywith24 and my blog content has been sorted here. Click on the Watch in the upper right corner to get updates of my posts 😉

  • The Denver nuggets

  • Small column

  • Github