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 ,打开如下页面:
点击加号创建新的类模版
在这填写模版的名字和生效的类型文件名
模版的内容,可变的部分用变量代替
举例一个简单的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" /> ...// 省略一些参数// ... <thumbs > <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 > 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.然后添加了四个参数:adapterName
,layoutName
,itemViewName
,modelName
。最后制定了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.ftl
、Model.kt.ftl
、ItemView.kt.ftl
、layout.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.Contextimport android.view.Viewimport android.view.ViewGroupimport com.tencent.news.pullrefreshrecyclerview.RecyclerViewAdapterEximport com.tencent.news.pullrefreshrecyclerview.RecyclerViewHolderExclass $ {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.Contextimport android.util.AttributeSetimport android.view.LayoutInflaterimport 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/