DEV Community

loading...

How to use Android Studio File Templates to speed up development.

Samuel Agbesinyale
Mobile Software Engineer
・7 min read

Introduction

Android Studio comes with a nifty feature that I use quite often to speed 🚀 up my development process.

Introducing... File Templates🎉.

We have all used this feature probably without realizing it can help us do much more.

An instance where we have all utilized file templates is when we right-click on a package in android studio to create a Class, Enum, or Interface.

Android Studio allows us to modify these file templates and even lets us create our own.

Anytime we create a new interface or class from the context menu, we are utilizing their respective template files.

Here's an example.

Creating an Interface

The "File and Code Templates" Window.

Before we begin editing, let's learn how to access the Templates window. There, we can find all existing templates and also create our own.

Steps:

  • Right-click on any file/package
  • Navigate to "New"
  • Click on "Edit File Templates..."

You should be presented with a window that looks like this.
Alt Text
Note: The Android Studio version being used at the time of writing this article, is v4.1.3

Important information

The template language used is Apache Velocity.

When working with File Templates in Android Studio, there are a number of helpful predefined variables which we can utilize to simplify creating or editing template contents.

Here are some of these predefined variables and what they do:

${DATE}, returns the current system date
${TIME}, returns the current system time
${NAME}, defines the name of the new file (class, interface, enum, etc...)
${PACKAGE_NAME}, returns the name of the target package in which the new class or interface file is created
${PROJECT_NAME}, returns the name of the current project
${USER}, returns the login name of the current user.

We will take a closer look at how some of these variables are used later on in this article.

Understanding the structure and keywords

Let's take an empty kotlin interface class for instance.

This is what it looks like.

package com.samdev.templates

interface MyInterface {
}
Enter fullscreen mode Exit fullscreen mode

And below is what the Kotlin Interface template looks like.

#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME} #end

#parse("File Header.java")
interface ${NAME} {
}
Enter fullscreen mode Exit fullscreen mode

You can find the Kotlin Interface template by scrolling through the list of available templates in the "File and Code Templates" window.

Now Let's take note of a few things.
1️⃣ The first thing we notice here is how the package name is represented in the template. This is an example of how we use the ${PACKAGE_NAME} variable mentioned earlier.

2️⃣ The #if #end block, which basically ensures the existence of a package name before actually printing it out.
This is necessary so when we create a class in the project root folder, the template engine will skip setting the package name, since none exists at that point.

3️⃣ The ${NAME} variable which we also mentioned earlier as the variable that is used to define the name of the class. More on this later.

4️⃣ The #parse keyword. This script element simply allows us to import a variable or file that contains a template.
Think of this as a "superclass", any templates that exist in the specified file (File Header.java in our case) will be applied in the template file that references it. Makes sense?

Let's see the #parse script element in action.

The current File Header template class is empty, so we are going to place some code in there and see how it affects our existing interface.

/**
 * @author ${USER}
 * Created ${DATE} at ${TIME}
 */
Enter fullscreen mode Exit fullscreen mode

Note☝: Any template files we intend to reference with the #parse script element, should be created in the "Includes" section of the "File and Code Templates" window.

The Includes tab in the Templates window

We have already learned what each of the predefined variables ${USER}, ${DATE}, and ${TIME} does (refer to this guy), so let's just take a look at the gif below to see the effect.

New interface creation after editing header

We end up with something like this.

package com.samdev.templates

/**
 * @author samdev
 * Created 06/05/2021 at 1:14 AM
 */
interface MyInterface {
}
Enter fullscreen mode Exit fullscreen mode

You will notice that the section that contained the #parse logic, which was blank before, now contains the result of the header template we added.

Note☝: We are not bound by predefined variables when editing templates. We can define our own variables using the #set element, and we can also create variables whose values will be provided via a prompt displayed by Android Studio.

I will demonstrate this in the next section.

Creating new templates

To create our own custom templates, we navigate to the "Templates and Code" window by following the steps listed here and tap the '+' icon at the top left corner.

New template button

We are presented with a view where we are required to name the template and also set the extension.

When writing java templates, we replace the "kt" in the extension field with "java".

After creating our templates, we can find them in the "New" sub-menu, as indicated below.

To use them, all we need to do is click, and follow the prompts.
find_custom_templates

Now, the reason you are here.

For the purpose of this tutorial, we will be creating 2 templates.

  1. A Java Singleton template.
  2. A Kotlin data access object (Dao) interface template to be used with Room Database.

The Java Singleton.

Let's take a look at the actual Singleton class below

class MySingleton {

    private static MySingleton instance = null;

    private MySingleton() {
    }

    public static MySingleton getInstance() {
        if (instance == null) {
            synchronized (MySingleton.class) {
                if (instance == null) {
                    instance = new MySingleton();
                }
            }
        }
        return instance;
    }

}
Enter fullscreen mode Exit fullscreen mode

Now let's take a look at our singleton template.

#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME}; #end

#parse("File Header.java")

class ${NAME} {

    private static ${NAME} instance = null;

    private ${NAME}() {}

    public static ${NAME} getInstance() {
        if (instance == null) {
            synchronized(${NAME}.class) {
                if (instance == null) {
                    instance = new ${NAME}();
                }
            }
        }
        return instance;
    }
}

Enter fullscreen mode Exit fullscreen mode

1️⃣ The first thing we do, just as we have previously indicated, is to set the package name, using the ${PACKAGE_NAME} variable.

2️⃣ We include any necessary header files using the #parse script element.

3️⃣ The ${NAME} variable is used here again.

Unlike the other variables like ${PACKAGE_NAME}, ${DATE}, etc... the ${NAME} variable obtains its value from user input.

So how do we set the ${NAME}? The IDE handles that for us, it will display a prompt requesting the Filename.

Let's see how it works in the gif below.
Java singleton

After we selected our newly created template named Java Singleton, we received a prompt, requesting that we enter the "File name". Whatever value we enter in the dialog becomes the value for ${NAME}.

That's it! We have successfully created a java singleton template. Whenever we require a java singleton in our current/future projects, all we need to do is select the template, provide a name, and voila!

The Dao Interface class.

When working with Room, we interact with the stored data by defining data access objects. The DAOs include methods that offer abstract access to your app's database.

Eg: If we have 3 tables/entities, a User table, Class table, and a Lesson table, we are required to create a Dao class for each table so we can access the data in those tables.

As the number of tables increases, the number of Dao interfaces you have to create also increases.

In order to make life a little bit easier for us, we can create a Dao Template file, which will be used anytime we need to create new Dao Interfaces.

Readers who can not familiar with Room and Dao interfaces can refer to this link for clarification.

Code Sample

Let's look at a Dao interface sample for a User entity.

Dao
interface UserDao {
    Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insertAll(users: List<User>)

    Query("SELECT * FROM user")
    fun getAll(): LiveData<List<User>>

    Query("SELECT * FROM user WHERE id = :id")
    fun getById(id: Long) : LiveData<User>

    Delete
    fun delete(User user)
}
Enter fullscreen mode Exit fullscreen mode
Template

Now let's look at our Dao template

#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME} #end

#parse("File Header.java")

import androidx.lifecycle.LiveData
import androidx.room.*

Dao
interface ${NAME} {

    Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insertAll(users: List<${Entity_Class}>)

    Query("SELECT * FROM ${Table_Name}")
    fun getAll() : LiveData<List<${Entity_Class}>>

    Query("SELECT * FROM ${Table_Name} WHERE id = :id")
    fun getById(id: Long) : LiveData<${Entity_Class}>

    Delete
    fun delete(entity: ${Entity_Class})

}
Enter fullscreen mode Exit fullscreen mode

We have introduced a few new concepts.

1️⃣ Class imports. You can import and use any existing classes when creating templates as well.

2️⃣ Custom variables. You will notice that in this template, we are dealing with multiple custom variables, ${Entity_Class} to define our entity data type and ${Table_Name} to define our "table_name"

Syntax to create a custom variable is ${Variable_Name}

The variables get their values the same way the ${NAME} predefined variable gets its value, via the prompt.

Let's see them in action in the gif below.
Dao

Conclusion

Template classes are not that different from your regular java/kotlin classes, so there's so much you can do with them.

You the next time you need to create a class but already have the template.👇

goteem

Here are a few more templates you can take a look at to better understand how they work the extent to which they can be used.

If you have questions, please leave them in the comments section and I'd do my best to address them!

Discussion (5)

Collapse
mahendranv profile image
Mahendran

Nice article. Most of the time I endup creating a lot of boilerplate when using recyclerview.

Also I avoid using fragment's template since it includes some arguments that I just delete on first encounter.

May be it's time to edit the templates.

Collapse
wilderminds profile image
Samuel Agbesinyale Author

Thank you 🙏

In the link I included at the bottom of the article, there's an example of a recyclerview adapter template, so you don't have to manually deal with all the boilerplate.

...and yeah, I also end up deleting a bunch of the code when I create a regular fragment.

So editing or creating one that works for you is a great idea!

Collapse
hahnsaja profile image
Han

Hi Samuel, is there a way to share our template to others? (but not manually copy -> paste in Files & code templates)

Collapse
hahnsaja profile image
Han

nevermind. just copy the .idea/fileTemplates folder and paste in different computer/project

Collapse
wilderminds profile image
Samuel Agbesinyale Author • Edited

Hi Han,

Interesting approach, for some reason though, I could not find the "fileTemplates" subdirectory in my project's ".idea" folder.

So for other readers who might have the same issue,

AndroidStudio provides a way to share export these templates and/or other android studio settings across devices.

To do that, from the menu, go to

  • File -> Manage IDE Settings -> Export Settings
  • In the dialog that opens up, ensure that the "File templates" checkbox is checked.
  • Specify the destination path where the settings should be saved and click "OK"

You can then copy the resulting .zip file to the other computer/user.
Importing the shared file templates is just as easy.

Go to

  • File -> Manage IDE Settings -> Import Settings
  • Select the exported .zip settings file.
  • In the Import Settings dialog, ensure that the "File templates" checkbox is checked and click OK.

Thats it.

NB: Be mindful of the checkboxes you enable when exporting, so you do not end up exporting unnecessary/unwanted settings

You can also check this link here for more information.
jetbrains.com/help/idea/sharing-li...