Android Button States with Kotlin XML: State List Drawables Tutorial

In Android development, State List Drawables, defined using the <selector> XML tag, are powerful tools for creating dynamic and visually appealing button states. These drawables change appearance based on the button’s current state, such as whether it is pressed, focused, or in its default state. Using state list drawables significantly enhances user experience by providing clear visual feedback on user interactions.

What is a State List Drawable?

A state list drawable is an XML file that defines a set of drawables. Each drawable is associated with one or more states of a view. When the state of the view changes, the system automatically selects and displays the drawable that matches the current state.

Why Use State List Drawables?

  • Improved User Experience: Provides visual feedback to the user, making the application more intuitive.
  • Consistency: Ensures a consistent look and feel across different devices and screen sizes.
  • Maintainability: Centralizes the definition of different states, making it easier to manage and update.

How to Create State List Drawables for Button States in Kotlin XML

Follow these steps to create state list drawables for common button states like pressed and focused:

Step 1: Create the Drawable Resources

Create new drawable resource files in your res/drawable directory to define the appearance of the button for different states.

Example: Default State (button_default.xml)

This drawable defines the default appearance of the button.

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="@color/colorPrimary"/>
    <corners android:radius="8dp"/>
    <padding
        android:left="10dp"
        android:top="10dp"
        android:right="10dp"
        android:bottom="10dp"/>
</shape>
Example: Pressed State (button_pressed.xml)

This drawable defines the appearance when the button is pressed.

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="@color/colorPrimaryDark"/>
    <corners android:radius="8dp"/>
    <padding
        android:left="10dp"
        android:top="10dp"
        android:right="10dp"
        android:bottom="10dp"/>
</shape>
Example: Focused State (button_focused.xml)

This drawable defines the appearance when the button is focused.

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="@color/colorAccent"/>
    <corners android:radius="8dp"/>
    <padding
        android:left="10dp"
        android:top="10dp"
        android:right="10dp"
        android:bottom="10dp"/>
    <stroke
        android:width="2dp"
        android:color="@color/black"/>
</shape>

Step 2: Create the State List Drawable XML

Create a new XML file (e.g., button_states.xml) in the res/drawable directory. Use the <selector> tag to define the different states and their corresponding drawables.

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Pressed State -->
    <item android:drawable="@drawable/button_pressed" android:state_pressed="true"/>

    <!-- Focused State -->
    <item android:drawable="@drawable/button_focused" android:state_focused="true"/>

    <!-- Default State -->
    <item android:drawable="@drawable/button_default"/>
</selector>

Key attributes and elements in the <selector> XML:

  • <selector>: The root element defining the state list drawable.
  • <item>: Specifies a drawable to use for a particular state.
    • android:drawable: Reference to the drawable resource.
    • android:state_pressed: True if the state is pressed, false otherwise.
    • android:state_focused: True if the state is focused, false otherwise.
    • Other state attributes can be used for different states, such as state_enabled, state_selected, etc.

Step 3: Apply the State List Drawable to a Button

In your layout XML file, apply the state list drawable as the background of your button.

<Button
    android:id="@+id/myButton"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Click Me"
    android:background="@drawable/button_states"/>

Here, android:background="@drawable/button_states" sets the background of the button to the state list drawable we created.

Step 4: Kotlin Implementation (if needed)

In your Kotlin code, you generally don’t need to do anything special for the state list drawable to work. However, if you need to dynamically change the state or background, you can do so programmatically.

Example: Changing Background Dynamically (Optional)
import android.os.Bundle
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity

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

        val myButton: Button = findViewById(R.id.myButton)

        // Example: Change the button's background dynamically (not usually needed)
        // myButton.background = resources.getDrawable(R.drawable.button_states, theme)
    }
}

In most cases, defining the android:background attribute in the XML layout is sufficient. The system handles the state changes automatically.

Example: Full Implementation

Colors (res/values/colors.xml)

<resources>
    <color name="colorPrimary">#6200EE</color>
    <color name="colorPrimaryDark">#3700B3</color>
    <color name="colorAccent">#03DAC5</color>
    <color name="black">#000000</color>
</resources>

Button Default State (res/drawable/button_default.xml)

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="@color/colorPrimary"/>
    <corners android:radius="8dp"/>
    <padding
        android:left="10dp"
        android:top="10dp"
        android:right="10dp"
        android:bottom="10dp"/>
</shape>

Button Pressed State (res/drawable/button_pressed.xml)

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="@color/colorPrimaryDark"/>
    <corners android:radius="8dp"/>
    <padding
        android:left="10dp"
        android:top="10dp"
        android:right="10dp"
        android:bottom="10dp"/>
</shape>

Button Focused State (res/drawable/button_focused.xml)

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="@color/colorAccent"/>
    <corners android:radius="8dp"/>
    <padding
        android:left="10dp"
        android:top="10dp"
        android:right="10dp"
        android:bottom="10dp"/>
    <stroke
        android:width="2dp"
        android:color="@color/black"/>
</shape>

State List Drawable (res/drawable/button_states.xml)

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Pressed State -->
    <item android:drawable="@drawable/button_pressed" android:state_pressed="true"/>

    <!-- Focused State -->
    <item android:drawable="@drawable/button_focused" android:state_focused="true"/>

    <!-- Default State -->
    <item android:drawable="@drawable/button_default"/>
</selector>

Layout File (res/layout/activity_main.xml)

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="16dp"
    android:paddingTop="16dp"
    android:paddingRight="16dp"
    android:paddingBottom="16dp"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/myButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Click Me"
        android:background="@drawable/button_states"/>

</RelativeLayout>

Conclusion

Using state list drawables in Android development enhances the user experience by providing clear visual feedback on user interactions. By defining different drawables for various states like pressed and focused, you can create more intuitive and visually appealing applications. This method is maintainable, consistent, and greatly improves the overall look and feel of your application.