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
You are currently viewing page 3 of 3 of this article. Click here to view the first page.

Resource qualifiers

Another enhancement you could make to the layout is to organize the weather icons as 2 columns and 3 rows as opposed to the current 3 columns and 2 rows layout. We could duplicate the forecast_grid.xml layout, but then it would be duplicated code and harder to maintain. The width occupied by each weather icon in relation to the FlexBox view width is determined by the layout_flexBasisPercent attribute:

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

The value is a fraction type and is currently equal to 33% in the resource qualifier file res/values/fractions.xml. Following the same approach to creating a landscape layout, you can create resource files for the landscape configuration. Right-click on res/values and select the New\Values resource file menu item. Name the file fractions and add the landscape orientation qualifier:

Android Studio New Fractions File

Inside the resources XML tag, add the following:

<item name="weather_icon" type="fraction">49%</item>

Return to the main activity layout in landscape mode and notice the weather icons are laid out on 2 columns and 3 rows:

Android Preview Landscape Layout updated

Well done! You can pause here and appreciate the fact that once again, you didn’t have to deploy the application to achieve this result. Of course now you should build & run though and make sure it works :]

The configuration qualifiers can be applied to any attribute type in your XML layout (font size, colors, margins etc.).

Extra Large Layout

Return to the portrait orientation in the layout editor and drag the screen size all the way to the X-Large size range.

Android Preview Drag X-large

For devices with that much screen real estate, you could show all the weather icons on 1 row. Go ahead and right-click on res/values and select the New\Values resource file menu item. Name the file fractions and add the X-Large Size qualifier:

Android Studio Create New Fractions File

Add the following inside the resources XML tag:

<item name="weather_icon" type="fraction">16%</item>

Return to the layout editor and notice that all the weather icons are aligned on 1 row.

Android Preview X-large Layout

Configuration Calculations

Don’t worry, the content in this section isn’t as scary as the title makes it sound. When the user interacts with the application, the layout state changes over time (rows are selected, input fields populated with text etc.). When the layout changes (for example when the orientation changes), the existing layout is thrown away a new layout is inflated. But the system has no way of knowing how to restore the state because the two layouts could be completely different as far as it knows.

To see a live example of this in action, build and run the application. Select a location then change the orientation and notice the location isn’t selected anymore!

Android Emulator Example Run 1

If you are not already surprised that the forecast in London is sunny all week then you may also notice that the selected row was deselected after switching to landscape.

To fix this, you will hook into the activity lifecycle methods to save the selected location to a bundle and retrieve after the screen rotation.

Add the following at the top of MainActivity, below the properties:

companion object {
  private const val SELECTED_LOCATION_INDEX = "selectedLocationIndex"  
}

Then add the following method to MainActivity below the onCreate() method:

override fun onSaveInstanceState(outState: Bundle) {
  super.onSaveInstanceState(outState)
  outState.putInt(SELECTED_LOCATION_INDEX, locationAdapter.selectedLocationIndex)
}

Add the following to the end of onCreate():

if (savedInstanceState != null) {
  val index = savedInstanceState.getInt(SELECTED_LOCATION_INDEX)
  if (index >= 0 && index < locations.size) {
    locationAdapter.selectedLocationIndex = index
    loadForecast(locations[index].forecast)
  }
}

Build and run again and this time the location remains selected across configuration changes. Hooray!

Android Emulator Example Run 2

Where to Go From Here

Well done! You’ve built your first Android app with adaptive layouts and you learned how activities can make use of multiple layouts. You learned how drawables work with different displays, and how to make your app come to life on practically any Android device.

Of course, there's a lot more to Android than layouts, and no shortage of ways to build on the adaptive UI principles you discovered in this adaptive UI for Android tutorial. To learn more, check out Google's guidelines on the best UI practices. If you want, you can challenge yourself by trying the following:

  • Use another available qualifier to have yet another type of layout. For example, what if you'd like to have a different background color based on the locale qualifier?
  • Or, try using size qualifier on other resources, such as strings. You could add a TextView which shows a short message, or a longer message with the same name if the screen is in landscape?

Get the full source code for this project as a downloadable zip.

Feel free to share your feedback, findings or ask any questions in the comments below or in the forums. Talk to you soon!