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.
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.
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 {
}
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} {
}
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}
*/
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.
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.
We end up with something like this.
package com.samdev.templates
/**
* @author samdev
* Created 06/05/2021 at 1:14 AM
*/
interface MyInterface {
}
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.
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.
Now, the reason you are here.
For the purpose of this tutorial, we will be creating 2 templates.
- A Java Singleton template.
- 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;
}
}
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;
}
}
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.
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)
}
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})
}
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.
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.👇
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!
Top comments (5)
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.
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!
Hi Samuel, is there a way to share our template to others? (but not manually copy -> paste in Files & code templates)
nevermind. just copy the .idea/fileTemplates folder and paste in different computer/project
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
You can then copy the resulting .zip file to the other computer/user.
Importing the shared file templates is just as easy.
Go to
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...