📜 ⬆️ ⬇️

Themes, styles and other


Almost all developers know that there are Themes in 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 styling mechanism.

Content


Introduction
Attributes for RectView
Style for RectView
Default style
Theme attribute for the RectView style
Theme at the level of Activi
Theme at the level of twist
Order of Applying Attribute Values
Resource Topics
Results

Introduction


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

We need a simple custom view that will draw a rectangle with the desired color within the borders 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 rectangle from the layout.
To do this, we need to add a new attribute, add to the file attrs.xml

 <resources> <attr name="rectColor" format="color"/> </resources> 

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

 <org.berendeev.themes.RectView android:id="@+id/text2" android:layout_width="100dp" app:rectColor="@color/colorPrimary" android:layout_height="100dp"/> 

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

Let's teach RectView to understand the rectColor attribute, add an attribute group

 <resources> <attr name="rectColor" format="color"/> <declare-styleable name="RectView"> <attr name="rectColor"/> </declare-styleable> </resources> 

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

And 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 a dozen of them - it would be extremely inconvenient for us each time to set all these attributes in the layout for similar elements.

The decision to make everything in style.

 <style name="DefaultRectViewStyle"> <item name="rectColor">@color/colorAccent</item> </style> 

Now we can create as many as I wish with the same design, specifying the style.

 <org.berendeev.themes.RectView android:id="@+id/text2" android:layout_width="100dp" style="@style/DefaultRectViewStyle" android:layout_height="100dp"/> 

Default style


Now we want all the default views to have some kind of style. 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 RectViews that we add to the layout will have the default style DefaultRectViewStyle.

Theme attribute for the RectView style


The default is good, but I would like to control the design more flexibly. It is convenient to set the style of a specific view on the entire application or for an individual Activity.

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

 <attr name="rectViewStyle" format="reference"/> 

And teach our rectView to understand this theme attribute. 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 a name rectViewStyle and a style type value is specified in the theme, this style will apply to all RectViews with this theme.

Theme at the level of Activi


Set the value of the rectViewStyle attribute in our theme.

 <resources> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> <item name="rectViewStyle">@style/LocalRectViewStyle</item> </style> </resources> 

Specify the theme in the application manifest.

 <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.berendeev.themes"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application> </manifest> 

All activit will install the AppTheme theme by default. We set the RectView design for the entire application.

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

 <activity android:name=".MainActivity" android:theme="@style/MyTheme"> 

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

Theme at the level of twist


At the level of twist, we do not set the theme again, but overwrite the necessary attribute values, so it is not necessary to inherit from the standard theme. The parent can be empty or, for example, we can inherit from one of the overlays ThemeOverlay.AppCompat.

Here we set the subject of the MyOverlay to the LinearLayout group and all its descendants along the hierarchy.

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:theme="@style/MyOverlay"> <org.berendeev.themes.RectView android:layout_width="100dp" android:layout_height="100dp"/> </LinearLayout> 

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

 <style name="MyOverlay"> <item name="rectViewStyle">@style/LocalRectViewStyle</item> </style> 

Order of Applying Attribute Values



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

Resource Topics


Theme 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.

 <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" > <solid android:color="?attr/colorPrimary"/> </shape> 

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

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

Results



PS: If the article is interesting, I will write a more advanced article or article with a large number of real examples.

Source: https://habr.com/ru/post/453812/


All Articles