Themes, styles and others


Almost all developers know that there are Themes in the android, but their use is usually limited to copying pieces of xml from Stack Overflow or other resources. There is information on the Internet on topics, but this is usually just a recipe for how to achieve a certain result. In this article, I tried to give an introductory overview of the android stylization mechanism.

Content


Introduction
Attributes for RectView Style
for RectView
Default Style Theme
Attribute for RectView Style
Theme at Activity Level
Theme at View Level
Procedure for Applying the Attribute Values
Themes in Resources
Summary

Introduction


Styles and Themes in android are mechanisms that allow you to separate the design details (for example, color, font size, etc.) from the structure of the UI. To understand how this works, a simple example with custom view will help us.

We will need a simple custom view that will draw a rectangle with the desired color within the bounds of the view.

class RectView @JvmOverloads constructor(
    context: Context,
    attrSet: AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attrSet, defStyleAttr) {
    private val paint = Paint().apply {
        color = Color.BLUE
    }
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), paint)
    }
}



Attributes for RectView


Now I would like to be able to change the color of the layout rectangle.
To do this, we need to add a new attribute, add attrs.xml to the file


Now in code we can access the id of this attribute through R.attr.rectColor and
in the layout of the screen we can use the app: rectColor attribute.


But RectView does not yet know that there is an attribute where you can take the color for the rectangle.

Let's teach RectView to understand the rectColor attribute, add a group of attributes


In the R class, 2 new fields were generated:
R.styleable.RectView is an array of id attributes, currently it is an array of one element R.attr.rectColor
R.styleable.RectView_rectColor is the attribute id index in the R.styleable.RectView array, those. we can get the id of the attribute and so R.styleable.RectView [R.styleable.RectView_rectColor]

And we will add support for the rectColor attribute to the code.

init {
        val typedArray = context.theme.obtainStyledAttributes(
            attrSet,
            R.styleable.RectView,
            0,
            0
        )
        try {
            paint.color = typedArray.getColor(
                R.styleable.RectView_rectColor,
                Color.BLUE
            )
        } finally {
            typedArray.recycle()
        }
}

Full code
class RectView @JvmOverloads constructor(
    context: Context,
    attrSet: AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attrSet, defStyleAttr) {
    private val paint = Paint().apply {
        color = Color.BLUE
    }
    init {
        val typedArray = context.theme.obtainStyledAttributes(
            attrSet,
            R.styleable.RectView,
            0,
            0
        )
        try {
            paint.color = typedArray.getColor(
                R.styleable.RectView_rectColor,
                Color.BLUE
            )
        } finally {
            typedArray.recycle()
        }
    }
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), paint)
    }
}


Now we can change the color of the rectangle from the layout:



Style for RectView


Now we have only one attribute, but imagine that there are ten of them - it would be extremely inconvenient for us to set all these attributes in the layout for similar elements each time.

The decision to make everything in style.


Now we can create any kind of view with the same appearance by specifying style.


Default style


Now we want all the views to have some kind of style by default. To do this, we will specify the default style for the obtainStyledAttributes method:

context.theme.obtainStyledAttributes(
            attrSet,
            R.styleable.RectView,
            0,
            R.style.DefaultRectViewStyle
)

It's enough. Now all the RectView that we add to the layout will have the default style DefaultRectViewStyle.

RectView Style Theme Attribute


The default value is good, but I would like to control the layout more flexibly. It is convenient to set the style of a particular view for the whole application or for a separate Activity.

For this we need a new attribute. We will set its value in the theme, the value will be the style that determines the appearance of the RectView.


And teach our rectView to understand this theme attribute. By passing the third parameter R.attr.rectViewStyle to the obtainStyledAttributes method.


context.theme.obtainStyledAttributes(
                attrSet,
                R.styleable.RectView,
                R.attr.rectViewStyle,
                R.style.DefaultRectViewStyle
            )

Now, if an item with the name rectViewStyle and a value of type style is specified in the topic, this style will apply to all RectView with this theme.

Activity Level Theme


We set the value of the rectViewStyle attribute in our topic.


Specify the topic in the application manifest.


All activity will be set to the default AppTheme theme. We set the RectView design for the entire application.

The topic can also be set for a specific Activity in the manifest.


It is important here that the theme that we will set at the application or activity level should be inherited from the standard theme. For example, Theme.AppCompat. Otherwise, we get crash in runtime.
java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.

View Topic


At the view level, we do not reinstall the theme, but overwrite the required attribute values, so we don’t need to inherit from the standard theme. A parent can be empty, or for example, we can inherit from one of ThemeOverlay.AppCompat overlays.

Here we set the MyOverlay theme for the LinearLayout group and all its descendants in the hierarchy.


In the subject, we can do without a parent, because we just modify the activity theme.


How to apply attribute values



If for the view we define the attribute values ​​both in the layout and in the style and in the theme, then the order of choosing the value will be as follows. The highest priority is the value specified in the layout, i.e. if the value is set in the layout, then the style and theme will be ignored, then the style goes on, then the theme and in last place the default style.

Resource Topics


Topic values ​​can be used in resources. For example, in drawable we can set a color that will depend on the color set in the theme.


Now the color of the rectangle depends on the value of the colorPrimary attribute in the theme. The attribute can be any. For example, we can set our attribute in resources and set its value in the subject.

Such a trick can be used in all resources, for example, in selector or in vector drawing. This makes the design more structured and flexible. We can quickly change the theme for all activity.

Summary


  • The theme and style at the resource level are one and the same, but are used differently.
  • A theme can contain other themes, just meanings or styles for the view.
  • The style is indicated in the layout at the view level. LayoutInflater calculates the style values ​​and passes them as an AttributeSet to the View constructor.
  • Themes is a mechanism that allows you to define the appearance globally for the entire activity.
  • The topic values ​​can be changed for the element (and descendants) in the view hierarchy.
  • The theme values ​​can be used not only in View, but also in resources.

PS: If the article is interesting, I’ll write a more advanced article or an article with a lot of real examples.

Also popular now: