Mastering Level List Drawables in Android with Kotlin XML

In Android development, the <level-list> drawable is a versatile tool that allows you to switch between different drawable resources based on the current numerical level. This is particularly useful for creating dynamic UI elements, such as battery indicators, signal strength meters, or progress bars. This blog post dives into how to create and use <level-list> drawables effectively within your Android applications using Kotlin XML development.

What is a <level-list> Drawable?

A <level-list> is an XML-based drawable resource that manages a number of alternative drawables. Each drawable is associated with a level. When you set the level of the LevelListDrawable, the system loads and displays the corresponding drawable. This enables you to change the appearance of a View or UI component programmatically by adjusting the level of the drawable.

Why Use a <level-list> Drawable?

  • Dynamic UI: Easily switch drawables based on application state.
  • Efficiency: Reduces the need for multiple ImageView elements or dynamic drawable creation.
  • Maintainability: Keeps drawables organized and centrally managed in XML resources.

How to Create a <level-list> Drawable

Step 1: Define the <level-list> in XML

First, you need to create a new XML file in the res/drawable/ directory of your Android project. This file will define the <level-list> drawable and its associated levels.

<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/level0" android:maxLevel="0" />
    <item android:drawable="@drawable/level1" android:minLevel="1" android:maxLevel="1" />
    <item android:drawable="@drawable/level2" android:minLevel="2" android:maxLevel="2" />
    <item android:drawable="@drawable/level3" android:minLevel="3" />
</level-list>

Explanation:

  • The root element is <level-list>, which declares this as a LevelListDrawable resource.
  • Each <item> represents a drawable associated with a particular level or range of levels.
  • android:drawable specifies the drawable resource to use for this level.
  • android:minLevel and android:maxLevel define the inclusive range of levels for which this drawable will be selected. If only android:maxLevel is provided, it’s used for a specific level. If only android:minLevel is provided, it matches levels starting from the minimum.

Step 2: Create the Individual Drawables

Make sure you have the drawable resources (level0.xml, level1.xml, level2.xml, level3.xml) defined. These can be simple shapes, images, or other drawables.

For example, res/drawable/level0.xml:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="#FF0000" />
</shape>

And res/drawable/level1.xml:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="#00FF00" />
</shape>

And so on for level2.xml and level3.xml.

Step 3: Use the <level-list> Drawable in Your Layout

Now, apply the <level-list> drawable to an ImageView or other View in your layout XML:

<ImageView
    android:id="@+id/levelImageView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/my_level_list" />

Step 4: Set the Level Programmatically in Kotlin

In your Kotlin code, get a reference to the ImageView and update the level of the drawable:

import android.graphics.drawable.LevelListDrawable
import android.os.Bundle
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity

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

        val imageView: ImageView = findViewById(R.id.levelImageView)
        val levelListDrawable = imageView.drawable as? LevelListDrawable

        // Set the level to 2
        levelListDrawable?.level = 2
    }
}

Explanation:

  • First, you obtain a reference to the ImageView from your layout.
  • Next, you retrieve the drawable from the ImageView and cast it to a LevelListDrawable.
  • Finally, you set the level to a desired value (e.g., 2), which updates the displayed drawable accordingly.

Practical Examples

1. Battery Indicator

Create a <level-list> that shows different battery icons based on the battery level:

<level-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/battery_low" android:maxLevel="20" />
    <item android:drawable="@drawable/battery_medium" android:minLevel="21" android:maxLevel="80" />
    <item android:drawable="@drawable/battery_full" android:minLevel="81" />
</level-list>

Update the level based on the battery percentage:

val batteryLevel = getBatteryPercentage() // Function to retrieve current battery level
levelListDrawable?.level = batteryLevel

2. Signal Strength Indicator

A similar approach can be used for displaying signal strength. Define drawables for different signal levels and switch them accordingly:

<level-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/signal_level_0" android:maxLevel="0" />
    <item android:drawable="@drawable/signal_level_1" android:minLevel="1" android:maxLevel="1" />
    <item android:drawable="@drawable/signal_level_2" android:minLevel="2" android:maxLevel="2" />
    <item android:drawable="@drawable/signal_level_3" android:minLevel="3" />
</level-list>

Kotlin code to update signal strength:

val signalStrength = getSignalStrength() // Function to retrieve current signal strength
levelListDrawable?.level = signalStrength

Conclusion

<level-list> drawables offer a straightforward way to create dynamic UI elements in Android. By defining multiple drawables and associating them with different levels, you can easily update the appearance of your views based on application state or user interactions. This method is efficient, maintainable, and reduces complexity in your codebase, making it a valuable tool for any Android developer using Kotlin XML development.