Custom Theme Attributes in Android: Kotlin & XML Guide

In Android development, creating a consistent and maintainable user interface is paramount. Using custom theme attributes allows developers to define UI properties that can be applied consistently across the application, enabling easy theme changes and reducing code duplication. Kotlin and XML together offer a robust environment for achieving this. This blog post will guide you through the process of creating and using custom theme attributes in Kotlin-based Android projects.

What are Theme Attributes?

Theme attributes, also known as typed arrays in the context of themes, are references to resources defined in a theme. They provide a way to abstract specific property values into a central location (i.e., your theme), making it easier to apply consistent styles throughout your app. Using theme attributes, you can define colors, dimensions, fonts, and other attributes that affect the look and feel of your UI elements.

Why Use Custom Theme Attributes?

  • Consistency: Ensure uniform styling across your app.
  • Maintainability: Update the look and feel from a single point, without modifying individual UI elements.
  • Theming Support: Easily switch between different themes (e.g., light and dark mode) by changing the theme attributes.
  • Reduced Duplication: Avoid hardcoding style values in multiple places.

Step-by-Step Guide to Creating Custom Theme Attributes in Android with Kotlin and XML

Step 1: Define the Custom Attribute

First, define the custom attributes in a declare-styleable resource within your res/values directory. Create a new XML file (e.g., attrs.xml) or add to an existing one.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CustomThemeAttributes">
        <attr name="customTextColor" format="color" />
        <attr name="customBackgroundColor" format="color" />
        <attr name="customTextSize" format="dimension" />
        <attr name="customFontFamily" format="string" />
    </declare-styleable>
</resources>

In this example, we’ve defined the following custom attributes:

  • customTextColor: A color for text.
  • customBackgroundColor: A background color.
  • customTextSize: A text size dimension.
  • customFontFamily: A font family string.

Step 2: Define the Theme

Next, define the theme that uses these attributes. In your res/values/themes.xml file (or create one if it doesn’t exist), create a new theme and assign values to your custom attributes using the ?attr/attributeName syntax.

<resources xmlns:tools="http://schemas.android.com/tools">
    <!-- Base application theme. -->
    <style name="Base.Theme.MyApp" parent="Theme.Material3.DayNight.NoActionBar">
        <!-- Customize your light theme here. -->
        <item name="customTextColor">@color/light_text</item>
        <item name="customBackgroundColor">@color/light_background</item>
        <item name="customTextSize">16sp</item>
        <item name="customFontFamily">sans-serif</item>
    </style>

    <style name="Theme.MyApp" parent="Base.Theme.MyApp" />

    <style name="Theme.MyApp.Dark" parent="Theme.Material3.Dark.NoActionBar">
        <item name="customTextColor">@color/dark_text</item>
        <item name="customBackgroundColor">@color/dark_background</item>
        <item name="customTextSize">18sp</item>
        <item name="customFontFamily">monospace</item>
    </style>
</resources>

Ensure that the color and dimension resources (@color/light_text, @color/light_background, etc.) are defined in your colors.xml and dimens.xml files respectively.

<!-- res/values/colors.xml -->
<resources>
    <color name="light_text">#000000</color>
    <color name="light_background">#FFFFFF</color>
    <color name="dark_text">#FFFFFF</color>
    <color name="dark_background">#333333</color>
</resources>

Step 3: Apply the Theme to Your Application

In your AndroidManifest.xml, apply the defined theme to your application or specific activities.

<application
    android:theme="@style/Theme.MyApp">
    ...
</application>

Or, for dark mode:

<application
    android:theme="@style/Theme.MyApp.Dark">
    ...
</application>

Step 4: Access Custom Theme Attributes in Kotlin

To use these custom theme attributes in your Kotlin code, you need to resolve them from the context.


import android.content.Context
import android.util.TypedValue
import androidx.core.content.ContextCompat

object ThemeUtils {

    fun getColorFromAttr(context: Context, attrId: Int): Int {
        val typedValue = TypedValue()
        context.theme.resolveAttribute(attrId, typedValue, true)
        return typedValue.data
    }

    fun getDimensionFromAttr(context: Context, attrId: Int): Float {
        val typedValue = TypedValue()
        context.theme.resolveAttribute(attrId, typedValue, true)
        return typedValue.getDimension(context.resources.displayMetrics)
    }

    fun getStringFromAttr(context: Context, attrId: Int): String {
        val typedValue = TypedValue()
        context.theme.resolveAttribute(attrId, typedValue, true)
        return typedValue.string.toString()
    }
}

Step 5: Use Custom Theme Attributes in UI Elements

Now, you can use the utility functions to apply the custom theme attributes to your UI elements.


import android.os.Bundle
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import com.example.myapp.ThemeUtils // Ensure this matches your package name
import com.example.myapp.R

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val textView: TextView = findViewById(R.id.myTextView)

        // Apply custom theme attributes
        textView.setTextColor(ThemeUtils.getColorFromAttr(this, R.attr.customTextColor))
        textView.setBackgroundColor(ThemeUtils.getColorFromAttr(this, R.attr.customBackgroundColor))
        textView.textSize = ThemeUtils.getDimensionFromAttr(this, R.attr.customTextSize) / resources.displayMetrics.density
        textView.typeface = Typeface.create(ThemeUtils.getStringFromAttr(this, R.attr.customFontFamily), Typeface.NORMAL)
    }
}

Step 6: Create the Layout File

In your layout file (e.g., activity_main.xml), define a TextView to apply these styles.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/myTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello, Custom Theme!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Best Practices

  • Naming Conventions: Use clear and consistent naming conventions for your custom attributes (e.g., customTextColor, customBackgroundColor).
  • Resource Definition: Always define resource values (colors, dimensions) in their respective res directories.
  • Theme Variants: Utilize different themes for different modes (e.g., light and dark) to ensure a seamless user experience.
  • Attribute Grouping: Group related attributes under a single declare-styleable for better organization.

Conclusion

Creating custom theme attributes in Kotlin XML development is a powerful technique for maintaining a consistent and themeable user interface in Android applications. By abstracting style properties into theme attributes, you can ensure that your app remains easy to maintain, update, and adapt to different user preferences or branding requirements. Following the steps outlined in this guide, you can effectively implement and utilize custom theme attributes in your projects.