Adaptive UI Tutorial for Android with Kotlin

Make your Android app feel at home on any device. Learn how to build an adaptive UI that looks and works well across all devices and screen sizes. By Joe Howard.

Leave a rating/review
Save for later
Share

Update Note: This tutorial is now up to date with the latest version of Android Studio version 3.0.1, and uses Kotlin for app development. Update by Joe Howard. Original tutorial by James Nocentini.

Android runs on a wide variety of devices that offer different screen sizes and densities. Because of this, it is important for Android apps to have a responsive user interface that can adapt to these different screens. Since the early days of the Android platform, system APIs have provided very powerful abstractions to design adaptive UIs, also known as adaptive layouts.

This is an update to our adaptive UI in Android tutorial which will show you how to build apps that work across different devices by dealing with the fragmentation in the Android device market. You’ll learn about:

  • Configuration qualifiers
  • Alternative layouts and drawables
  • And layout previews in Android Studio — an immensely useful tool

What would a tutorial be without something to tinker with? It’d be pretty boring. So, you’ll build the user interface for a simple weather app completely from scratch! When you’re done, the screen will display an image, text labels and a map in three different configurations. Apps look so cool and well built when they have an adaptive UI. :]

Adaptive UI Tutorial for Android Sample App

Getting Started

Download the starter project named Adaptive Weather here, and open it in Android Studio 3.0.1 or later. Then build and run.

The app displays a simple RecyclerView listing several cities.

cities

To learn all about RecyclerViews, we recommend reading our Android RecyclerView tutorial.

Open the build.gradle file of the app module to declare the following dependency:

dependencies {
  ...
  implementation 'com.google.android:flexbox:0.3.2'
}

Google’s FlexBox provides an implementation of the FlexBox specification on the Android platform. As you will see later on, it is a very useful tool for designing adaptive layouts. And combining it with Android’s resource qualifier system makes it even more powerful!

Note: The Android platform is constantly updated and the version numbers may have increased since we published this tutorial. You can find details of the different versions, including the most recent on the support library pages on the Android developer site.

During this tutorial, you’ll often switch between the Android and Project modes in the Project navigator. Generally speaking:

Weather Icons

Android Studio Project Navigator

Android devices have different screen densities, and for that reason it’s a good practice to import static images in multiple sizes. This is one way Android’s system APIs provide a way to create adaptive UIs. As described in the Supporting Multiple Screens guide, the categories of screen densities are:

Whilst some UI editors make it easy to export images in different sizes, we will be exploring a different approach in this tutorial. Android Studio recently added support for Vector Drawables. This means that all your assets can be imported once and will be scaled at runtime depending on the device configuration (screen size and orientation).

Download the Weather Icons and extract. In Android Studio right-click on res/drawable and click on the New\Vector Asset menu item:

Android Studio Create Vector Asset

Select Local file (SVG, PSD) under Asset Type. From the filesystem location chooser under Path locate the weather-icons folder and choose the first icon, cloud.svg. Make sure to check the Override under Size setting otherwise your icons will look a bit distorted later on (¯\_(ツ)_/¯). Click Next and Finish:

Android Studio Configure Vector Asset

Now you should see your icon in Android Studio as res/drawable/ic_cloud.xml. Repeat the same operations for the other icons: fog, rain, snow, sun, thunder.

Finally, enable the use of Vector Drawables in the app module’s build.gradle as follows:

  • Android mode is the default when working within Android Studio because it provides a clean and simple file structure.
  • Project mode is also necessary for building alternative layouts.
    • ldpi (low) ~120dpi
    • mdpi (medium) ~160dpi
    • hdpi (high) ~240dpi
    • xhdpi (extra-high) ~320dpi
    • xxhdpi (extra-extra-high) ~480dpi
    • xxxhdpi (extra-extra-extra-high) ~640dpi
  • ldpi (low) ~120dpi
  • mdpi (medium) ~160dpi
  • hdpi (high) ~240dpi
  • xhdpi (extra-high) ~320dpi
  • xxhdpi (extra-extra-high) ~480dpi
  • xxxhdpi (extra-extra-extra-high) ~640dpi
android {
  ...

  defaultConfig {
      ...
      vectorDrawables.useSupportLibrary = true
  }
}

With scalable assets now in place in the project, you’re ready to start customizing the layouts.

Building layouts

With the dependencies declared, you get to shift your focus to building some layouts!

This simple application only contains one screen, which is represented by MainActivity. From the Project navigator, open res/layout/activity_main.xml. Click on the Preview button on the right side to see it in action.

An activity comprises a Kotlin or Java class — in this case MainActivity.kt — and a layout file. In fact, one activity can have several layouts, as you’ll see shortly. For now, it’s important to remember that the existing layout file, activity_main.xml, is the default layout.

Forecast Grid View

First, define the default layout for your main activity. To start this, open res/values/colors.xml and replace its content with the following:

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <color name="color_primary">#9B26AF</color>
  <color name="color_primary_dark">#89229b</color>
  <color name="text_color_primary">#ffffff</color>
  <color name="forecast_grid_background">#89bef2</color>
</resources>

Here you’re overriding the default Material theme colors and providing a background color for the forecast grid. Next, right-click on the values folder and select the New\Value resource file menu:

Android Studio New Resource File

Enter fractions.xml for the file name and paste the following:

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <item name="weather_icon" type="fraction">33%</item>
</resources>

Here you’re specifying that the width taken by each icon should be 1/3 of the total width.

Next, create a new layout in res/layout called forecast_grid.xml and add the following list of images inside a FlexboxLayout:

<?xml version="1.0" encoding="utf-8"?>
<com.google.android.flexbox.FlexboxLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  android:id="@+id/forecast"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:background="@color/forecast_grid_background"
  app:alignItems="center"
  app:flexWrap="wrap"
  app:justifyContent="space_around">

  <android.support.v7.widget.AppCompatImageView
    android:id="@+id/day1"
    android:layout_width="wrap_content"
    android:layout_height="60dp"
    app:layout_flexBasisPercent="@fraction/weather_icon"
    app:srcCompat="@drawable/ic_thunder" />

  <android.support.v7.widget.AppCompatImageView
    android:id="@+id/day2"
    android:layout_width="wrap_content"
    android:layout_height="60dp"
    app:layout_flexBasisPercent="@fraction/weather_icon"
    app:srcCompat="@drawable/ic_fog" />

  <android.support.v7.widget.AppCompatImageView
    android:id="@+id/day3"
    android:layout_width="wrap_content"
    android:layout_height="60dp"
    app:layout_flexBasisPercent="@fraction/weather_icon"
    app:srcCompat="@drawable/ic_rain" />

  <android.support.v7.widget.AppCompatImageView
    android:id="@+id/day4"
    android:layout_width="wrap_content"
    android:layout_height="60dp"
    app:layout_flexBasisPercent="@fraction/weather_icon"
    app:srcCompat="@drawable/ic_snow" />

  <android.support.v7.widget.AppCompatImageView
    android:id="@+id/day5"
    android:layout_width="wrap_content"
    android:layout_height="60dp"
    app:layout_flexBasisPercent="@fraction/weather_icon"
    app:srcCompat="@drawable/ic_cloud" />

  <android.support.v7.widget.AppCompatImageView
    android:id="@+id/day6"
    android:layout_width="wrap_content"
    android:layout_height="60dp"
    app:layout_flexBasisPercent="@fraction/weather_icon"
    app:srcCompat="@drawable/ic_sun" />

</com.google.android.flexbox.FlexboxLayout>

There are a couple things to note with the above block:

  1. You’re using the com.google.android.flexbox.FlexboxLayout resource to layout the icons on the screen.
  2. You’re using the android.support.v7.widget.AppCompatImageView resource to draw the weather icons on the screen. You would normally use the ImageView resource with plain images (.png, .jpg) but for Vector Drawables you must use this component instead.
Note: If your Activity/dialog is provided by appcompat from the support library, then the Android system will use an AppCompatImageView under the hood when you specify a regular ImageView in your layout. So, in that case, you could just use a regular ImageView with your vector assets.

In the Preview pane, you see should the weather icons aligned perfectly:

Android Studio Forecast Grid Color

This is already starting to feel adaptive!

Instead of positioning the icons with margins or using a relative layout you have used the FlexBox properties to spread them symmetrically. If you remove a middle icon for example, the remaining ones will automatically shift to the left to fill in the empty space. This is the true power of using FlexBox in layouts. The forecast grid is now ready to be used in your default layout for the main activity.