What is the root of human progress? It’s laziness, yes, it’s laziness, and it’s when you want to be lazy that you find an easier way to get things done. The same goes for writing code. Programmers who don’t want to be lazy are not good programmers. Let’s look at how to be lazy.

First of all, declare that the role of this article is purely to throw a brick to attract jade, will not be too detailed introduction of the specific method of use, only about the use of ideas and tread pit diary. Although this article uses MVP as an example, this article does not stop there. You can generate templates for almost any template code for later use.

Anyone who has worked on an Android project in MVP mode knows that creating an activity usually involves creating five classes, including interfaces. Writing one or two interfaces is fine. If you’re writing an entire project, just creating these classes is a pain in the ass. Of course there is. The easiest way to do this is to use the file template that comes with Studio.


#if (${PACKAGE_NAME} && ${PACKAGE_NAME} ! = "")package ${PACKAGE_NAME}; #end #parse("File Header.java") public class ${NAME}Presenter extends StyleActivityPresenter<I${NAME}View,I${NAME}Model>{ @Override protected void initPresenter (Bundle savedInstanceState) { }}Copy the code

Effect:

public class TestPresenter extends StyleActivityPresenter<ITestView.ITestModel> {
    @Override
    protected void initPresenter (Bundle savedInstanceState) {}}Copy the code

${NAME} is the NAME you typed when you created the file.

The template function above makes it easy for me to create a class without having to write too much code, but it is still not good enough. As mentioned above, MVP mode usually contains 5 classes, layout files, and activity registration code, which is required for each activity creation. If you just use the file Template function above, you still need to create files multiple times under different packages. Is there a lazy way to do this? Of course, there is studio’s powerful activity template function. In fact, this function is often used, but many people do not pay attention to it. For example, when we create a new project:

This is actually the activity template that comes with Studio. We know that when we select a template of a certain type and generate a project, the project will have the corresponding Java code and layout, and it will register the activity in the manifest for you.

The following is what we need to customize this function, let it implement the input of a class name in your specified package automatically generated 5 MVP related classes and layout files have been manifest registration.

XXX\android-studio\plugins\ Android \lib\templates\activities :XXX\android-studio\plugins\ Android \lib\templates\activities :XXX\android-studio\plugins\ Android \lib\templates\activities :XXX\android-studio\plugins\ Android \lib\templates\activities Here are a few important files in the template directory and their functions. Take LoginActivity as an example:

Root: This directory contains our code template, which is similar to the file template code, but with some differences. I like to call them template output prototypes.

Globals.xml: This file is used to configure some special attributes, such as whether the page is launched or not.

Recipe. XML: This file mainly configures which files to generate, which template to use, and which directory to export them to.

Template. XML is used to define variable properties such as file names and package names.

Template_login_activity.png: This is the interface diagram shown in the image above. You usually don’t need to use it, but you can replace it with your own image.

Here’s how to customize your own templates. (As stated at the beginning of this article, this article does not cover the details of how to customize templates.) > < p style = “max-width: 100%; clear: both; min-height: 1em;

  • First of all, it is recommended that everyone from the most simple template to try, don’t start completely MVP class to write their own, such as familiar with the relevant properties and rules to write again after the MVP related template, this is because the template this thing is not our project code, if you configure wrong, although there will be an error when using tips, but is not accurate, So if you’re writing too many things at once, it’s slow to check for errors.
  • Second, it is recommended to directly copy the system template code such as (LoginActivity template), and then modify on this basis, do not create each file, for the same reason as the first one, error prone.

Then we begin our template creation journey:

1. Create a Demo project for testing

Very simple, just a default auto-generated MainActivity class

2. Copy the LoginActivity template

Copy the LoginActivity folder in our directory XXX\ Android-studio \plugins\ Android \lib\templates\ Activities and rename it MVPTestActivity. Then go to the folder we copied. Open the template. XML file and change the name value, preferably to the name of your directory. In this case, we call it MVPTestActivity.

<template
    format="5"
    revision="6"
    name="Login Activity"-->This property is renamed MVPTestActivity Description ="Creates a new login Activity, allowing users to optionally sign in with Google+ or enter an email address and password to log in to or register with your application." minApi="8" minBuildApi="14">Copy the code

Parameter, this label is the field on our configuration interface. I only take screenshots of two important fields here:

 <parameter
        id="activityClass"
        name="Activity Name"
        type="string"
        constraints="class|unique|nonempty"
        default="LoginActivity"
        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_login"
        help="The name of the layout to create for the activity" />Copy the code

The id of the activityClass tag is the name of our class, the ID of the layoutName is the name of our layout, and the ID field is the reference to name in other configuration files. The type field is the type. Constraints are input constraints, and default is of course the default value implemented on the configuration screen. For details on how each property is used, check out the blog posts for details. This is not the focus of this article.

We’ll start with the simplest, so we definitely don’t need properties like Title, so we’ll comment out fields that don’t need to be displayed or configured.

 <! -- <parameter id="activityTitle" name="Title" type="string" constraints="nonempty" default="Sign in" help="The name of the activity." /> -->.Copy the code

Some other fields can also be adjusted according to our needs, so that the file is basically modified.

Recipe. XML. If we open this file, we can see

<?xml version="1.0"? >
<#import "root://activities/common/kotlin_macros.ftl" as kt>
<recipe>
   <#if appCompat && !(hasDependency('com.android.support:appcompat-v7')) >
       <dependency mavenUrl="com.android.support:appcompat-v7:${buildApi}.+" />
    </#if>

    <#if (buildApi gte 22) && appCompat && !(hasDependency('com.android.support:design')) >
        <dependency mavenUrl="com.android.support:design:${buildApi}.+" />
    </#if>

    <#include "./common/recipe_theme.xml.ftl" />

    <merge from="root/AndroidManifest.xml.ftl"
             to="${escapeXmlAttribute(manifestOut)}/AndroidManifest.xml" />

    <merge from="root/res/values/dimens.xml"
             to="${escapeXmlAttribute(resOut)}/values/dimens.xml" />

    <merge from="root/res/values/strings.xml.ftl"
             to="${escapeXmlAttribute(resOut)}/values/strings.xml" />

    <instantiate from="root/res/layout/activity_login.xml.ftl"
                   to="${escapeXmlAttribute(resOut)}/layout/${layoutName}.xml" />

<#if generateKotlin>
    <@kt.addAllKotlinDependencies />
    <instantiate from="root/src/app_package/LoginActivity.kt.ftl"
                   to="${escapeXmlAttribute(srcOut)}/${activityClass}.kt" />
    <open file="${escapeXmlAttribute(srcOut)}/${activityClass}.kt" />
<#else>
    <instantiate from="root/src/app_package/LoginActivity.java.ftl"
                   to="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />
    <open file="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />
</#if>

</recipe>Copy the code

If you are confused, it is not difficult. Ifelse believes that without too much explanation, we will focus on a few other important tags.

  • This is mainly used in resource files or manifest files, because we usually need to merge a new XML file with a project. This is a little bit about manifest file merging, because other resource file merging is very simple.

Open the manifest file in the MVPTestActivity\root directory. The main code is as follows:

<activity android:name=".${activityClass}" <#if isNewProject> android:label="@string/app_name" <#else> android:label="@string/title_${simpleName}" </#if> <#if hasNoActionBar> android:theme="@style/${themeNameNoActionBar}" <#elseif ! (hasApplicationTheme! false)> android:theme="@style/${themeName}" </#if> <#if buildApi gte 16 && parentActivityClass ! = "">android:parentActivityName="${parentActivityClass}"</#if>> <#if parentActivityClass ! = ""> <meta-data android:name="android.support.PARENT_ACTIVITY" android:value="${parentActivityClass}" /> </#if> <@manifestMacros.commonActivityBody /> </activity>Copy the code

${} is a reference to an attribute in another file.

<activity android:name=".${activityClass}" <#if isNewProject> android:label="@string/app_name" </#if> <#if hasNoActionBar> android:theme="@style/${themeNameNoActionBar}" <#elseif ! (hasApplicationTheme! false)> android:theme="@style/${themeName}" </#if>> <@manifestMacros.commonActivityBody /> </activity>Copy the code
  • Instantiate means to create files
  • Open file is, of course, after we’ve generated the class, open the related class

For example, if we want to instantiate a simple layout file, we’ll see that the instantiate tag says “from” and “to” :

<instantiate from="root/res/layout/activity_login.xml.ftl"
                   to="${escapeXmlAttribute(resOut)}/layout/${layoutName}.xml" />Copy the code

The root/res/layout/activity_login.xml. FTL path is the same as the file in the template directory. ${escapeXmlAttribute(resOut)} is the home directory of the res resource for the current project, and ${layoutName} is clearly the String configured in the template.xml referenced by the user. With these attributes in mind, we made the following changes:

  • Delete the values folder in the MVPTestActivity\root\res directory
  • Comment out the two resource merge tags in the recipe. XML file (leaving them will result in a null pointer because we’ve deleted the resource template)
  • Finally, modify the loginactivity. Java file in MVPTestActivity\root\ SRC \app_package. This file contains too much content, so we can simplify it to the following code:
package ${packageName};

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
<#if applicationPackage??>
import ${applicationPackage}.R;
</#if>
/**
 * A login screen that offers login via email/password.
 */
public class ${activityClass} extends AppCompatActivity {

    @Override
    protected void onCreate (Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.${layoutName});
    }

}Copy the code

Ok, that’s it, now that we’ve made some of the simplest changes, let’s take a look at the results. After saving each file, restart Studio (don’t try to change it all at once when you start writing, or you’ll probably give it up for failure) :

As you can see, we have changed the template description, changed the default activity name, removed the title and parent fields, let’s click finish to test the effect:

TestActivity and Activity_test are both automatically generated from templates, and mainfest has registered this activity, so you can try it out for yourself.

The above steps have completed the basic use of the template, but it is not enough for our requirements, because we want to generate multiple files, and multiple files may not be in the same package, so how do we achieve this?

First, suppose our project is structured as follows:

So what I need now is:

  • Create a TestActivityPresenter class under the Presenter package
  • Create a TestActivityModel under the Model package
  • Create a TestActivityView under the View package
  • Create an ITestActivityModel and ITestActivityView interface under the Port package
  • And TestActivityView should implement the ITestActivityView interface, and TestActivityModel should implement the ITestActivityModel interface
  • Generate the corresponding layout file and register the activity

The last one, basically need not say again, we have satisfied the initial changes, focus on the following several implementation, mainly is to talk about how to generate corresponding files in different directories.

Back in our recipe. XML file, let’s pay attention here

<instantiate from="root/src/app_package/LoginActivity.java.ftl"
                   to="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />Copy the code

This is the configuration code used to generate the TestActivity we just created. Obviously, if we want to generate more files, we just need to write more tags like this:

 <! --IView -->
    <instantiate from="root/src/app_package/LoginActivity.java.ftl"
                   to="${escapeXmlAttribute(srcOut)}/I${activityClass}View.java" />
  <! --View -->
    <instantiate from="root/src/app_package/LoginActivity.java.ftl"
                   to="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />
  <! --IModel -->
    <instantiate from="root/src/app_package/LoginActivity.java.ftl"
                   to="${escapeXmlAttribute(srcOut)}/I${activityClass}Model.java" />   
  <! --Model -->  
    <instantiate from="root/src/app_package/LoginActivity.java.ftl"
                   to="${escapeXmlAttribute(srcOut)}/${activityClass}Model.java" />    
  <! --Presenter -->
    <instantiate from="root/src/app_package/LoginActivity.java.ftl"
                   to="${escapeXmlAttribute(srcOut)}/${activityClass}Presenter.java" />                                    
    <open file="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />Copy the code

Note the last point of the to value. I used a concatenation method to specify the name format of each file, so that all the MVP class names conform to a certain naming convention, of course not the same as mine, but you must have your own format, do not arbitrarily name, this is the basic, but I will explain why. This will give us 5 files, but it is not enough, because they are all in the same directory, how to make them in their own directory, and then change:

  <! --IView -->
    <instantiate from="root/src/app_package/LoginActivity.java.ftl"
                   to="${escapeXmlAttribute(srcOut)}/port/I${activityClass}View.java" />
  <! --View -->
    <instantiate from="root/src/app_package/LoginActivity.java.ftl"
                   to="${escapeXmlAttribute(srcOut)}/view/${activityClass}.java" />
  <! --IModel -->
    <instantiate from="root/src/app_package/LoginActivity.java.ftl"
                   to="${escapeXmlAttribute(srcOut)}/port/I${activityClass}Model.java" />   
  <! --Model -->  
    <instantiate from="root/src/app_package/LoginActivity.java.ftl"
                   to="${escapeXmlAttribute(srcOut)}/model/${activityClass}Model.java" />    
  <! --Presenter -->
    <instantiate from="root/src/app_package/LoginActivity.java.ftl"
                   to="${escapeXmlAttribute(srcOut)}/${activityClass}Presenter.java" />                                    
    <open file="${escapeXmlAttribute(srcOut)}/presenter/${activityClass}.java" />
</#if>Copy the code

Isn’t that easy? Just use ${escapeXmlAttribute(srcOut)} to keep up with the package path. We also lack the template source file, because the from file above is the LoginActivity file, which means that the code generated for each class is the same.

Now let’s finish the last step, write the template code, this is relatively easy, after all, is basically Java code, not difficult to you, In MVPTestActivity\root\ SRC \app_package, we will make four copies of loginactivity.java.ftl file, regardless of the kt file, which is for Kotlin. If you are using Kotlin, you can also copy that file. Then change the name in turn, and the effect is as follows:

The code for each class is as follows: mvpView.java

package ${packageName}.view; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; <#if applicationPackage?? > import ${applicationPackage}.R; </#if> import com.xujl.demo.port.I${activityClass}View; import com.xujl.demo.presenter.${activityClass}Presenter; class ${activityClass}View extends AppCompatActivity implements I${activityClass}View{ private ${activityClass}Presenter  mPresenter; @Override protected void onCreate (Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.${layoutName}); mPresenter = new ${activityClass}Presenter(this); }}Copy the code

MvpPresenter.java

package ${packageName}.presenter;

import com.xujl.demo.model.${activityClass}Model;
import com.xujl.demo.port.I${activityClass}Model;
import com.xujl.demo.port.I${activityClass}View;

public class ${activityClass}Presenter  {
    private I${activityClass}View mView;
    private I${activityClass}Model mModel;

    public ${activityClass}Presenter(I${activityClass}View view){
        mView = view;
        mModel = new${activityClass}Model(); }}Copy the code

MvpModel.java

package ${packageName}.model;

import com.xujl.demo.port.I${activityClass}Model;

public class ${activityClass}Model implements I${activityClass}Model {



}Copy the code

IMvpModel.java

package ${packageName}.port;

public interface I${activityClass}Model{

}Copy the code

IMvpView.java

package ${packageName}.port;


public interface I${activityClass}View{

}Copy the code

Finally, don’t forget to change the filename of the template from in victim.xml and the name of the activity in the template Mainfest:

<! --IView --> <instantiate from="root/src/app_package/IMvpView.java.ftl" to="${escapeXmlAttribute(srcOut)}/port/I${activityClass}View.java" /> <! --View --> <instantiate from="root/src/app_package/MvpView.java.ftl" to="${escapeXmlAttribute(srcOut)}/view/${activityClass}.java" /> <! --IModel --> <instantiate from="root/src/app_package/IMvpModel.java.ftl" to="${escapeXmlAttribute(srcOut)}/port/I${activityClass}Model.java" /> <! --Model --> <instantiate from="root/src/app_package/MvpModel.java.ftl" to="${escapeXmlAttribute(srcOut)}/model/${activityClass}Model.java" /> <! --Presenter --> <instantiate from="root/src/app_package/MvpPresenter.java.ftl" to="${escapeXmlAttribute(srcOut)}/${activityClass}Presenter.java" /> ---------------------------------------------------- <activity android:name=".view.${activityClass}View" <#if isNewProject> android:label="@string/app_name" </#if> <#if hasNoActionBar> android:theme="@style/${themeNameNoActionBar}" <#elseif ! (hasApplicationTheme! false)> android:theme="@style/${themeName}" </#if>> <@manifestMacros.commonActivityBody /> </activity>Copy the code

Finally, let’s see how this works (make sure you right-click on the main package, otherwise the build path may fail) :



At this point we’re done with a complete template, of course, that is both package structure and naming, or MVP structure, is my own definition, you can completely according to your own project actual situation to write, I’m such a defined structure, benefit is only need to type in the name of the class, you could generate on the other, of course you can also set a few more package name field, Used to dynamically configure package paths for model, View, Presenter, and interface.

Finally, attach a link to the template file for the entire MVPTestActivity:pan.baidu.com/s/1skW4nTVPassword: kbve