Layout xml merge tag

If you have a section of layout that you wish to reuse, it can be added using the <include> tag. Typically the code you are including would need a root view e.g. LinearLayout. However, if that root view would result in redundant code e.g. a vertical LinearLayout before the <include> tag immediately followed by the LinearLayout inside the reusable code, then instead of providing a root view for your reusable code then use the <merge> tag.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <include layout="@layout/reusable_layout"/>

    <include layout="@layout/reusable_layout"/>

    <TextView android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:text="@string/hello"
              android:padding="10dp" />

    ...

</LinearLayout>

 

 

// This is the block of reusable code
<merge xmlns:android="http://schemas.android.com/apk/res/android"> // No need for a root view here if it is to be nested inside a ViewGroup anyway. Putting a vertical LinearLayout here would produce a redundant ViewGroup

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/add"/>

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/delete"/>

</merge>

 

Responsive design

To cater for the wide variety of device specifications, we need to consider screen size and density.

These are the 5 most popular density buckets:

  • mdpi (medium) ~160dpi
  • hdpi (high) ~240dpi
  • xhdpi (extra high) ~320dpi
  • xxhdpi (extra extra high) ~ 480dpi
  • xxxhdpi (extra extra extra high) ~640dpi

Density-independent pixels (dp’s) largely overcome these variations in density by making an image of e.g. 48dp look approximately the same physical size on all screens. **Make touch targets 48dp at least** However, you should supply a range of variations of each image to cater for the difference in density e.g. 48px:72px:96px:144px:192px in their appropriate folders to avoid placing unnecessary load on the processor and distorting the image, through scaling. To read more about how to cater for these go here.

For different screen layouts/orientations we can define individual xml files to cater for the variation in available space. Like the density buckets, you can place your layout files in the appropriately named folder and Android will use the layout that corresponds to its current configuration.

At runtime, Android will check the configuration of the device and choose the appropriate resources from the relevant folders accordingly. This is why it is necessary for Android to destroy and recreate Activities on screen rotation, as all of the resources within it could be completely different. Examples of folder variations:
values-fr/ – values for the French language
values-fr-rCA/ – values for the French-Canadian dialect
layout-desk/ – device is docked
layout-stylus/ – for device screens with stylus input
drawable-xhdpi/ – drawable resources for a xhdpi density screen
layout-land/ – landscape orientation
layout-sw720dp/ – minimum smallest screen width the layout will apply to

An example resource directory structure:

res/
   layout/
      activity_main.xml
      detail_activity.xml
      list_item.xml
   layout-sw600dp/
      detail_activity.xml
      list_item.xml
   layout-sw720dp/
      list_item.xml

To create a smallest-width qualifier folder, right-click on res->New->Android Resource Directory, Resource type: layout, Available qualifiers: Smallest Screen Width, enter screen width (in dp), then ‘OK’. File names must be identical across folders to them to be overridden.

Include layouts

If creating multiple instances of the same layout it is good practice to save that layout code in its own file and instantiate it using the tag e.g. you are designing separate portrait and landscape layouts of the same content with groups of objects such as those in a table which will remain in an identical relative layout across both designs.

Given this original layout:

<?xml version="1.0" encoding="utf-8"?>
<layout>
<ScrollView xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/scroll"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <android.support.constraint.ConstraintLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:text="@string/passenger_label"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            ... />

        <TextView
            tools:text="@string/passenger_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            ... />

<!-- Start of section of code defining a group of objects in a fixed layout to be duplicated across portrait and landscape -->

        <ImageView
            android:id="@+id/leftRectangle"
            android:layout_width="60dp"
            android:layout_height="80dp"
            ... />

        <ImageView
            android:id="@+id/divider"
            android:background="@color/colorPrimaryLight"
            ... />

        <ImageView
            android:id="@+id/rightRectangle"
            android:layout_width="60dp"
            android:layout_height="80dp"
            ... />

        <TextView
            android:id="@+id/textViewOriginAirport"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            ... />

        <TextView
            android:id="@+id/textViewDestinationAirport"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            ... />

        <ImageView
            android:id="@+id/imagePlane"
            android:layout_height="wrap_content"
            android:layout_width="wrap_content"
            ... />

        <TextView
            android:id="@+id/textViewFlightCode"
            tools:text="@string/flight_code"
            android:layout_width="wrap_content"
            ... />

<!-- Endof section of code defining a group of objects -->

        <TextView
            android:id="@+id/textViewBoardingTimeLabel"
            android:text="@string/boarding_time_label"
            android:layout_width="wrap_content"
            ... />

        <TextView
            android:id="@+id/textViewBoardingTime"
            tools:text="@string/boarding_time"
            android:layout_width="wrap_content"
            ... />

        <TextView
            android:id="@+id/textViewDepartureTimeLabel"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            ... />

        <TextView
            android:id="@+id/textViewDepartureTime"
            tools:text="@string/departure_time"
            android:layout_width="wrap_content"
            ... />

    </android.support.constraint.ConstraintLayout>
</ScrollView>
</layout>

The extracted code pasted into it separate layout file (name boarding_info.xml) would look like:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android" // Very important to include these tags
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <android.support.constraint.ConstraintLayout // Very important to include these tags
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ImageView
            android:id="@+id/leftRectangle"
            android:layout_width="60dp"
            android:layout_height="80dp"
            ... />

        <ImageView
            android:id="@+id/divider"
            android:background="@color/colorPrimaryLight"
            ... />

        <ImageView
            android:id="@+id/rightRectangle"
            android:layout_width="60dp"
            android:layout_height="80dp"
            ... />

        <TextView
            android:id="@+id/textViewOriginAirport"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            ... />

        <TextView
            android:id="@+id/textViewDestinationAirport"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            ... />

        <ImageView
            android:id="@+id/imagePlane"
            android:layout_height="wrap_content"
            android:layout_width="wrap_content"
            ... />

        <TextView
            android:id="@+id/textViewFlightCode"
            tools:text="@string/flight_code"
            android:layout_width="wrap_content"
            ... />

    </android.support.constraint.ConstraintLayout>

</layout>

And the original file would look like:

<layout>
<ScrollView xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/scroll"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <android.support.constraint.ConstraintLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:text="@string/passenger_label"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            ... />

        <TextView
            tools:text="@string/passenger_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            ... />

        <include
            android:id="@+id/boarding_info" // Essential for binding data
            layout="@layout/boarding_info" // Only essential attribute - others added on on aligning with surrounding elements
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            ... />

        <TextView
            android:id="@+id/textViewBoardingTimeLabel"
            android:text="@string/boarding_time_label"
            android:layout_width="wrap_content"
            ... />

        <TextView
            android:id="@+id/textViewBoardingTime"
            tools:text="@string/boarding_time"
            android:layout_width="wrap_content"
            ... />

        <TextView
            android:id="@+id/textViewDepartureTimeLabel"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            ... />

        <TextView
            android:id="@+id/textViewDepartureTime"
            tools:text="@string/departure_time"
            android:layout_width="wrap_content"
            ... />

    </android.support.constraint.ConstraintLayout>
</ScrollView>
</layout>

If binding to these layouts you will need to add in an extra reference to the include file e.g.

mBinding.textViewOriginAirport.setText("aString");

becomes

mBinding.boardingInfo.textViewOriginAirport.setText("aString"); // boardingInfo automatically generated from 'boarding_info' xml name