Android Studio 自定義模板
前言
在開發 Android 專案的過程中,難免會遇到重複編寫同一段邏輯的程式碼的情況,就拿目前比較流行的 MVP 模式來舉例好了,要實現一個頁面的 MVP 開發,我們需要編寫以下的類:
- 一個 MVP 的契約介面,裡面有一個
view
層介面 和P
層的介面(或抽象類) - 對應的
view
層介面的實現類 - 對應的
P
層介面的實現類 - 對應的
model
層
如果當前頁面需要使用 RecyclerView
,那麼還得去寫對應的 Adapter
和 viewHolder
等你全部弄完後,估計你的大腦都快進入 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 :型別,有 string,boolean,enum 等,經常用到的也就 string 和 boolean
-
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
結語
模板看著很簡單,不過真正要寫出適合自己的模板,還是要花點時間的
但是在掌握它之後,你就會對它愛不釋手了