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模板

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容