Look (at android design layout) before you leap (run the app)

Debug Labs
6 min readJul 16, 2019

--

Visualising your Android app’s layout design, with customizable placeholder data.

Use Case :

  1. Customisable place holder text during design so that the preview is accurate.
  2. Multi language support for the place holder data so that the previews are as accurate as possible.

“Android Studio supports a variety of XML attributes in the tools namespace that enable design-time features (such as which layout to show in a fragment) or compile-time behaviour (such as which shrinking mode to apply to your XML resources). When you build your app, the build tools remove these attributes so there is no effect on your APK size or runtime behaviour.”

For this discussion though, we shall focus only on setting design time place holder data onto the views through

  1. the predefine tools namespace data and
  2. through custom sampledata files.

1. Predefined Tools Data :

Lets start with a simple view, say a row view for a list of users, with following fields : 1. Avatar image 2. Full name 3. City of residence 4. Mobile Number 5. Premium membership status

This is how we will add the respective data, to the layout file using the tools namespace and the predefined data . (Highlighted )(“xmlns:tools=”http://schemas.android.com/tools"”)

The tools namespace supports a lot of attributes. like

  1. tools:src for ImageView src
  2. tools:text for Text Views
  3. tools:checked for Switches etc
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/user_avataar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:src="@tools:sample/avatars"

/>
<TextView android:id="@+id/user_full_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:textAppearance="@style/TextAppearance.AppCompat.Headline"
android:layout_toEndOf="@+id/user_avataar"
tools:text="@tools:sample/full_names"
/>
<TextView android:id="@+id/user_city"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
tools:text="@tools:sample/cities"
android:layout_below="@+id/user_full_name"
android:layout_toEndOf="@+id/user_avataar"
/>
<TextView android:id="@+id/user_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
tools:text="@tools:sample/us_phones"
android:layout_below="@+id/user_city"
android:layout_toEndOf="@+id/user_avataar"
/>
<android.support.v7.widget.SwitchCompat
android:id="@+id/premium_membership_status"
android:layout_width="match_parent" android:layout_height="wrap_content"
tools:checked="true"/> <!--hard coded data-->

</RelativeLayout>

Note : we do not have any mapping for membership status in the predefined tools data, hence we have hardcoded the value to true. sampledata is extremely helpful in such situations.

This is how that looks on design view :

Design View using tools namespace predefined data

2. Custom sampledata (As place holder data)

The tools predefined data set has very limited set of values available (Like user name and address). I am sure all of us are knee deep into much more complex projects (Movie bookings and shoe shopping! 😛 and we need a wide variety of values/formatted-data as placeholder text to fully realise our views. May be even try out multi-language/locale support( 3. Using Gradle tasks to add multi language support for sampledata)

Adding the sampledata folder to the project

Using the same layout, with the same data fields, lets define a .json file as follows.

{
"data": [
{
"name": "Chaitanya Duse",
"gender": "Male",
"isPremium": "true",
"mobile_no" : "+124535192",
"city" : "Bengaluru, India",
"avatar": "@sample/images"
}
]
}

Now add this users_data.json file to sampledata folder.

Add an images directory to the sampledata folder and add one or more images to the folder.

Adding images/avatars folder to the sample data for customizsable images

Note the to refer the user avatar image from the images folder, in the users_data.json file we need to define the “avatar” field value to “@sample/images”

  • * In my experience one may need to rebuild the project or sometimes even invalidate cache and restart for this data to be reflected in the UI, the first time.

This is how we refer to the sampledata folder fields in the layout @sample/users_data.json/data/{field_name}

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/user_avataar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:src="@sample/users_data.json/data/avatar"

/>
<TextView android:id="@+id/user_full_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:textAppearance="@style/TextAppearance.AppCompat.Headline"
android:layout_toEndOf="@+id/user_avataar"
tools:text="@sample/users_data.json/data/name"
/>
<TextView android:id="@+id/user_cityr"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
tools:text="@sample/users_data.json/data/city"
android:layout_below="@+id/user_full_name"
android:layout_toEndOf="@+id/user_avataar"
/>
<TextView android:id="@+id/user_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
tools:text="@sample/users_data.json/data/mobile_no"
android:layout_below="@+id/user_cityr"
android:layout_toEndOf="@+id/user_avataar"
/>
<android.support.v7.widget.SwitchCompat
android:id="@+id/premium_membership_status"
android:layout_width="match_parent" android:layout_height="wrap_content"
tools:checked="@sample/users_data.json/data/isPremium"
/>

</RelativeLayout>

This is how the the layout looks now in the design view.

Design View using customizable sampledata json file

This feature makes a simple feature like place-holder data really powerful. We can add multiple json files to represent different views in our app. Running the app just to check UI alignment with runtime data is a thing of the past.

The best part of this is that there is no configuration or code needed by the developer to prevent this data from Shipping all this data to build apk! All is handled by the Studio by default and no trace of this data is found in the apks!

3. Using Gradle tasks to add multi language support for sampledata

Now onto a more specific use case.

Once I got hooked to this feature, there was no way I ever wanted to run the app to check if the UI is displayed and aligned properly. There however was one challenge. What about multi language support ?

I found that there is a limitation where we can’t create different folders in the sample data for different locales.(en, ar, fr etc), the way it is handled for resources

Using Gradle tasks I could come up with a workaround.

Step 1 :

In this step we will add one ore more .json files to assets folder.

Since this is needed only for debug builds we shall add this to debug folder (create debug folder if not created earlier).

Since we need to support multiple language/locale we will create one folder each for the locale and add the respective translated .json files, with the same name, in the respective locale folders (assets/fr, assets/ar , assets/en .. etc)

The Gradle script’s objective is to copy files form one of these folders depending on the locale, to the sampledata folder.

movies and user data for Arabic (ar) and English(en)

User data in Arabic

{
"data": [
{
"name": "تشيتانيا دوس",
"gender": "الذكر",
"subscription_type": "علاوة"
}
]
}

Step 2

Declare a variable in project level gradle which holds the value for desired locale/language (In which we want our views be laid out)

Using value from this variable we will construct a path to the appropriate assets folder (assets/ar) fetch all the .json files and replace them in the sampledata folder.

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {

ext{
kotlin_version = '1.3.31'
debugDataLocale
= 'en'
}
............

Step 3

Just before we build the app we will clear the sampledata folder of all the data files (.json files)

We will extend gradle’s clean task to perform the delete operation of all the files in the sampledata folder (Will be deleting only data files currently assuming that avatar images will remain same across languages)

clean.doFirst { //extend gradle's clean task
tasks.deleteFiles

}

task deleteFiles(type: Delete) {
def samplesDir = new File(projectDir.absolutePath, "sampledata")
delete fileTree(samplesDir) {
include '**/*.json'
}
}

Step 4

Only when we are building the app with build type debug (“generateDebugBuildConfig”) task we shall

  1. Construct folder path using the debugDataLocale value declared in the project level gradle in Step 2.
  2. Copy data from this directory to the sampledata directory.
tasks.whenTaskAdded { task ->
if (task.name == 'generateDebugBuildConfig') {
task.dependsOn copySampleData
}
}
//Task to copy files from assets folder to sampleData folder
task copySampleData(type: Copy) {
def samplesDir = new File(projectDir.path, "sampledata")
def assetsDir = new File(projectDir.path, "/src/debug/assets/" + project.properties.debugDataLocale)
from(assetsDir)
into(samplesDir)
}

and done! Just 4 steps!

Now in our example we will change the debugDataLocale to ar and build the app again and see the magic!

I have created a short video on youtube where we can see this in action

As usual the code is available on Github!

https://github.com/ChaitanyaDuse/sampledata

--

--

Debug Labs
Debug Labs

Written by Debug Labs

🚀 Android Dev (13+ yrs) | Jetpack Compose | AI & ML Enthusiast | Writing on Background Work, Room DB, Clean Architecture & more | Simplifying dev concepts

No responses yet