走馬觀花- AS 自定義模板

公司有自己的一套 MVP 架構(gòu),每次創(chuàng)建新的 MVP ActivityMVP Fragment 時(shí),都需要寫相對(duì)應(yīng)的 PresenterContract,想到 AS 內(nèi)嵌的 Activity、Fragment、AIDL 等模板,便捷好用。就想自己將這個(gè) MVP 整成一個(gè)模板,這樣就能節(jié)省不少寫模板代碼的時(shí)間了。

FreeMarker Template Language(FTL)

FreeMarker 模板語言,是一臺(tái)基于模板和要改變的數(shù)據(jù),并用來生成輸出文本的(HTML 網(wǎng)頁、電子郵件、配置文件、源代碼等 ) 的通用的工具,意味著要準(zhǔn)備數(shù)據(jù)在真實(shí)編程語言中來顯示, 之后模板顯示已經(jīng)準(zhǔn)備好的數(shù)據(jù)。在模板中,你可以專注于如何展現(xiàn)數(shù)據(jù), 而在模板之外可以專注于要展示什么數(shù)據(jù)。

FTL 基本語法
  • 標(biāo)簽

FTL 標(biāo)簽與 HTML 標(biāo)簽有相似之處,但是不是從屬關(guān)系。FTL 標(biāo)簽都是以 # 開頭的。用戶自定義 FTL 標(biāo)簽需要使用 @ 符號(hào)替代 #。

<#include "xxx.tfl"/>
  • 注釋
<#--  這是注釋 -->
  • if 、 else 指令
<#if 變量名>
    ......
<#elseif 變量名>
    ......
<#else>
    ......
</#if>

例如:
<#if generateKotlin>
    <instantiate from="root/src/app_package/SimpleActivity.kt.ftl"
                   to="${escapeXmlAttribute(srcOut)}/${activityClass}.kt" />
    <open file="${escapeXmlAttribute(srcOut)}/${activityClass}.kt" />
<#else>
    <instantiate from="root/src/app_package/SimpleActivity.java.ftl"
                   to="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />
    <open file="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />
</#if>
  • 訪問變量值
${變量名}   例如:${packageName}
  • 引入其他文件
<#include "xxx.ftl"/> 
例如:
<#include "../common/common_globals.xml.ftl" />
  • list 變量
<#list variables as loopVariable>
    repeatThis
</#list>
例:
<#list fruits as fruit>
    <li>${fruit}
</#list>

模板格式

按照慣例,模板目錄結(jié)構(gòu)中由 FreeMarker 處理的任何文件都應(yīng)具有 .ftl 文件擴(kuò)展名。因此,如果你的一個(gè)源文件是 MyActivity.java ,并且它包含 FreeMarker 指令,那么它應(yīng)該被命名為 MyActivity.java.ftl

目錄結(jié)構(gòu)

模板是包含許多XML和FreeMarker文件的目錄。只有兩個(gè)必填文件是template.xml和recipe.xml.ftl。模板源文件(PNG文件,模板化Java和XML文件等)屬于root/子目錄。下面是模板的示例目錄結(jié)構(gòu):

image
  • root

存放我們的代碼模板文件和資源文件

  • globals.xml.ftl

定義全局變量

  • recipe.xml.ftl

配置需要應(yīng)用的模板路徑和生成的文件的路徑

  • template.xml

定義模板參數(shù)。

template.xml

每個(gè)模板目錄必須包含一個(gè) template.xml 文件。此 XML 文件包含有關(guān)模板的元數(shù)據(jù),包括 IDE 將作為用戶選項(xiàng)顯示的名稱,描述,類別和用戶可見參數(shù)。XML 文件還指示 recipe.xml.ftl 的名稱,以及 globals.xml 文件

下面是一個(gè) template.xml 文件示例:

<?xml version="1.0"?>
<template
    format="5"
    revision="5"
    name="MVP Activity"
    minApi="9"
    minBuildApi="14"
    description="Creates a new MVP activity">

    <category value="Activity" />
    <formfactor value="Mobile" /> # 如同我們?cè)趧?chuàng)建module時(shí)所顯示的類型,如:Wear、TV等。

    <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>
        <!-- default thumbnail is required -->
        # 可選,用于創(chuàng)建模板時(shí),在左邊顯示名為template_blank_activity的預(yù)覽圖片
        <thumb>template_blank_activity.png</thumb>
    </thumbs>

    # 可選,將工程定義的全局變量包含進(jìn)來
    <globals file="globals.xml.ftl" />
    # 開始執(zhí)行模板渲染
    <execute file="recipe.xml.ftl" />

</template>
image

以下是 template.xml 支持的標(biāo)簽列表:

  • format 此模板遵循的模板格式版本
  • revision 此模板的整數(shù)版本(您可以在更新模板時(shí)遞增),可選
  • name 模板名稱。在 AS 操作 File --> New --> Activity 可找到對(duì)應(yīng)的 Activity
  • description 模板的描述。見圖
  • minApi

模板所需的最小 API 值,IDE 將確保在實(shí)例化模板之前,目標(biāo)工程的 minSdkVersion 不低于這個(gè)值,可選

  • minBuildApi

此模板所需的最小構(gòu)建目標(biāo) API。在實(shí)例化模板之前,IDE 將確保目標(biāo)項(xiàng)目的目標(biāo)是大于或等于此值的 API 級(jí)別。這可確保模板可以安全地使用較新的 API( 可選擇由運(yùn)行時(shí) API 級(jí)別檢查保護(hù) ,而不會(huì)將編譯時(shí)錯(cuò)誤引入目標(biāo)項(xiàng)目,可選

  • <dependency>

表示模板要求目標(biāo)項(xiàng)目中存在給定庫。如果不存在,IDE 將向項(xiàng)目添加依賴項(xiàng)。

name : 庫的名稱。目前接受的值有:
    1、android-support-v4
    2、android-support-v13

revision : 此模板所需的庫的最低版本。
例如:
<dependency name =“android-support-v4”revision =“8”/>
  • <category>

模板類型。此元素是可選的

value : 模板類型。應(yīng)該是以下值之一:
    1、Applications
    2、Activities
    3、UI Components
例 :
<category value =“Activities”/>
  • <parameter>

用戶可自定義的模板參數(shù)

id : 表示此變量的標(biāo)識(shí)符在 FreeMarker 文件中作為全局變量提供。如果標(biāo)識(shí)符是 foo,則參數(shù)值將在 FreeMarker 文件中通過 ${foo} 可得到
name : 模板參數(shù)的顯示名稱。假設(shè) <category value = "Activities"  />,則在 AS 中通過 File  --> New --> Activity 可找到對(duì)應(yīng)的 Activity
type : 參數(shù)的數(shù)據(jù)類型。要么string,boolean,enum,或 separator
help : 操作 <parameter/> 時(shí),底部顯示的提示語
default : 參數(shù)的默認(rèn)值
suggest : 建議值
visibility : 根據(jù)其他 View 的 ${id} 定義該 View 是應(yīng)該可見還是消失
constraints : 屬性值約束
android:inputType : text|textEmailAddress|number|textPassword

type

定義了實(shí)際的視圖屬性,分別有EditText、SpinnerCheckBox 類型

string : 表示對(duì)應(yīng)的實(shí)際視圖是一個(gè)EditText
enum : 表示對(duì)應(yīng)的實(shí)際視圖是一個(gè)Spinner
boolean : 表示對(duì)應(yīng)的實(shí)際視圖是一個(gè) CheckBox

constraints

可選屬性。強(qiáng)加于參數(shù)值的約束??梢允褂媒M合約束 |。有效的約束類型有:

class : 該值應(yīng)表示有效的Java類名稱,例如(Activity、Fragment、Presenter、Model、Utility等類名)
nonempty : 該屬性字段不能為 null 或 empty。此約束僅在指定其他約束時(shí)才有意義,例如layout,這意味著該值不應(yīng)表示現(xiàn)有布局資源名稱
unique : 這個(gè)確保包中不會(huì)存在相同的名稱。(也就是說,假設(shè)項(xiàng)目中已經(jīng)存在 MainActivity,則通過顯示 Main2Activity 等簡(jiǎn)單建議,避免使用重復(fù)的 Activity 名稱)
apilevel : 數(shù)字化的 API 級(jí)別
package : 有效的 java 類名
layout : 有效的 layout 名稱
drawable : 有效的 drawable 名稱
string : 有效的 string 
id : 有效 id 資源名稱
exists : 值必須已經(jīng)存在; 此約束僅在指定其他約束時(shí)才有意義,例如layout,這意味著該值應(yīng)表示現(xiàn)有布局資源名稱

suggest

可選的。表示自動(dòng)建議參數(shù)值的 FreeMarker 表達(dá)式(“ 動(dòng)態(tài)默認(rèn)值 ”)。當(dāng)用戶修改其他參數(shù)值時(shí),如果此參數(shù)的值未從其默認(rèn)值更改,則該值將更改為此表達(dá)式的結(jié)果。這似乎是循環(huán)的,因?yàn)閰?shù)是可以與 suggest 相互對(duì)照的值,但這些表達(dá)式僅針對(duì)未編輯的值進(jìn)行更新,因此這種方法允許用戶編輯任一參數(shù)值,而另一個(gè)將自動(dòng)更新為合理的默認(rèn)值

屬性的內(nèi)置方法 :

1、suggest="${layoutToActivity(layoutName)}"  

    layoutName="activity_main"  -->  MainActivity
    layoutName="main"           -->  MainActivity

2、suggest="${activityToLayout(activityClass)}"

    activityClass=“MainActivity”    -->  activity_main
    activityClass=“Main”            -->  activity_main

3、suggest="${underscoreToCamelCase(classToResource(activityClass))}Adapter" //首字母是大寫

    activityClass=“MainActivity”    -->  MainAdapter
    activityClass=“Main”            -->  MainAdapter

4、suggest="item_${classToResource(activityClass)}"  //首字母變成了小寫

    activityClass=“MainActivity”    -->  item_main
    activityClass=“Main”            -->  item_main

classToResource(activityClass):這句話的意思是,當(dāng)我們?cè)趧?chuàng)建該模板后,在 activityClass 對(duì)應(yīng)的文本框中輸入某個(gè)值,比如:test,它會(huì)直接在 layoutName 對(duì)應(yīng)的文本框中顯示,即:test,所以以完整的語句 suggest="${classToResource(activityClass)}_activity" 而言,此時(shí) layoutName 對(duì)應(yīng)的文本框中顯示的應(yīng)該是 test_activity 。

  • <option>

類型的參數(shù)enum,表示值的選擇.

id : 如果選擇此選項(xiàng),則設(shè)置的參數(shù)值
minApi : 可選的。選擇此選項(xiàng)時(shí)所需的最低API級(jí)別。minSdkVersion在實(shí)例化模板之前,IDE將確保目標(biāo)項(xiàng)目的值不低于此值
[text] : 此元素的文本內(nèi)容表示選擇的顯示值

<parameter
        id="navType"
        name="Navigation Type"
        type="enum"
        default="none">
        <option id="none" default="true">None</option>
        <option id="tabs" minApi="11">Tabs</option>
        <option id="pager" minApi="11">Swipe Views</option>
        <option id="dropdown" minApi="11">Dropdown</option>
    </parameter>
  • <thumb>

表示模板的縮略圖。<thumb> 元素應(yīng)包含在 <thumbs> 元素內(nèi)。此元素的文本內(nèi)容表示縮略圖的路徑。如果此元素具有多個(gè)屬性,則它們將被視為參數(shù)值的選擇器。例如,如果有兩個(gè)縮略圖:

<thumbs>
    <thumb>template.png</thumb>
    <thumb navType="tabs">template_tabs.png</thumb>
</thumbs>

如果值 navType 模板參數(shù)是 tabsname 模板“預(yù)覽”縮略圖將顯示template_tabs.png ,否則顯示 template.png。

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>

這個(gè)文件用于定義一些全局變量。

  1. <global> 定義一個(gè)全局變量
  2. id : 變量名稱
  3. type : 變量類型
  4. value : 默認(rèn)值

訪問這些變量的方法:${變量id}。例如:

${hasNoActionBar};
recipe.xml.ftl

recipe.xml.ftl 包含從此模板生成代碼時(shí)應(yīng)執(zhí)行的各個(gè)指令。例如,您可以復(fù)制某些文件或目錄( copy 指令 ,可選地通過 FreeMarker 運(yùn)行源文件( instantiate 指令,并在生成代碼( open 指令 后要求 ADTEclipse 中打開文件。

注意: recipe.xml.ftl 的名稱可自定義,但必須在 template.xml 聲明。但按照慣例,最好稱之為 recipe.xml.ftl

注意:全局變量 globals.xml.ftl 可用于recipe.xml.ftl。

<?xml version="1.0"?>
<#import "root://activities/common/kotlin_macros.ftl" as kt>
<recipe>
    <#include "../common/recipe_manifest.xml.ftl" />
    <@kt.addAllKotlinDependencies />

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

<#if generateKotlin>
    <instantiate from="root/src/app_package/SimpleActivity.kt.ftl"
                   to="${escapeXmlAttribute(srcOut)}/${activityClass}.kt" />
    <open file="${escapeXmlAttribute(srcOut)}/${activityClass}.kt" />
<#else>
    <instantiate from="root/src/app_package/SimpleActivity.java.ftl"
                   to="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />
    <open file="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />
</#if>

</recipe>

該文件用于定義如何生成文件和代碼。

  • <copy>

用于從 root 文件夾復(fù)制文件到目標(biāo)文件

唯一必需的參數(shù)是 from 指定要在 root/ 目錄下復(fù)制的源文件的位置。如果需要,將自動(dòng)創(chuàng)建所有必需的祖先目錄。

默認(rèn)目標(biāo)位置是輸出目錄根目錄下的相同路徑(即目標(biāo)項(xiàng)目的位置)。如果提供了可選 to 參數(shù),則指定輸出目錄。請(qǐng)注意,如果 from 路徑以 ... 結(jié)尾 .ftl ,則會(huì)自動(dòng)刪除它。例如 <instantiate from="res/values/strings.xml.ftl" /> 是足夠的; 這將創(chuàng)建一個(gè)名為的文件 strings.xml,而不是 strings.xml.ftl

此參數(shù)以遞歸方式工作,因此如果 from 是目錄,則以遞歸方式復(fù)制該目錄。

  • <instantiate>

.ftl 文件轉(zhuǎn)化成為 .java.kt 文件

  • <merge>

用于合并文件,如將模板文件的 string.xml 合并到我們項(xiàng)目的 string.xml

  • <open>

在代碼生成后打開指定文件,例如:當(dāng)我們創(chuàng)建一個(gè) Activity 時(shí),AS 會(huì)自動(dòng)打開 Activity 以及布局文件

  • <#include>

導(dǎo)入另一個(gè) ftl 文件

額外模板功能

FreeMarker 幾個(gè)重要函數(shù) :

  • string activityToLayout(string)

作用:

此函數(shù)將類似 activity calss 的標(biāo)識(shí)符字符串 例如 FooActivity)轉(zhuǎn)換為對(duì)應(yīng)的資源標(biāo)識(shí)符字符串,例如 activity_foo。

參數(shù):

activityClass,活動(dòng)類名稱,例如FooActivity重新格式化。

  • string camelCaseToUnderscore(string)

作用 :

此函數(shù)將 camel-case 標(biāo)識(shí)符字符串例如 FooBar)轉(zhuǎn)換為其對(duì)應(yīng)的下劃線分隔標(biāo)識(shí)符字符串,例如 foo_bar。

參數(shù) :

camelStr,駝峰式字符串,例如 FooBar 轉(zhuǎn)換為下劃線分隔的字符串。

  • string escapeXmlAttribute(string)

作用 :

此函數(shù)用來轉(zhuǎn)義字符串,例如 Android's,它可以用作 XML 屬性值:Android&apos;s。特別是,它將轉(zhuǎn)義',"<。

參數(shù) :

str,要轉(zhuǎn)義的字符串。

  • string escapeXmlText(string)

作用 :

此函數(shù)用來轉(zhuǎn)義字符串,例如 A & B's 可以將其用作 XML 文本。這意味著它將轉(zhuǎn)義 <>,但不像 escapeXmlAttribute 它將不會(huì)轉(zhuǎn)義'"。在前面的示例中,它將轉(zhuǎn)義字符串為 A &amp; B\s。請(qǐng)注意,如果您想要使用 XML 文本作為 <string> 資源的值,您應(yīng)該考慮使用 escapeXmlString,因?yàn)樗鼒?zhí)行額外的所需的字符串資源轉(zhuǎn)義

參數(shù) :

str,轉(zhuǎn)義為正確XML文本的字符串。

  • string escapeXmlString(string)

作用 :

此函數(shù)用來轉(zhuǎn)義字符串,例如 A & B's ,它適合作為 XML 文本插入字符串資源文件中,例如 A &amp; B\s 。除了轉(zhuǎn)義 < 之類的 XML 字符外,它還執(zhí)行其他Android 特定的轉(zhuǎn)義,例如使用反斜杠轉(zhuǎn)義撇號(hào),等等

參數(shù) :

str,例如,Activity's Title 以轉(zhuǎn)義為適當(dāng)?shù)馁Y源 XML 值。

  • string extractLetters(string)

作用 :

此函數(shù)從字符串中提取所有字母,有效刪除任何標(biāo)點(diǎn)符號(hào)和空白字符。

參數(shù) :

str,從中提取字母的字符串。

  • string classToResource(string)

作用 :

此函數(shù)將 Android 類名稱 例如 FooActivityFooFragment)轉(zhuǎn)換為相應(yīng)的資源標(biāo)識(shí)符字符串,例如 foo ,刪除 activityfragment 后綴。目前刪除的后綴列在下面:

  • Activity
  • Fragment
  • Provider
  • Service

參數(shù) :

className,類名,例如 FooActivity 重新格式化為下劃線分隔的字符串,后綴已刪除。

  • string layoutToActivity(string)

作用 :

此函數(shù)將資源標(biāo)識(shí)符字符串 例如 activity_foo)轉(zhuǎn)換為對(duì)應(yīng)的 Java 類標(biāo)識(shí)符字符串,例如 FooActivity。

參數(shù) :

resourceName,資源名稱,例如 activity_foo 重新格式化。

  • string slashedPackageName(string)

作用 :

此函數(shù)將完整 Java 包名稱轉(zhuǎn)換為其對(duì)應(yīng)的目錄路徑。例如,如果給定的參數(shù)是 com.example.foo ,則返回值為 com/example/foo

參數(shù) :

packageName,要重新格式化的包名稱,例如 com.example.foo。

  • string underscoreToCamelCase(string)

作用 :

此函數(shù)將下劃線分隔的字符串 例如 foo_bar)轉(zhuǎn)換為其對(duì)應(yīng)的駝峰字符串,例如 FooBar。

參數(shù) :

underStr,下劃線分隔的字符串,例如 foo_bar 轉(zhuǎn)換為駝峰字符串。

工具元數(shù)據(jù)

創(chuàng)建活動(dòng)布局時(shí),請(qǐng)確保在布局的根視圖中包含活動(dòng)名稱作為 tools 命名空間的一部分,如以下示例所示

<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:text="@string/hello_world"
    android:padding="@dimen/padding_medium"
    tools:context=".${activityClass}" />

我們?cè)诓季种惺褂么藢傩詠砭S護(hù)到用于布局的活動(dòng)活動(dòng)的映射。是的,可以有多個(gè),但是此屬性顯示您要編輯布局的活動(dòng)上下文。例如,它將用于查找主題注冊(cè) 這是每個(gè)活動(dòng)而不是每個(gè)布局 ) ) 在清單文件中,我們將來會(huì)將其用于其他功能 - 例如預(yù)覽操作欄,這也需要我們知道活動(dòng)上下文。

具體參考

Android ADT Template Format Document

Android模板制作

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 學(xué)習(xí)編寫模板最好的方式呢,就是參考IDE中已經(jīng)提供的最簡(jiǎn)單的模板,那么在Android Studio中最簡(jiǎn)單的ac...
    開心的鑼鼓閱讀 2,957評(píng)論 0 18
  • Android Studio自定義模板 寫頁面竟然可以如此輕松 1、概述 上一篇文章,已經(jīng)初步對(duì)Android S...
    Art_Collector閱讀 1,515評(píng)論 0 5
  • FreeMarker的模板文件并不比HTML頁面復(fù)雜多少,FreeMarker模板文件主要由如下4個(gè)部分組成: 1...
    年輕小伙程序員閱讀 3,165評(píng)論 0 5
  • 陶罐里的白玫瑰終于還是枯敗了,邊緣是淡的黃褐色,開始呈現(xiàn)種像揉皺紙張的質(zhì)感。一直狂咳的喉嚨也是如此,說話的聲音,像...
    mo清夜無塵閱讀 603評(píng)論 0 5
  • 散落的容顏,一個(gè)人孤獨(dú),一個(gè)人悲傷,最后的唯美,是人生的在意,傷感了最后的風(fēng)景,溫柔的慈悲,傷感了太多,只是那個(gè)憔...
    酒分閱讀 381評(píng)論 1 4

友情鏈接更多精彩內(nèi)容