自定义Android-Studio工程模板

每次创建新的Android Studio工程时,都需要手动修改一些工程的配置,
比如删除不必要的依赖、删掉Activity中不必要的代码 配置私有maven库的地址、增加公用的依赖库、修改.gitingore、关闭lint的严格检查、配置APK的输出路径等等,当有很多类似的项目的时候就有大量的无用功,
浪费时间,就可以考虑修改Android Studio默认的project和module模板。

默认模板路径

Android Studio的工程模板在安装目录的“\plugins\android\lib\templates\gradle-projects”文件夹下,这里面包含了导入工程模板、新建工程模板、新建module模板等。

模板的文件结构

学习编写模板最好的方式就是参考Android Studio中已经提供的最简单的模板,那么在Android Studio中最简单的activity模板就是:Empty Activity了,我们打开该模板文件,首先对文件结构有个直观的了解,如图:

文件夹包含

  • template.xml
  • globals.xml.ftl
  • recipe.xml.ftl
  • root文件夹 存放对应源码的ftl文件,以及资源文件
  • 效果缩略图

下面我们逐一对上述每个文件的作用就行介绍。

template.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<?xml version="1.0"?>
<template
format="5"
revision="5"
name="Empty Activity"
minApi="9"
minBuildApi="14"
description="Creates a new empty 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="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="isLauncher"
name="Launcher Activity"
type="boolean"
default="false"
help="If true, this activity will have a CATEGORY_LAUNCHER intent filter, making it visible in the launcher" />
<parameter
id="backwardsCompatibility"
name="Backwards Compatibility (AppCompat)"
type="boolean"
default="true"
help="If false, this activity base class will be Activity instead of AppCompatActivity" />
<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>

其中

  • template中的name属性,对应新建Activity时显示的名字

  • category对应New的类别为Activity

剩下的,对应我们AndroidStudio新建Empty Activity的界面就非常好理解了,如图:

看到这个界面,大部分属性都应该能才出来了,我们重点看parameter,界面上每一个紫色框出来的部分都对应一个parameter,部分属性介绍:

  • id :唯一标识,最终通过该属性的值,获取用户输入值(文本框内容,是否选中)
  • name:界面上的类似label的提示语
  • type : 输入值类型
  • constraints:填写值的约束
  • suggest:建议值,比如填写ActivityName的时候,会给出一个布局文件的建议值。
  • default:默认值
  • help:底部显示的提升语

这个部分对应界面还是非常好理解的,大家可以简单的修改一些字符串,或者添加一个parameter,重启AS,看看效果。

template.xml的最下面的部分引入了globals.xml.ftl和recipe.xml.ftl。

这两个我们会详细介绍。

globals.xml.ftl

1
2
3
4
5
6
7
8
9
<?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标签,分别定义id,type,默认值。

同理,我们可以通过id的值访问到该值,例如:

${hasNoActionBar}的值为false。

recipe.xml.ftl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0"?>
<recipe>
<#include "../common/recipe_manifest.xml.ftl" />
<#if generateLayout>
<#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>

为了介绍,我将该xml中比较重要的几个标签都列出来了:

  • copy :从root中copy文件到我们的目标目录,比如我们的模板Activity需要使用一些图标,那么可能就需要使用copy标签将这些图标拷贝到我们的项目对应文件夹。
  • merge : 合并的意思,比如将我们使用到的strings.xml合并到我们的项目的stirngs.xml中
  • instantiate : 和copy类似,但是可以看到上例试将ftl->java文件的,也就是说中间会通过一个步骤,将ftl中的变量都换成对应的值,那么完整的流程是ftl->freemarker process -> java。
  • open:在代码生成后,打开指定的文件,比如我们新建一个Activity后,默认就会将该Activity打开。

在介绍instantiate时,涉及到了freemarker,不可避免的需要对它进行简单的介绍。

目前我们已经基本了解了一个模板其内部的文件结构了,以及每个文件大致包含的东西,我们简单做个总结:

  • template 中parameter标签,主要用于提供参数
  • global.xml.ftl 主要用于提供参数
  • recipe.xml.ftl 主要用于生成我们实际需要的代码,资源文件等;例如,利用参数+MainActivity.java.ftl -> MainActivity.java;其实就是利用参数将ftl中的变量进行替换。

    简单的freemarker语法

上面我们已经基本了解模板生成的大致的流程以及涉及到的文件,大致了解了我们生成的源码或者xml文件,需要经过:

1
ftl->freemarker process->java/xml

这样的流程,那么我们必须对freemarker有个简单的了解。

  • 非常简单的例子

比如我们有个变量user=zhy;
有个ftl文件内容:helloL${user}
最后经过freemarker的输出结果即为 hello:zhy

  • if语法
1
2
3
<#if generateLayout>
//生成layout文件
</#if>

看一眼就知道大概的意思了~有一定的编程经验,即使不知道这个叫freemarker,对于这些简单的语法还是能看懂的。

我们最后以Empty Activity模板的中的SimpleActivity为例:

root/src/app_package/SimpleActivity.java.ftl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package ${packageName};
import ${superClassFqcn};
import android.os.Bundle;
public class ${activityClass} extends ${superClass} {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
<#if generateLayout>
setContentView(R.layout.${layoutName});
</#if>
}
}

可以看到其内部包含很多变量,这些变量的值一般来源于用户输入和global.xml.ftl中预定义的值,经过recipe.xml.ftl中instantiate标签的处理,将变量换成实际的值,即可在我们的项目的指定位置,得到我们期望的Activity。

具体的模板实例

蔡小木 wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!
坚持原创技术分享,您的支持将鼓励我继续创作!