Android Studio的三种类型的模版(Templates)创建

Android Studio的三种类型的模版(Templates)创建

如果说使用快捷键是程序员的刀🔪,那灵活的使用代码模版就应该是程序员的剑。

这里说的模版(Templates),是指在使用开发创建类文件,甚至是某些代码块时,IDE自动按照规定的格式创建出类或代码的功能。如果类中有大量相似代码,使用模版可以极大的提高开发效率,降低出错概率。

下面我们看一下如何在Android Studio上使用模版,我将讲述三种模版的创建,分别是:

  • 使用Live Templates创建代码块模版
  • 使用File and Code Templates创建类模版
  • 基于FreeMarker创建多文件模版

1.代码块模版

java中经常需要定义这样的静态常量:

1
private static final int DEFAULT_VALUE = 1;

使用Live Templates后,就可以直接输入const就可以快速定义静态常量:

其设置方法是,打开File->Setting(⌘+,),搜索“Live Templates”,打开如下的界面:

其中,1指的是缩写和该缩写的描述;2中输入的是模版的内容;3中可以指定该模版应用生效的语言和场所,例如,可以限制该模版只应用到Java语言定义变量(declaration)的时候; 4可以将模版中变化的部分定义为变量,如上图中的${name}${value}

上面例子中的const是默认的模版,你可以点击加号添加自己的模版,例如为kotlin定义一个tag常量的模版:

2.创建类模版

如果我经常创建Fragment,有些必填的步骤就可以放到模版里去

打开File->setting,找到File and Code Templates,打开如下页面:

  1. 点击加号创建新的类模版
  2. 在这填写模版的名字和生效的类型文件名
  3. 模版的内容,可变的部分用变量代替

举例一个简单的Fragment模版:

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
#if (${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

public class ${NAME} extends Fragment {

private static final String TAG = "${NAME}";

public static ${NAME} newInstance() {
return new ${NAME}();
}

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.${layout}, container, false);
}

}

其中${NAME}代表的是类名变量,${layout}代表了资源文件名变量,这些变量在创建文件时会要求手动填入。

使用时,在文件夹上点击右键 new 的时候,就可以看到自己定义的模版了:

点击后,填写一下自定义的变量,就可以生成模版文件了:

上方的layout就是在定义模版时定义的变量,而NAME变量是系统预留变量,会被影射成File name这个名字。关于定义类模版的具体用法,可以参考设置中其他的模版,或者参考文档:

https://www.jetbrains.com/help/idea/using-file-and-code-templates.html

一个小尾巴

这里顺便说一下如何定义类的作者信息模版:File and Code Template中有一个Includes标签,打开后是这个样子的:

这里填写一个模版,以后创建类的时候会自动把模版内容放在类名上方。一些像日期一样的变量可以在右下方查询。

3.创建多文件模版

上面两种方法都是可以在设置中搞定的,比较好理解,下面这种就稍微复杂一点了。

相信大家肯定用File -> new -> Activity -> EmptyActivity来创建一个新的页面,Android Studio会自动在manifest文件中注册Activity的名字,并创建好一个固定模版的Java和xml布局文件。

那么它是怎么实现的呢?我们可不可以像它一样也自定义创建多个不同类型的文件模版呢?答案是可以的。Android Studio使用的是Apache的FreeMarker模版引擎生成代码。

Android Studio将所有的这种模版的配置文件放在下面路径:

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

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

可以看下它的目录结构:

-activities -gradle -gradle-projects -other

可以看出它大致对不同类型的模版进行了简单的分类,下面我们以就以activities为例,看一下它是怎么做的。

打开activities->EmptyActivity文件夹,可以看到创建模版所涉及的几个主要文件/夹:

1
2
3
4
5
6
7
8
9
├── globals.xml.ftl
├── recipe.xml.ftl
├── root
│   └── src
│   └── app_package
│   ├── SimpleActivity.java.ftl
│   └── SimpleActivity.kt.ftl
├── template.xml
└── template_blank_activity.png

template.xml

在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
<?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" />

...// 省略一些参数// ...

<!-- 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属性指定了该模版在Android Studio中显示的名字,如:File -> new -> Activity -> EmptyActivity
  • category标签指定了该模版放在Android Studio的那个分类中,如File -> new -> Activity -> EmptyActivity。这里的分类名字可以自己指定。
  • parameter标签指定了该模版需要的参数,例如我在创建EmptyActivity时弹出的Wizard中的每一项其实都是在这里配置的(顺便说一下,标签配置的就是下图中的那个图片):

  • globals标签和execute标签分别制定了全局变量的配置文件和最核心的行为控制的文件(这里recipe.xml.ftl文件我也不知道应该叫什么,暂时这么称呼吧)

recipe.xml.ftl

这是整个配置的核心文件,如果template.xml文件是Android工程中的Layout布局文件,那recipe.xml.ftl就是java文件,它告诉Android Studio需要按照一定的顺序做一些逻辑工作,例如,创建文件,在IDE中打开文件等。下面我们看一下其内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0"?>
<#import "root://activities/common/kotlin_macros.ftl" as kt>
<recipe>
<#include "../common/recipe_manifest.xml.ftl" />
<@kt.addAllKotlinDependencies />

<#if generateLayout>
<#in
clude "../common/recipe_simple.xml.ftl" />
<open file="${escapeXmlAttribute(resOut)}/layout/${layoutName}.xml" />
</#if>

<instantiate from="root/src/app_package/SimpleActivity.${ktOrJavaExt}.ftl"
to="${escapeXmlAttribute(srcOut)}/${activityClass}.${ktOrJavaExt}" />
<open file="${escapeXmlAttribute(srcOut)}/${activityClass}.${ktOrJavaExt}" />

</recipe>

上面的<#include/>标签主要做了一些复用的工作,例如<#include "../common/recipe_manifest.xml.ftl" />就是调用了recipe_manifest.xml.ftl文件在Manifest文件中插入了activity的信息。

我们主要关注下instantiate标签和open标签,这两句话翻译成自然语言就是:

根据模版文件:root/src/app_package/SimpleActivity.${ktOrJavaExt}.ftl生成类文件到${escapeXmlAttribute(srcOut)}/${activityClass}.${ktOrJavaExt}中去。

然后在Android Studio中打开文件:${escapeXmlAttribute(srcOut)}/${activityClass}.${ktOrJavaExt}

如果需要同时生成多个文件,就需要在这里用instantiate标签生成。那么生成文件的模版是在哪里定义的呢?下面说的root文件夹就是。

root文件夹(模版文件夹)

root指的是工程的代码根目录,其内部是src、res甚至AndroidManifest.xml.ftl这样跟工程目录对应位置的模版文件。我们以root/src/app_package/SimpleActivity.java.ftl文件为例,看下其模版是怎么定义的:

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
package ${packageName};

import ${superClassFqcn};
import android.os.Bundle;
<#if (includeCppSupport!false) && generateLayout>
import android.widget.TextView;
</#if>

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

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
<#if generateLayout>
setContentView(R.layout.${layoutName});
<#include "../../../../common/jni_code_usage.java.ftl">
<#elseif includeCppSupport!false>

// Example of a call to a native method
android.util.Log.d("${activityClass}", stringFromJNI());
</#if>
}
<#include "../../../../common/jni_code_snippet.java.ftl">
}

可以看出,就是对一个Activity进行了模版化,其中很多变量提高了该模版的可扩展性,这里可以使用include引入其他模版的内容,或者使用if来进行条件判断,功能还是蛮强大的,更多的语法内容,可以参考FreeMarker模版引擎的文档:

https://freemarker.apache.org/

关于AndroidStudio的EmptyActivity模版创建分析就先到这里,下面我们实际的应用一下看看。

实战:创建列表Adapter模版

背景

目前做的工程中对RecycleView进行了封装,每次创建列表的Adapter时需要创建一系列文件才能开始写逻辑,这些文件包括:

  • 列表的Adapter文件

  • 数据Model的Pojo文件

  • 列表中的View文件(ListItemView)

  • View文件的layout文件

为了减少创建列表Adapter的工作量,按照下面步骤为其创建一套类似于EmptyActivity的模版:

Step 1. 创建相关文件

在第三节提到的Templates路径中的other文件夹下新建一个文件夹,随意命名为“ListAdapter”,然后在内部创建下面的文件目录:

1
2
3
4
5
6
7
8
9
10
11
12
f├── globals.xml.ftl
f├── recipe.xml.ftl
d├── root
d│   ├── res
d│   │   └── layout
f│   │   └── layout.xml.ftl
d│   └── src
d│   └── app_package
f│   ├── Adapter.kt.ftl
f│   ├── ItemView.kt.ftl
f│   └── Model.kt.ftl
f└── template.xml

Step 2. 配置template.xml

要想灵活的填写创建Adapter时涉及到的文件的名字,就要将这些名字参数化,在创建之前让程序员填写,这就需要在template文件中配置相应的参数:

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
<?xml version="1.0"?>
<template
format="5"
revision="1"
name="Create RecycleView Adapter"
minApi="9"
minBuildApi="14"
description="Create a RecycleView Adapter in DreamReader.">

<category value="DreamReader"/>
<formfactor value="Mobile" />

<parameter id="adapterName"
name="Adapter Name"
type="string"
constraints="class|unique|nonempty"
default="RecycleViewAdapter"
help="The name of the adapter of RecycleView"/>

<parameter id="layoutName"
name="Layout Name"
type="string"
constraints="layout|nonempty|unique"
default="view_list_item"
help="The name of the layout file of RecycleView"/>

<parameter id="itemViewName"
name="Item View Name"
type="string"
constraints="class|unique|nonempty"
default="ItemListView"
help="The name of the item list view class of RecycleView"/>

<parameter id="modelName"
name="Model Name"
type="string"
constraints="class|unique|nonempty"
default="ItemModel"
help="The name of the data model class of RecycleView"/>

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

</template>

从上面可以看出,我们自定义了一个名字为DreamReader的category,并将该模版的名字命名为:Create RecycleView Adapter.然后添加了四个参数:adapterNamelayoutNameitemViewNamemodelName。最后制定了global文件和execute文件的名字。

Step 3.配置global文件

global文件需要指定用到的全局变量,这里只需要用到src和res路径:

1
2
3
4
<globals>
<global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
<global id="resOut" value="${resDir}" />
</globals>

Step 4. 配置recipe文件

recipe文件中写明了具体要进行的操作,指定了具体模版文件的路径和名字:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0"?>
<#import "root://activities/common/kotlin_macros.ftl" as kt>
<recipe>

<instantiate from="root/src/app_package/Adapter.kt.ftl"
to="${escapeXmlAttribute(srcOut)}/${adapterName}.kt" />
<open file="${escapeXmlAttribute(srcOut)}/${adapterName}.kt" />

<instantiate from="root/src/app_package/Model.kt.ftl"
to="${escapeXmlAttribute(srcOut)}/${modelName}.kt" />
<open file="${escapeXmlAttribute(srcOut)}/${modelName}.kt" />

<instantiate from="root/src/app_package/ItemView.kt.ftl"
to="${escapeXmlAttribute(srcOut)}/${itemViewName}.kt" />
<open file="${escapeXmlAttribute(srcOut)}/${itemViewName}.kt" />

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

</recipe>

上面代码生成(instantiate)了四个文件,并且让其全部在IDE中打开(open)。其中每个生成一个文件,都为其指定了具体的模版文件:Adapter.kt.ftlModel.kt.ftlItemView.kt.ftllayout.xml.ftl

Step 5. 编写具体的模版文件

下面贴出我写的具体的模版文件内容,由于我是用kotlin写的,所以后缀是kt.ftl,如果是java文件则应该是java.ftl后缀:

Adapter.kt.ftl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package ${escapeKotlinIdentifiers(packageName)}

import android.content.Context
import android.view.View
import android.view.ViewGroup
import com.tencent.news.pullrefreshrecyclerview.RecyclerViewAdapterEx
import com.tencent.news.pullrefreshrecyclerview.RecyclerViewHolderEx

class ${adapterName}(val context:Context) : RecyclerViewAdapterEx<${modelName}>() {
private val TYPE_DEFAULT = 0

override fun getNormalItemType(position: Int): Int {
return TYPE_DEFAULT
}

override fun getLayoutViewByViewType(parent: ViewGroup?, viewType: Int): View {
return ${itemViewName}(context)
}

override fun bindData(holder: RecyclerViewHolderEx?, data: ${modelName}?, dataPos: Int) {
(holder?.itemView as? ${itemViewName})?.setData(data, dataPos)
}

}

其中用到的变量名字都是在第二步中的template中定义的。

Model.kt.ftl

1
2
3
package ${escapeKotlinIdentifiers(packageName)}

data class ${modelName}(var name:String)

ItemView.kt.ftl

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
package ${escapeKotlinIdentifiers(packageName)}

import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.RelativeLayout
<#if applicationPackage??>
import ${applicationPackage}.R
</#if>

import kotlinx.android.synthetic.main.${layoutName}.*

class ${itemViewName} @JvmOverloads constructor(context: Context,
attributeSet: AttributeSet? = null,
defStyleAttributeSet: Int = 0)
: RelativeLayout(context, attributeSet, defStyleAttributeSet) {

init {
LayoutInflater.from(context).inflate(R.layout.${layoutName}, this, true)
}

fun setData(data: ${modelName}?, index: Int) {

}
}

layout.xml.ftl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/sample_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />

</FrameLayout>

Step 6.使用

重启Android Studio,这样,再写列表时,就可以一键生成上面四个文件了,在New列表中可以看到我们定义的category和name:

点击后,弹出填写参数的窗口:

框中的内容都是我们自定义的内容,可以在这里填入想要的文件名,点击finish就生成了相应的文件:

总结

以上就是对Android Studio中模版用法的介绍和简单理解,可能有很多错误的地方,如有问题欢迎指正。本文主要讲了在Android Studio中创建Live Templates代码块模版、File and Code Templates类模版以及创建多文件模版,希望大家在工作中能巧用模版,提高效率。

除了Android Studio,其他JetBrain产品例如Intelli J、Clion、Pycharm等应该都是一样的,但是需要大家自己尝试一下,话说 Jetbrain真是在让程序员变懒这条路上一去不复返了😂。

参考

[1] https://medium.com/androidstarters/mastering-android-studio-templates-ed8fdd98cb78

[2] https://riggaroo.co.za/custom-file-template-group-android-studiointellij/

[3] https://www.jetbrains.com/help/idea/using-file-and-code-templates.html

[4] https://www.jetbrains.com/help/idea/using-live-templates.html

[5] https://freemarker.apache.org/

Author

calvinche

Posted on

2019-03-07

Licensed under

CC BY-NC-SA 4.0

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×