Blog Infos
Author
Published
Topics
Author
Published
Creating Android Studio templates for your Architecture.
Posted By: Vipin KT

Android Studio has a set of pre-packaged templates for creating Activities, Services, Layouts etc. They reduce the burden of writing the same piece of code again and again. But if you are following a specific architecture (or a certain pattern of writing code) in your project, then these template may-not be suited for your needs as they are too generic. And it will take much time to adjust the code in each file generated by the template to follow your architecture.

We can create custom templates using Android Studio’s template engine to follow the common standards and practices across the team.


Introduction

In this article I will explain creating template for a very simple architecture which follows MVVM with data binding. To make it simple, I have excluded all complex logic like dependency injection from this example.

I have a BaseActivity and a BaseViewModel under the core package.

The BaseActivity is responsible for initialising the view model, data binding and binding the view model to the layout file.

abstract class BaseActivity<D : ViewDataBinding, V : BaseViewModel> : AppCompatActivity(){
@get:LayoutRes
protected abstract val layoutId: Int
protected abstract val viewModelClass: Class<V>
internal val viewModel:V by lazy { ViewModelProvider(this).get(viewModelClass) }
internal lateinit var binding: D
private set
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.onActivityCreated(intent.extras)
binding = DataBindingUtil.setContentView(this, layoutId)
binding.setVariable(BR.viewModel, viewModel)
binding.lifecycleOwner = this
onCreated(savedInstanceState,intent.extras)
}
open fun onCreated(savedInstanceState: Bundle?, extras: Bundle?) { }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
viewModel.onActivityResult(requestCode, resultCode, data)
}
}
view raw BaseActivity.kt hosted with ❤ by GitHub

The BaseViewModel is simple and has only two methods.

abstract class BaseViewModel : ViewModel() {
open fun onActivityCreated(extras: Bundle?) {}
open fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {}
}

The activities derived from BaseActivity has to override two variables, layoutId and viewModelClass. After that, the activity can access view model by using the variable viewModel and binding class using bindingvariable.

The layout xml file also need to be changed to use data binding like below.

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="yourpackage.YourViewModel" />
</data>
<!-- Add your views here.-->
</layout>

As you have seen, to follow the architecture we have to write boilerplate every time we are creating a new screen. This is a time consuming as well as boring work.

In this article we will create a template which will generate an Activity with a ViewModel and a layout xml file, all following the architecture above.

Before starting, copy the Base classes to the core directory under your root package.

Let’s Get Started
Open Android Studio’s template directory.

In Mac :

/Applications/Android Studio.app/Contents/plugins/android/lib/templates/

In Windows :

 

{ANDROID_STUDIO_LOCATION}/plugins/android/lib/templates/

There you can see a set of directories for the pre-packaged templates.v

Create a folder for your template.

Create a directory and name it with your template name, I am creating this under the activities directory and naming it as AppActivity.

activities/AppActivity/
Create files and folders

There are some files & folders which plays major role in template creation.

  1. templates.xml— Represents how your template screen should look like.
  2. recipe.xml.ftl — The container box where all your template files (.ftl) are ready to evolve into Android code files (.kt, .java , .xml) based on your checks, logic and id defined in templates.xml
  3. globals.xml.ftl— The place where you keep all your directories, path and package structures as variables.
  4. root/— The place where all files containing the code of your template will be saved.

Create these files and copy paste the code.

templates.xml
<?xml version="1.0"?>
<template
format="5"
revision="5"
name="App Activity"
minApi="21"
minBuildApi="21"
description="Creates a new app activity">
<category value="Activity" />
<formfactor value="Mobile" />
<parameter
id="activityClass"
name="Activity Name"
type="string"
constraints="class|unique|nonempty"
suggest="${layoutToActivity(layoutName)}"
default="MainActivity"
help="The name of the activity class to create" />
<parameter
id="layoutName"
name="Layout Name"
type="string"
constraints="layout|unique|nonempty"
suggest="${activityToLayout(activityClass)}"
default="activity_main"
help="The name of the layout to create for the activity" />
<parameter
id="viewModelClass"
name="ViewModel Name"
type="string"
constraints="class|unique|nonempty"
suggest="${underscoreToCamelCase(classToResource(activityClass))}ViewModel"
help="The name of the view model class to create" />
<parameter
id="packageName"
name="Package name"
type="string"
constraints="package"
default="com.mycompany.myapp" />
<parameter
id="activityTitle"
name="Activity Title"
type="string"
suggest="${underscoreToCamelCase(classToResource(activityClass))}"
help="Title of the new activity" />
<globals file="globals.xml.ftl" />
<execute file="recipe.xml.ftl" />
</template>
view raw template.xml hosted with ❤ by GitHub

Each <parameter> will create one user input field. Most of the attributes are self explanatory .

type decides what type of input should be created.

  • string —edittext.
  • enum — dropdown/spinner.
  • boolean —checkbox.
recipe.xml.ftl
<?xml version="1.0"?>
<recipe>
<merge from="root/res/values/strings.xml.ftl"
to="${escapeXmlAttribute(resOut)}/values/strings.xml" />
<merge from="root/AndroidManifest.xml.ftl"
to="${escapeXmlAttribute(manifestOut)}/AndroidManifest.xml" />
<instantiate from="root/src/app_package/Activity.kt.ftl"
to="${escapeXmlAttribute(srcOut)}/${activityClass}.kt" />
<instantiate from="root/src/app_package/ViewModel.kt.ftl"
to="${escapeXmlAttribute(srcOut)}/${viewModelClass}.kt" />
<instantiate from="root/res/layout/activity.xml.ftl"
to="${escapeXmlAttribute(resOut)}/layout/${layoutName}.xml" />
<open file="${escapeXmlAttribute(resOut)}/layout/${layoutName}.xml" />
<open file="${escapeXmlAttribute(srcOut)}/${viewModelClass}.kt" />
<open file="${escapeXmlAttribute(srcOut)}/${activityClass}.kt" />
</recipe>
view raw recipe.xml.ftl hosted with ❤ by GitHub

This will give commands to transform files in the root/ folder into new files. Some commands that you can use include:

  • instantiate — This will create new files based on the user inputs and code in the template files in root/ folder.
  • merge — Merge the contents of one file into another.
  • open — Open files that have been generated by instantiate.
globals.xml.ftl

We will keep this file empty as we are not using any variable for this template.

root/

Now we can create some files and folders inside the root directory.

Create files for activity, viewmodel, layout, strings and manifest. The final file structure will look like,

Job Offers

Job Offers

There are currently no vacancies.

OUR VIDEO RECOMMENDATION

, ,

Supporting your architecture with code coverage and static analysis rules

One challenge of engineering teams is how to ensure you’re writing high-quality code. Code coverage has traditionally been one measure for this, but aiming for complete coverage across your codebase will rarely lead to meaningful…
Watch Video

Supporting your architecture with code coverage and static analysis rules

Michael Tweed
Principal Software Engineer
Skyscanner

Supporting your architecture with code coverage and static analysis rules

Michael Tweed
Principal Software E ...
Skyscanner

Supporting your architecture with code coverage and static analysis rules

Michael Tweed
Principal Software Engine ...
Skyscanner

Jobs

Copy code from below and paste in appropriate files.

package ${escapeKotlinIdentifiers(packageName)}
import android.content.Context
import android.content.Intent
import android.os.Bundle
import ${applicationPackage}.R
import ${applicationPackage}.core.BaseActivity
import ${applicationPackage}.databinding.${underscoreToCamelCase(layoutName)}Binding
class ${activityClass} : BaseActivity<${underscoreToCamelCase(layoutName)}Binding,${viewModelClass}>() {
override val layoutId: Int
get() = R.layout.${layoutName}
override val viewModelClass: Class<${viewModelClass}>
get() = ${viewModelClass}::class.java
override fun onCreated(savedInstanceState: Bundle?, extras: Bundle?) {
super.onCreated(savedInstanceState, extras)
}
companion object{
fun start(context: Context) {
context.startActivity((Intent(context,${activityClass}::class.java)))
}
}
}
view raw Activity.kt.ftl hosted with ❤ by GitHub
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="${packageName}.${viewModelClass}" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="${packageName}.${activityClass}">
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<activity android:name="${packageName}.${activityClass}"
android:label="@string/title_${activityToLayout(activityClass)}" />
</application>
</manifest>
<resources>
<string name="title_${activityToLayout(activityClass)}">${escapeXmlString(activityTitle)}</string>
</resources>
view raw strings.xml.ftl hosted with ❤ by GitHub
package ${escapeKotlinIdentifiers(packageName)}
import android.os.Bundle
import ${applicationPackage}.core.BaseViewModel
class ${viewModelClass} : BaseViewModel() {
override fun onActivityCreated(extras: Bundle?) {
super.onActivityCreated(extras)
}
}

Rather than creating all files (like AndroidManifest.xml.ftl), you can even reuse some file from activities/common.

Done! We have successfully completed all the steps for a template creation.

Let’s see the magic

Now open Android Studio (restart if you have already opened).

Open the project and and right click on the package where you want to create your new AppActivity.

Select AppActivity from the templates menu.

https://gist.github.com/NewgtActivitygtAppActivity

On selecting AppActivity, the template dialog will popup with the input fields which we have configured in template.xml.

Change the values as per your needs and click on Finish.

Multiple files will be created and all the files will be compatible with the architecture.

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="com.ktvipin.templatedemo.demo.DemoViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.ktvipin.templatedemo.demo.DemoActivity">
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
package com.ktvipin.templatedemo.demo
import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.ktvipin.templatedemo.R
import com.ktvipin.templatedemo.core.BaseActivity
import com.ktvipin.templatedemo.databinding.ActivityDemoBinding
class DemoActivity : BaseActivity<ActivityDemoBinding, DemoViewModel>() {
override val layoutId: Int
get() = R.layout.activity_demo
override val viewModelClass: Class<DemoViewModel>
get() = DemoViewModel::class.java
override fun onCreated(savedInstanceState: Bundle?, extras: Bundle?) {
super.onCreated(savedInstanceState, extras)
}
companion object {
fun start(context: Context) {
context.startActivity((Intent(context, DemoActivity::class.java)))
}
}
}
view raw DemoActivity.kt hosted with ❤ by GitHub
package com.ktvipin.templatedemo.demo
import android.os.Bundle
import com.ktvipin.templatedemo.core.BaseViewModel
class DemoViewModel : BaseViewModel() {
override fun onActivityCreated(extras: Bundle?) {
super.onActivityCreated(extras)
}
}

You can also see that the Activity will be added to the AndroidManifest.xmlwith a label referred from strings.xml.

Wrap Up

The goal of this article is to give you a quicker idea of creating templates using Android Studio’s template engine (FreeMarker). Creating custom template will help to reduce writing boilerplate and to follow common standards and practices across the team.

The architecture used was very basic and may not have included proper configurations. Please change the code as per your requirements.

All the code mentioned in this article are available on my GitHub repo:

ktvipin27/ArchitectureTemplate

Android studio template for a simple architecture. – ktvipin27/ArchitectureTemplate

github.com

 

References

Thanks for reading! I hope you enjoyed this article. Please show your love by clicking on the? buttonFollow me to get notified when I post new articlesIf you have any feedback, feel free to reach me on Twitter | LinkedIn | Github.

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
In the first two articles, we explored the challenges of implementing traditional Clean Architecture…
READ MORE
blog
Here’s a scenario that’s recognisable to most Android developers… You are tasked with adding…
READ MORE
blog
I wrote recently about the theory behind good Android app architecture. It ended up…
READ MORE
blog
As part of a recent project, I decided to utilize Jetpack Compose for my…
READ MORE

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.

Menu