1. 程式人生 > >Android Studio 自定義模板

Android Studio 自定義模板

前言

在開發 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

結語

模板看著很簡單,不過真正要寫出適合自己的模板,還是要花點時間的

但是在掌握它之後,你就會對它愛不釋手了