Mastering Custom Attributes in Android with Kotlin XML and attrs.xml

In Android development, custom views offer a powerful way to create reusable and specialized UI components. When developing custom views, you often need to allow developers using your custom view to configure its appearance and behavior. This is achieved by defining custom attributes using the attrs.xml file in your Android project. In this comprehensive guide, we’ll explore how to define custom attributes for custom views in Kotlin XML development for Android.

What are Custom Attributes?

Custom attributes are XML attributes that you define in your project’s res/values/attrs.xml file. These attributes can then be used in your custom view’s XML layout to configure various aspects of its appearance or behavior.

Why Use Custom Attributes?

  • Flexibility: Allow developers to customize the appearance and behavior of your custom view through XML.
  • Reusability: Enable developers to reuse your custom view in different contexts with varying configurations.
  • Consistency: Provide a consistent and declarative way to configure custom views across different parts of an application.

Steps to Define and Use Custom Attributes

Step 1: Create the attrs.xml File

If it doesn’t already exist, create an attrs.xml file in the res/values directory of your Android project.


<?xml version="1.0" encoding="utf-8"?>
<resources>
</resources>

Step 2: Define Custom Attributes

Inside the attrs.xml file, define your custom attributes within a <declare-styleable> element. The name attribute of <declare-styleable> specifies the name of the custom view.


<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CustomTextView">
        <attr name="textColor" format="color" />
        <attr name="textSize" format="dimension" />
        <attr name="text" format="string" />
    </declare-styleable>
</resources>

In this example:

  • CustomTextView is the name of our custom view.
  • textColor, textSize, and text are the custom attributes we’re defining.
  • The format attribute specifies the type of each attribute (e.g., color, dimension, string).

Possible format values include:

  • string: For text values.
  • integer: For integer values.
  • boolean: For boolean values.
  • color: For color values (e.g., #RRGGBB or #AARRGGBB).
  • dimension: For size values (e.g., 16dp, 20sp).
  • float: For floating-point numbers.
  • enum: For a set of symbolic values.
  • flag: For a bitwise OR of symbolic values.

Step 3: Create Your Custom View Class

Create a Kotlin class for your custom view that extends View, TextView, or any other appropriate base class.


import android.content.Context
import android.graphics.Color
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatTextView

class CustomTextView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : AppCompatTextView(context, attrs, defStyleAttr) {

    init {
        attrs?.let {
            val typedArray = context.obtainStyledAttributes(
                it,
                R.styleable.CustomTextView,
                0,
                0
            )

            val textColor = typedArray.getColor(R.styleable.CustomTextView_textColor, Color.BLACK)
            val textSize = typedArray.getDimension(R.styleable.CustomTextView_textSize, 16f)
            val textValue = typedArray.getString(R.styleable.CustomTextView_text)

            setTextColor(textColor)
            setTextSize(textSize)
            text = textValue

            typedArray.recycle()
        }
    }
}

In this code:

  • The constructor takes a Context and an optional AttributeSet.
  • Inside the init block, we obtain the styled attributes using context.obtainStyledAttributes().
  • We then retrieve the values of the custom attributes using the appropriate get...() methods (e.g., getColor(), getDimension(), getString()).
  • Finally, we apply these values to our view’s properties.
  • It’s essential to call typedArray.recycle() after you’re done using the TypedArray to free up resources.

Step 4: Use the Custom View in XML Layout

Now, you can use your custom view in your XML layouts and specify the custom attributes.


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="16dp">

    <com.example.customview.CustomTextView
        android:id="@+id/customTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:textColor="#FF0000"
        app:textSize="20sp"
        app:text="Hello, Custom View!" />

</LinearLayout>

Make sure to add the xmlns:app attribute to your root layout element to specify the XML namespace for your custom attributes.

Step 5: Using enums

To use enums, you first define the enum attribute:


<declare-styleable name="CustomTextView">
    <attr name="textStyle" format="enum">
        <enum name="normal" value="0"/>
        <enum name="bold" value="1"/>
        <enum name="italic" value="2"/>
    </attr>
</declare-styleable>

And then in your Kotlin custom view:


import android.content.Context
import android.graphics.Typeface
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatTextView

class CustomTextView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : AppCompatTextView(context, attrs, defStyleAttr) {

    init {
        attrs?.let {
            val typedArray = context.obtainStyledAttributes(
                it,
                R.styleable.CustomTextView,
                0,
                0
            )

            val textStyleValue = typedArray.getInt(R.styleable.CustomTextView_textStyle, 0)

            val typeface = when (textStyleValue) {
                1 -> Typeface.BOLD
                2 -> Typeface.ITALIC
                else -> Typeface.NORMAL
            }

            setTypeface(null, typeface)

            typedArray.recycle()
        }
    }
}

Conclusion

Defining custom attributes is essential for creating flexible, reusable, and customizable custom views in Android. By following these steps, you can enable developers to easily configure your custom views through XML, making your components more versatile and user-friendly. Properly defined custom attributes help promote consistency and streamline UI development across different Android projects.