Android Studio上Template(模板)的開發進階篇

Android Studio上Template(模板)的開發基礎篇

引言

是上節基礎篇中講解了,如果通過AS第三方的插件進行創建模板。算是創建模板比較入門的一種方式,如果不知道的可以去看下。
實際上在Android開發中很多生成多個文件,比如:生成Activity的時候,想要生成layout、在AndroidManifest中注冊;再比如:MVP框架,新建一個Activity或Fragment,生成MVP相關的view、presenter、model層文件等等。這些需求用插件生成模板的方式就無法實現了,本篇我們就來講講如何隨心所欲的創建我們需要的模板。

進階

這就需要通過手動修改配置文件及模板文件,下面從以下幾個方面入手。

一、技術準備

前文中開篇就說過,AS中模板使用到的模板引擎是FreeMarker,那么就離不開對FreeMarker基礎語法的學習。
FreeMarker基礎語法
文檔中有詳細描述了各個指令和各個函數的功能,建議收藏以便查詢。

A36AAEB6.jpg

英語基礎不好的可以點擊下面指示,進行下載中文文檔:


中文文檔下載引導.png
二、常用模板語法講述
1.常用指令集
  • global 定義一個全局的變量,在你創建的模板空間中是全局可見的。
    格式:
<#global name=value>
或
<#global name1=value1 name2=value2 ... nameN=valueN>

其中,name是名稱,value是值。
實例:

在AS自帶的模板中是這樣的使用的
<global id="hasNoActionBar" type="boolean" value="false" />

這種方式也可以,增加幾個擴展屬性,更容易理解些。

  • if,else,elseif 條件指令,類似我們高級語言中的if else 語法。
    格式:
<#if condition>
  滿足條件執行此處
<#elseif condition2>
  ...
<#elseif condition3>
  ...
...
<#else>
  ...
</#if>

condition 條件表達式
例子:

<#if (activityClass?lower_case?ends_with("activity"))>
    <global id="mvpFileName" type="string" value="${activityClass?replace('Activity','')?cap_first}"/>
    <global id="newActivityName" type="string" value="${activityClass?cap_first}" />
<#else>
    <global id="mvpFileName" type="string" value="${activityClass?cap_first}"/>
    <global id="newActivityName" type="string" value="${activityClass?cap_first}Activity" />
</#if>
重點!重點!重點!

建議表達式用括號括起來
因為這里有個坑,比如:表達式“abc”?length > 0,如果不加括號,會報錯提示:表達式值是Bool型的,不是int型;也就是說只識別了“abc”?length部分,只有加了括號引擎才能將表達式整體識別。

  • import 引入外部定于的文件,類似Java中的包引入概念
    例子:
<#import "root://activities/common/kotlin_macros.ftl" as kt>
  • include 包含外部文件,類似Android xml中的<include>標簽
    例子:
<#include "../common/recipe_simple.xml.ftl" />
  • local 這個是定于局部變量的,它不是一個表達式。但它可以被寫作是字符串形式
    格式:
<#local name=value>

例子:

<#local "index"=1 >
2.常見內建函數
  • 字母順序索引

    • cap_first 首字符大寫
      用法:"abc"?cap_first,輸出結果:Abc
    • uncap_first 首字母小寫
      用法:"ABC"?uncap_first,輸出結果:aBC
    • capitalize 字符串中所有單詞的首字母大寫
      用法:"ABC"?capitalize,輸出結果:abc
    • upper_case
      字符串的大寫形式。比如 "GrEeN MoUsE" 將會是 "GREEN MOUSE".
    • 其他,參見文檔
  • 字符串內建函數
    類似【字母順序索引】,內建函數很多與Java中String中方法相似。

    • cap_first 首字符大寫
      用法:"abc"?cap_first,輸出結果:Abc
    • uncap_first 首字母小寫
      用法:"ABC"?uncap_first,輸出結果:aBC
    • capitalize 字符串中所有單詞的首字母大寫
      用法:"ABC"?capitalize,輸出結果:abc
    • upper_case
      字符串的大寫形式。比如 "GrEeN MoUsE" 將會是 "GREEN MOUSE".
    • contains 如果函數中的參數指定的子串出現在源字符串中, 那么返回true
      用法:"piceous"?contains("ice")

      這個內建函數從 FreeMarker 2.3.1 版本開始可用。 在2.3版本中是沒有的。

    • ends_with 返回是否這個字符串以參數中指定的子串結尾。
      用法:
       "ahead"?ends_with("head") 返回布爾值 true。"head"?ends_with("head") 也返回 true。
      
      • index_of 返回第一次字符串中出現子串時的索引位置。
        用法:"abcabc"?index_of("bc") 將會返回1

      • last_index_of 返回最后一次(最右邊)字符串中出現子串時的索引位置;
        用法:"abcabc"?last_index_of("ab")

      • length 返回字符串的長度;
        用法: "abcdef"?length將會返回6

      • lower_case 將字符串中字目小寫
        用法:"GrEeN MoUsE"?lower_case將會是"green mouse"

      • replace 在源字符串中,用另外一個字符串來替換原字符串中出現它的部分
        用法:"this is a car acarus"?replace("car", "bulldozer") 將輸出:this is a bulldozer abulldozerus

      • remove_beginning 和 remove_ending 分別從字符串的開頭和結尾移除參數中的子串,如果它不以參數 中的子串開頭, 那么就或者返回原字符串。
        用法:
        "abcdef"?remove_beginning("abc")
        "abcdef"?remove_ending("def")

        該內建函數從 FreeMarker 2.3.21 版本開始可用。

      • split 它被用來根據另外一個字符串的出現將原字符串分割成字符串序列
        用法:"someMOOtestMOOtext"?split("MOO")

      • starts_with 如果字符串以指定的子字符串開頭,那么返回true
        用法:"redirect"?starts_with("red")

      • 其他,參見api文檔

  • 數字內建函數

    • abs 給出數字的絕對值
      用法: x?abs

    該內建函數從 FreeMarker 2.3.20 版本開始存在。

    • round, floor, ceiling 使用確定的舍入法則,轉換一個數字到整數
      • round 四舍五入的方式
      • floor 返回數字的舍掉小數后的整數(向下取整)
      • ceiling 返回數字小數進位后的整數(向上取整)
    • lower_abc、upper_abc 將數字轉換成小寫或大寫字母
      • lower_abc 它轉換成小寫字母,將 1, 2, 3,等...,轉換為字符串 "a", "b", "c",等... 當到達 "z"時,那么會繼續轉換成如 "aa", "ab"
        用法:30?lower_abc 將輸出:ad
      • upper_abc 它轉換成大寫字母,同lower_abc

以上就是常用的內建函數,如需了解更多,可以查看官方文檔。


A44F72AD.png
三、實戰

經過上面無聊而啰嗦的文字描述,下面我們就一起來完善基礎篇中的例子,生成Activity,并生成layout,并在AndroidManifest.xml中進行Activity注冊。
還以基礎篇中提到的例子為基礎,進入模板目錄


image.png
  • root 目錄 是存放模板的,我們自己寫的模板都會放到該目錄下
  • globals.xml.ftl這個配置文件是用于定義全局變量的,一會我們會用到
  • recipe.xml.ftl 這個配置文件是指定生成的文件模板及新文件名稱和目錄
  • template.xml 這個是定義輸入參數的配置文件,
  • template_cover.png 從命名看是模板封面,沒錯,這個就是在AS中使用時的界面封面logo
1.打開template.xml增加輸入參數

打開template.xml文件,新增一個叫generateLayout的參數,并設置默認值為true

<!-- 新增加一個輸出參數,控制是否需要創建布局文件 -->
    <parameter
        id="generateLayout"
        name="Generate Layout File"
        type="boolean"
        default="true"
        help="If true, a layout file will be generated" />

會在界面中增加了輸入參數,如下圖:


image.png
2.修改recipe.xml.ftl文件

由于我們要生成layout文件,從recipe.xml.ftl內容看是不存在生成layout文件配置,因此我們要手動增加配置。

<?xml version="1.0"?>
<recipe>

    <instantiate from="root/src/app_package/MyTemplateActivity.java.ftl"
        to="${escapeXmlAttribute(srcOut)}/${activityName}.java" />

    <merge from="root/AndroidManifest.xml.ftl"
           to="${escapeXmlAttribute(manifestOut)}/AndroidManifest.xml" />

    
    <!-- 增加生成layout文件 generateLayout 是在template.xml定義的輸入參數-->
    <#if generateLayout>
        <instantiate from="root/res/layout/activity_template.xml.ftl"
               to="${escapeXmlAttribute(resOut)}/layout/${layoutName}.xml" />
    </#if>
</recipe>

其中activity_template.xml.ftl文件未生成layout的模板,存放目錄為:root/res/layout/

3.新增layout模板文件

activity_template.xml.ftl模板內容如下:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="${packageName}.${activityName}">

    <TextView
        android:id="@+id/username"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="24dp"
        android:layout_marginTop="96dp"
        android:layout_marginEnd="24dp"
        android:text="myTemplate"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>
    
</android.support.constraint.ConstraintLayout>

編寫到此處,就可以重啟AS嘗試下是否能成功布局文件了。下面是我在本機上運行生成的layout文件


image.png
4.在AndroidManifest.xml中注冊新建的Activity

同理,想在AndroidManifest.xml中注冊activity,我們就要先創建我們的AndroidManifest.xml模板,如下:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="${packageName}">
    <application>
        <activity android:name=".${activityName}"></activity>
    </application>
</manifest>

這里簡單注冊了一個<activity>,沒有給activity添加其他屬性,這里你可以自己添加自己的屬性,比如theme和screenOrientation等屬性。
當然別忘記了在recipe.xml.ftl中增加對AndroidManifest.xml的配置,如下:

<merge from="root/AndroidManifest.xml.ftl"
           to="${escapeXmlAttribute(manifestOut)}/AndroidManifest.xml" />

到此就能正常在AndroidManifest.xml中注冊我們生成的Activity了
同理重啟AS,使用模板,如下圖生成結果:


image.png

好了,到這里我們開發模板講解基本結束了。

總結

剛開始時可能比較生疏,要多練習,多摸索下。
另外AS自帶有插件模板(目錄:Android Studio\plugins\android\lib\templates),也可以拿來研究下,慢慢就能掌握很多技巧。

另附帶上本文的模板例子和自定義的一個MVp模板

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容