Android Kotlin: Optimizing Density-Specific Drawables (mdpi, hdpi, xhdpi)

In Android development, supporting a variety of screen densities is crucial for providing a consistent user experience across different devices. Density-specific drawables ensure that your app’s images and icons appear crisp and clear, regardless of the screen’s pixel density. This involves creating and managing drawables for various density qualifiers like mdpi, hdpi, xhdpi, xxhdpi, and xxxhdpi. This article explores how to effectively implement density-specific drawables in Kotlin XML-based Android projects.

Understanding Screen Density and Density Qualifiers

Screen density refers to the number of pixels within a given physical area on the screen. It is typically measured in dots per inch (dpi). Android uses several density qualifiers to categorize screens based on their density, ensuring the appropriate drawables are loaded for each device.

Common density qualifiers include:

  • mdpi (Medium DPI): Baseline density (160 dpi).
  • hdpi (High DPI): Approximately 1.5x the baseline density (240 dpi).
  • xhdpi (Extra High DPI): Approximately 2.0x the baseline density (320 dpi).
  • xxhdpi (Extra Extra High DPI): Approximately 3.0x the baseline density (480 dpi).
  • xxxhdpi (Extra Extra Extra High DPI): Approximately 4.0x the baseline density (640 dpi).

Providing drawables for each of these densities ensures that your app looks consistent and sharp on a wide range of devices.

Creating Density-Specific Drawables

To create density-specific drawables, follow these steps:

Step 1: Prepare Your Images

Start with a high-resolution version of your image. Then, scale it down to fit the different density buckets. For example, if your original image is 480×480 pixels for xxxhdpi, you would scale it as follows:

  • xxhdpi: 360×360 pixels (75% of xxxhdpi)
  • xhdpi: 240×240 pixels (50% of xxxhdpi)
  • hdpi: 180×180 pixels (37.5% of xxxhdpi)
  • mdpi: 120×120 pixels (25% of xxxhdpi)

Step 2: Organize Your Drawables

In your Android project, create the following drawable directories in the res folder:

  • res/drawable-mdpi/
  • res/drawable-hdpi/
  • res/drawable-xhdpi/
  • res/drawable-xxhdpi/
  • res/drawable-xxxhdpi/

Place the appropriately sized images in their respective directories. Ensure that each image has the same file name (e.g., my_image.png).


android_project/
├── app/
│   ├── res/
│   │   ├── drawable-mdpi/
│   │   │   └── my_image.png     # 120x120 px
│   │   ├── drawable-hdpi/
│   │   │   └── my_image.png     # 180x180 px
│   │   ├── drawable-xhdpi/
│   │   │   └── my_image.png     # 240x240 px
│   │   ├── drawable-xxhdpi/
│   │   │   └── my_image.png     # 360x360 px
│   │   ├── drawable-xxxhdpi/
│   │   │   └── my_image.png     # 480x480 px
│   │   └── ...
│   └── ...
└── ...

Step 3: Referencing Drawables in XML

In your XML layouts, reference the drawables without specifying the density qualifier. Android will automatically select the appropriate drawable based on the device’s screen density.


<ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/my_image" />

Example: Implementing Density-Specific Icons in a Kotlin Project

Let’s create a simple Kotlin Android project that uses density-specific icons.

Step 1: Create a New Android Project

Create a new Android project in Android Studio using Kotlin as the language.

Step 2: Prepare Density-Specific Icons

Create different sizes of your icon and place them in the respective drawable directories (mdpi, hdpi, xhdpi, xxhdpi, xxxhdpi).


res/
├── drawable-mdpi/
│   └── ic_launcher.png
├── drawable-hdpi/
│   └── ic_launcher.png
├── drawable-xhdpi/
│   └── ic_launcher.png
├── drawable-xxhdpi/
│   └── ic_launcher.png
├── drawable-xxxhdpi/
│   └── ic_launcher.png
└── ...

Step 3: Update the Layout File (activity_main.xml)

Add an ImageView to your layout file and reference the icon.


<?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">

    <ImageView
        android:id="@+id/iconImageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_launcher"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

Step 4: (Optional) Update MainActivity.kotlin

The Kotlin code does not need to be modified to handle density-specific drawables, as Android handles the selection automatically.


package com.example.densityspecificdrawables

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

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

Step 5: Run Your Application

Run your application on different emulators or devices with varying screen densities to verify that the correct drawables are being loaded.

Best Practices

  • Use Vector Drawables: For simple icons and graphics, use vector drawables (.xml files in drawable). Vector drawables scale without loss of quality, reducing the need for multiple density-specific versions.
  • Provide xxxhdpi Assets: Ensure you have xxxhdpi assets to support the highest density screens, and let Android scale down for lower densities.
  • Test on Multiple Devices: Always test your application on various devices or emulators with different screen densities to ensure that your drawables look correct.
  • Consider Adaptive Icons: For launcher icons, use adaptive icons to ensure they look good on different devices and Android versions.
  • Optimize Images: Use image optimization tools to reduce the size of your drawable files without significantly affecting quality.

Conclusion

Supporting density-specific drawables is a fundamental aspect of Android development that ensures your app provides a consistent and visually appealing experience across a wide range of devices. By preparing and organizing your drawables correctly, and by following best practices like using vector drawables and testing on multiple devices, you can create a polished and professional-looking Android application.