Android Studio 自定义模板

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/f409031mn/article/details/85226824

前言

在开发 Android 项目的过程中,难免会遇到重复编写同一段逻辑的代码的情况,就拿目前比较流行的 MVP 模式来举例好了,要实现一个页面的 MVP 开发,我们需要编写以下的类:

  • 一个 MVP 的契约接口,里面有一个 view 层接口 和 P 层的接口(或抽象类)
  • 对应的view 层接口的实现类
  • 对应的P 层接口的实现类
  • 对应的 model

如果当前页面需要使用 RecyclerView ,那么还得去写对应的 AdapterviewHolder

等你全部弄完后,估计你的大脑都快进入 CD 阶段了,效率一点都不搞高效

因此,我们很有必要去使用一些自动生成代码的技巧,比方说 Android Studio Template

Android Studio Template

当我们通过 Android Studio 创建一个新的 Actvity 时可以看到下面的图片:

在这里插入图片描述

只要点击下去,就能很轻松生成一个全新的 Actvity,这就是 Android Studio Template 的作用

Android Studio Template 依靠 FreeMarker 引擎,将事先定义好的模板文件生成我们所需的 class 文件、layout 文件等等,可以极大减少样板式代码的编写,帮助我们节省大量的时间

先去到 Android Studio\plugins\android\lib\templates 下面,这里就是模板的存放位置 ,可以看到这里其他默认的模板:

在这里插入图片描述

这里以相对简单典型的 EmptyActivity为例进行讲解

在这里插入图片描述

用编译器打开,可以看到下面的东西:

在这里插入图片描述

下面我们来一个一个来看,这里先将 kt(SimpleActivity.kt.ftl) 的模板忽略掉,先看 SimpleActivity.java.ftl 里面的内容:

package ${packageName};

........
........
........

public class ${activityClass} extends ${superClass} {

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

将全部的影响因素都去掉了,这里就很好懂了,这个就是 Activity 代码生成模板,想必这里时候都注意到了 ${activityClass} 等变量了,这些都是从外部获取值的标量,那么这个外部是哪里呢?

其实就是 template.xml

<?xml version="1.0"?>
<template
    format="5"
    revision="5"
    //模板的名字,不可重复(注释在开发模板时需删除)    
    name="Empty Activity"
    minApi="9"
    minBuildApi="14"
    //模板的提示语(注释在开发模板时需删除)    
    description="Creates a new empty activity">
    //category 是模板类型(注释在开发模板时需删除)   
    //不写的话,无法出现了上面图一右键菜单里面(注释在开发模板时需删除)    
    <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="generateLayout"
        name="Generate Layout File"
        type="boolean"
        default="true"
        help="If true, a layout file will be generated" />

    <parameter
        id="layoutName"
        name="Layout Name"
        type="string"
        constraints="layout|unique|nonempty"
        suggest="${activityToLayout(activityClass)}"
        default="activity_main"
        visibility="generateLayout"
        help="The name of the layout to create for the activity" />
     ...........
    <parameter
        id="packageName"
        name="Package name"
        type="string"
        constraints="package"
        default="com.mycompany.myapp" />

    <!-- 128x128 thumbnails relative to template.xml -->
    <thumbs>
        <!-- default thumbnail is required -->
        <thumb>template_blank_activity.png</thumb>
    </thumbs>

    <globals file="globals.xml.ftl" />
    <execute file="recipe.xml.ftl" />

</template>

这里也是只保留一些相对熟悉的内容,看到上面的 ${activityClass} 等字段,是不是就和一开始的 SimpleActivity.java.ftl 里面的内容链接上了呢

现在,先将上面的一些重要节点梳理一遍吧:

category

这个是模板的分类指向,如下图:

在这里插入图片描述

如果需要新增分类的话,category 也是需要修改的

thumb

这个是模板编译的封面图

在这里插入图片描述

parameter

这里设置在创建模板时可修改的参数变量

在这里插入图片描述

该节点有以下参数:

  • id:变量名, 变量的唯一标识,不可重复

  • name: 在创建模板时展示的变量名

  • type :类型,有 stringbooleanenum 等,经常用到的也就 stringboolean

  • constraints:变量约束, 常见的有 class,代表类名;layout 代表布局名;package 代表包路径; unique 则是不能与现有的重复;nonemptye 表示不能为空,一般来说这里直接 copy 原有的代码即可

  • suggest: 推荐值,未修改变量时根据其他变量生成,这里到了一些函数,比如在 layoutName 里有

     suggest="${activityToLayout(activityClass)}"
    

    这里的 activityToLayout(activityClass) 的作用是 activityClass的名字转为规范的 layout 名字,比如参数MainActivity 就会返回 activity_main
    除了 activityToLayout 这个函数,还有下面的这些函数:

    • classToResource(String):转为小写并删除资源提示的字符串,比方说 HomeFragment 就会转换为 home
    • layoutToActivity(string)activityToLayout 的反向操作
  • default:默认值

  • help:当编辑一个变量时,显示的提示语

globals 和 execute

execute:执行 recipe.xml.ftl文件的内容,将模板文件生成具体的可用文件,一般不用修改

globals :执行 global.xml.ftl文件的内容,一般不用修改

这两个都是外部文件,相信这时聪明的读者肯定已经知道这个 template.xml 的作用了

这时再看下 global.xml.ftl

<?xml version="1.0"?>
<globals>
    <global id="hasNoActionBar" type="boolean" value="false" />
    <global id="parentActivityClass" value="" />
    <global id="simpleLayoutName" value="${layoutName}" />
    <global id="excludeMenu" type="boolean" value="true" />
    <global id="generateActivityTitle" type="boolean" value="false" />
    <#include "../common/common_globals.xml.ftl" />
</globals>

整个文件很简单,用 global 标签定义了一系列的全局参数,供其他的模板文件使用

<#include> 标签的作用,这个很好猜测,有兴趣可以打开对应的文件看下,这里就不多说了

接着看 recipe.xml.ftl :

<?xml version="1.0"?>
.......
<recipe>
    <#include "../common/recipe_manifest.xml.ftl" />
.......
    <#include "../common/recipe_simple.xml.ftl" />
    <open file="${escapeXmlAttribute(resOut)}/layout/${layoutName}.xml" />
</#if>
.......
<instantiate from="root/src/app_package/SimpleActivity.java.ftl"
                   to="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />
<open file="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />
</recipe>

这里也是只保留一些核心内容

<#include> 标签那行表示包含了 recipe_manifest.xml.ftl 文件的内容,打开对应文件后内容如下:

<recipe folder="root://activities/common">

    <#if requireTheme!false>
    <#include "recipe_theme.xml.ftl" />
    </#if>

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

</recipe>

这里使用了 merge 标签,其作用就是将定义的 AndroidManifest.xml.ftl 里面的代码注入到项目中的 AndroidManifest.xml 中,比方说我们新生成的 activity 类在 AndroidManifest.xml 中自动添加注册代码

instantiate 是一个较为关键的标签 ,它的作用是以指定的 .ftl 模板文件,生成对应的内容的文件,不过需要指定文件名,比方说 SimpleActivity.java.ftl 为模板,依据里面的内容生成一个新的 HomeActivity.java 文件

open 标签这个就很好理解了,就是打开我们刚才生成的文件

好了,知道了以上的内容后,就开始制作一个自己的模板吧

定制模板

现在是动手的时间了,目标是自动生成下面的代码:

在这里插入图片描述

第一步、找到需要生成的文件并整理模板文件

为了方便,这里可以直接复制 EmptyActivity 文件,在 templates 下面新建个文件夹和重命名 EmptyActivity 文件夹:

在这里插入图片描述

要制作模板,优先要确定需要模板帮助我们生成什么代码文件,目前我们需要就是上面的 4 个文件,那么就创建 4个对应的 .ftl 文件,首先是 3 个 Java 文件的模板:

在这里插入图片描述

然后再新建几个文件,这里就是 xml 文件的模板了:

在这里插入图片描述

第二步、整理模板文件需要的变量

这里以代码量最多的 SimpleActivity.java.ftl 为例子,其他的请看结尾的文件

package ${packageName};

import android.view.View;

public class ${activityName} extends BaseActivity<${presenterName}> 
    implements ${contractName}.${contract}View, View.OnClickListener{

    @Override
    public int getLayoutId() {
        return R.layout.${activityLayoutName};
    }

     @Override
    public void initView() {

    }

    @Override
    public void initData() {

    }

    @Override
    @SingleClick
    public void onClick(View v) {
  		switch (v.getId()) {
            default:
                break;
        }
    }
}

上面的代码参数什么的,已经解析过一遍了,就不多说了

第三步、添加清单文件的处理

这里直接使用下面代码即可,不过文件存放位置要正确:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <application>
        <activity android:name="${packageName}.${activityName}"/>
    </application>

</manifest>

在这里插入图片描述

第四步、配置好 template.xml

现在已经有了代码模板了,就要设置变量了,这里就直接贴代码了:

<?xml version="1.0"?>
<template
    format="5"
    revision="5"
    name="Mvp Empty Activity"
    minApi="9"
    minBuildApi="14"
    description="项目中的 Mvp 结构的Base Activity 模板">
    //category 是模板类型(注释在开发模板时需删除)   
    //不写的话,无法出现了上面图一右键菜单里面(注释在开发模板时需删除)   
    <category value="Mvp" />
    <formfactor value="Mobile" />

    <parameter
        id="contract"
        name="Mvp Name"
        type="string"
        constraints="class|unique|nonempty"
        default="Main"
        help="即将创建模块的名字" />

    <parameter
        id="activityName"
        name="Activity Name"
        type="string"
        constraints="class|unique|nonempty"
        suggest="${contract}Activity"
        default="MainActivity"
        help="即将创建 Activity " />

    <parameter
        id="contractName"
        name="Contract Name"
        type="string"
        constraints="class|unique|nonempty"
        suggest="${contract}Contract"
        default="MainContract"
        help="即将创建的 MVP 契约类" />

    <parameter
        id="presenterName"
        name="Presenter Name"
        type="string"
        constraints="class|unique|nonempty"
        suggest="${contract}Presenter"
        default="MainPresenter"
        help="即将创建的 MVP 的 P层" />

    <parameter
        id="activityLayoutName"
        name="Activity Layout Name"
        type="string"
        constraints="layout|unique|nonempty"
        suggest="${activityToLayout(activityName)}"
        default="activity_main"
        help="即将创建 Activity xml 的名字" />
    
    <parameter
        id="packageName"
        name="Package name"
        type="string"
        constraints="package"
        default="com.mycompany.myapp" />

    <!-- 128x128 thumbnails relative to template.xml -->
    <thumbs>
        <!-- default thumbnail is required -->
        <thumb>template_blank_activity.png</thumb>
    </thumbs>

    <globals file="globals.xml.ftl" />
    <execute file="recipe.xml.ftl" />

</template>

第五步、globals.xml.ftl 和 recipe.xml.fl

globals.xml.fl 保存原样即可,但是部分文件路径需要修改一下:

<?xml version="1.0"?>
<globals>
    ............
    <#include "../../activities/common/common_globals.xml.ftl" />
</globals>

是的,就是 common 的路径需要修改一下,当然你也可以将它删除了,如果你不需要用到它的话

再来看看 recipe.xml.fl 文件

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

    <instantiate from="root/src/app_package/SimpleActivity.java.ftl"
                   to="${escapeXmlAttribute(srcOut)}/${activityName}.java" />

    <instantiate from="root/src/app_package/Contract.java.ftl"
                   to="${escapeXmlAttribute(srcOut)}/${contractName}.java" />

    <instantiate from="root/src/app_package/Presenter.java.ftl"
                   to="${escapeXmlAttribute(srcOut)}/${presenterName}.java" />

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

    <open file="${escapeXmlAttribute(manifestOut)}/AndroidManifest.xml" />

    <open file="${escapeXmlAttribute(srcOut)}/${activityName}.java" />

    <open file="${escapeXmlAttribute(srcOut)}/${contractName}.java" />

    <open file="${escapeXmlAttribute(srcOut)}/${presenterName}.java" />

    <open file="${escapeXmlAttribute(resOut)}/layout/${activityLayoutName}.xml" />
</recipe>

这里是自己去配置注入到清单文件的代码了,这样子足够简单,可以避免一些运行错误

说到运行错误,这里提一下,如果执行模板的代码出现问题,Android Studio 可以查看到具体的原因,大大减轻了编写模板的难度

测试模板

重启 Android Studio,让模板生效,这时候就可以测试我们刚才的模板了:

在这里插入图片描述

在这里插入图片描述

我的模板下载地址为:https://download.csdn.net/download/f409031mn/10871230

结语

模板看着很简单,不过真正要写出适合自己的模板,还是要花点时间的

但是在掌握它之后,你就会对它爱不释手了

猜你喜欢

转载自blog.csdn.net/f409031mn/article/details/85226824