上篇文章介紹的方法只能創(chuàng)建類模板,不能創(chuàng)建xml文件等資源文件以及配置文件等,這篇文章來(lái)介紹創(chuàng)建可以一次創(chuàng)建多個(gè)文件多種類型文件的方法。
我們?cè)谑褂肁ndroidStudio創(chuàng)建Activity的時(shí)候一般java文件,xml文件,以及在androidmanifest.xml配置文件中配置已經(jīng)完成。
上篇文章講到AndroidStudio的文件模板放在安裝目錄下/plugins/Android/lib/templates 文件夾下面。
可以看到里面那些正是AndroidStudio默認(rèn)提供的Activity類型(我截圖里面多了幾個(gè),是我自己測(cè)試寫的)。
下面我們分析最簡(jiǎn)單的一個(gè)模板EmptyActivity,EmptyActivity文件夾下面包括:
Android Studio 使用的是freeMaker模板引擎,所以文件夾下面的文件后綴名都是.ftl。
- globals.xml.ftl 全局變量文件 存放的是一些全局變量
- recipe.xml.ftl 配置要引用的模板路徑以及生成文件的路徑
- template.xml 模板的配置信息,以及要輸入的參數(shù).定義了模板的流程框架 基本結(jié)構(gòu)
- template_blank_activity.png 顯示的縮略圖(只是展示用)
- SimpleActivity.java.ftl Activity模板文件
globals.xml.ftl 定義了一些全局變量 以及將 common_globals.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>
common_globals.xml.ftl
<globals>
<#assign theme=getApplicationTheme()!{ "name": "AppTheme", "isAppCompat": true }>
<#assign themeName=theme.name!'AppTheme'>
<#assign themeNameNoActionBar=theme.nameNoActionBar!'AppTheme.NoActionBar'>
<#assign appCompat=theme.isAppCompat!false>
<#assign appCompatActivity=appCompat && (buildApi gte 22)>
<global id="themeName" type="string" value="${themeName}" />
<global id="implicitParentTheme" type="boolean" value="${(themeNameNoActionBar?starts_with(themeName+'.'))?string}" />
<global id="themeNameNoActionBar" type="string" value="${themeNameNoActionBar}" />
<global id="themeExistsNoActionBar" type="boolean" value="${(theme.existsNoActionBar!false)?string}" />
<global id="themeNameAppBarOverlay" type="string" value="${theme.nameAppBarOverlay!'AppTheme.AppBarOverlay'}" />
<global id="themeExistsAppBarOverlay" type="boolean" value="${(theme.existsAppBarOverlay!false)?string}" />
<global id="themeNamePopupOverlay" type="string" value="${theme.namePopupOverlay!'AppTheme.PopupOverlay'}" />
<global id="themeExistsPopupOverlay" type="boolean" value="${(theme.existsPopupOverlay!false)?string}" />
<global id="appCompat" type="boolean" value="${((isNewProject!false) || (theme.isAppCompat!false))?string}" />
<global id="appCompatActivity" type="boolean" value="${appCompatActivity?string}" />
<global id="hasAppBar" type="boolean" value="${appCompatActivity?string}" />
<global id="hasNoActionBar" type="boolean" value="${appCompatActivity?string}" />
<global id="manifestOut" value="${manifestDir}" />
<global id="buildVersion" value="${buildApi}" />
<#if !appCompat>
<global id="superClass" type="string" value="Activity"/>
<global id="superClassFqcn" type="string" value="android.app.Activity"/>
<global id="Support" value="" />
<global id="actionBarClassFqcn" type = "string" value="android.app.ActionBar" />
<#elseif appCompatActivity>
<global id="superClass" type="string" value="AppCompatActivity"/>
<global id="superClassFqcn" type="string" value="android.support.v7.app.AppCompatActivity"/>
<global id="Support" value="Support" />
<global id="actionBarClassFqcn" type = "string" value="android.support.v7.app.ActionBar" />
<#else>
<global id="superClass" type="string" value="ActionBarActivity"/>
<global id="superClassFqcn" type="string" value="android.support.v7.app.ActionBarActivity"/>
<global id="Support" value="Support" />
<global id="actionBarClassFqcn" type = "string" value="android.support.v7.app.ActionBar" />
</#if>
<global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
<global id="resOut" value="${resDir}" />
<global id="menuName" value="${classToResource(activityClass!'')}" />
<global id="simpleName" value="${activityToLayout(activityClass!'')}" />
<global id="relativePackage" value="<#if relativePackage?has_content>${relativePackage}<#else>${packageName}</#if>" />
</globals>
recipe.xml.ftl 、recipe_manifest.xml.ftl、recipe_simple.xml.ftl 配置要引用的模板路徑以及生成文件的路徑
<?xml version="1.0"?>
<recipe>
<#include "../common/recipe_manifest.xml.ftl" />
<#if generateLayout>
<#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>
//recipe_manifest.xml.ftl
<recipe folder="root://activities/common">
<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>
//recipe_simple.xml.ftl
<recipe folder="root://activities/common">
<#if appCompat && !(hasDependency('com.android.support:appcompat-v7'))>
<dependency mavenUrl="com.android.support:appcompat-v7:${buildApi}.+"/>
</#if>
<instantiate from="root/res/layout/simple.xml.ftl"
to="${escapeXmlAttribute(resOut)}/layout/${simpleLayoutName}.xml" />
<#if (isNewProject!false) && !(excludeMenu!false)>
<#include "recipe_simple_menu.xml.ftl" />
</#if>
<#include "recipe_simple_dimens.xml" />
</recipe>
這里<#if >是freeMaker的if判斷標(biāo)簽,對(duì)freeMaker我也不太熟悉,大家感興趣可以去看一下freeMaker語(yǔ)法。
<instantiate from="root/src/app_package/SimpleActivity.java.ftl"
to="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />
這句是根據(jù)root/src/app_package/SimpleActivity.java.ftlSimpleActivity.java.ftl模板文件生成escapeXmlAttribute(srcOut)目錄下對(duì)應(yīng)${activityClass}.java文件,escapeXmlAttribute(srcOut)這個(gè)是指你選擇的目錄即你點(diǎn)擊右鍵時(shí)的目錄。
<open file="${escapeXmlAttribute(srcOut)}/${activityClass}.java" /></recipe>
同樣這句的作用是生成后打開(kāi)這個(gè)文件。
<merge from="root/AndroidManifest.xml.ftl" to="${escapeXmlAttribute(manifestOut)}/AndroidManifest.xml" />
merge標(biāo)簽代表是將AndroidManifest.xml.ftl模板文件中的內(nèi)容添加到AndroidManifest.xml文件中,并不是覆蓋。
template.xml
<?xml version="1.0"?>
<template
format="5"
revision="5"
name="Empty Activity" 在向?qū)Ы缑嬷酗@示的模板名稱
minApi="7"
minBuildApi="14"
description="Creates a new empty activity"> 界面中顯示描述
<category value="Activity" /> 模板所屬類型 這個(gè)可以自定義
<formfactor value="Mobile" />
<parameter
id="activityClass" 唯一標(biāo)示,可以用${activityClass}獲取value
name="Activity Name" 界面輸入框前的提示文字
type="string" 類型 界面顯示為輸入框
constraints="class|unique|nonempty" 約束 class必須是類名 unique唯一 nonempty不能為空
suggest="${layoutToActivity(layoutName)}" 建議的名稱 默認(rèn)生成的名稱和布局文件名稱關(guān)聯(lián) 不太確定 汗
default="MainActivity" 默認(rèn)值
help="The name of the activity class to create" /> 界面提示文字
<parameter
id="generateLayout"
name="Generate Layout File"
type="boolean" 界面顯示為勾選框 type="enum" 在界面中為下拉選擇框
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="isLauncher"
name="Launcher Activity"
type="boolean"
default="false"
help="If true, this activity will have a CATEGORY_LAUNCHER intent filter, making it visible in the launcher" />
<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" />
根據(jù)模板生成對(duì)應(yīng)的文件
<execute file="recipe.xml.ftl" />
</template>
下面配一張圖
SimpleActivity.java.ftl Activity模板文件
package ${packageName};
import ${superClassFqcn};
import android.os.Bundle;
public class ${activityClass} extends ${superClass} {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
<#if generateLayout>
setContentView(R.layout.${layoutName});
</#if>
}
}
${activityClass} ${superClass}
其他文件中對(duì)變量的引用,都是前面定義的一些參數(shù),一般主要出自兩個(gè)地方,一個(gè)是globals.xml 全局變量文件,第二個(gè)是template.xml 界面中定義變量。
這種根據(jù)模板生成更類似于一種文件拷貝,不會(huì)檢查語(yǔ)法。
上面是理論基礎(chǔ)。下面我們來(lái)實(shí)戰(zhàn)一下,順便解決一下上篇文章的問(wèn)題如何生成多個(gè)文件,以及不同類型的文件。
實(shí)戰(zhàn)須知:
建議大家在修改之前,現(xiàn)將整個(gè)文件夾備份一下,免得改亂了。文件夾也不大。
globals.xml.ftl 全局變量文件 存放的是一些全局變量
<?xml version="1.0"?>
<globals>
<global id="resOut" value="${resDir}" />
<global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
<global id="collection_name" value="${extractLetters(itemClass?lower_case)}" />
</globals>
recipe.xml.ftl 配置要引用的模板路徑以及生成文件的路徑。
一次性生成多個(gè)文件,在于使用<instantiate>
標(biāo)簽,只要寫好引用模板文件和輸出文件路徑和文件名。
<?xml version="1.0"?>
<recipe>
<!-- 生成Adapter 文件-->
<instantiate from="src/app_package/CustomAdapter.java.ftl" to="${escapeXmlAttribute(srcOut)}/adapter/${itemClass}Adapter.java" />
<open file="${escapeXmlAttribute(srcOut)}/adapter/${itemClass}Adapter.java" />
<!-- 生成Fragment 文件-->
<instantiate from="src/app_package/Fragment.java.ftl" to="${escapeXmlAttribute(srcOut)}/fragment/${itemClass}Fragment.java" />
<open file="${escapeXmlAttribute(srcOut)}/fragment/${itemClass}Fragment.java" />
<!-- 生成Presenter 文件-->
<instantiate from="src/app_package/BasePresenter.java.ftl" to="${escapeXmlAttribute(srcOut)}/presenter/${itemClass}Presenter.java" />
<open file="${escapeXmlAttribute(srcOut)}/presenter/${itemClass}Presenter.java" />
<!-- 生成Contract 文件-->
<instantiate from="src/app_package/BaseContract.java.ftl" to="${escapeXmlAttribute(srcOut)}/presenter/${itemClass}Contract.java" />
<open file="${escapeXmlAttribute(srcOut)}/presenter/${itemClass}Contract.java" />
<!-- 生成InteractorImpl 文件-->
<instantiate from="src/app_package/BaseInteractor.java.ftl" to="${escapeXmlAttribute(srcOut)}/interactor/${itemClass}InteractorImpl.java" />
<open file="${escapeXmlAttribute(srcOut)}/interactor/${itemClass}Interactor.java" />
<!-- 生成model 文件-->
<instantiate from="src/app_package/BaseModel.java.ftl" to="${escapeXmlAttribute(srcOut)}/model/${itemClass}.java" />
<open file="${escapeXmlAttribute(srcOut)}/model/${itemClass}.java" />
<!-- 生成item布局 文件-->
<instantiate from="res/layout/item_simple.xml.ftl" to="${escapeXmlAttribute(resOut)}/layout/${layoutName}.xml" />
<!-- 生成fragment布局 文件-->
<instantiate from="res/layout/fragment_simple.xml.ftl" to="${escapeXmlAttribute(resOut)}/layout/${fragment_layoutName}.xml" />
</recipe>
template.xml 模板的配置信息,以及要輸入的參數(shù).定義了模板的流程框架 基本結(jié)構(gòu)
<?xml version="1.0"?>
<template
format="4"
revision="1"
name="ListFragmentAchieve"
description="Creates a new ListFragmentAchieve"
minApi="7"
minBuildApi="14">
<category value="ListFragmentAchieveName" />
<!-- <parameter
id="adapterClass"
name="Adapter Name"
type="string"
constraints="class|unique|nonempty"
default="CustomAdapter"
help="The name of the adapter class to create" /> -->
<parameter
id="itemClass"
name="Item Name"
type="string"
constraints="class|nonempty"
default="String"
help="The name of the item class to use" />
<parameter
id="layoutName"
name="item_layout"
type="string"
constraints="layout|nonempty"
default="item_layout"
help="The name of the row layout to use" />
<!-- fragment 類文件名稱 后來(lái)使用 itemClass 拼接完成了-->
<!-- <parameter
id="fragmentClass"
name="fragment Name"
type="string"
constraints="class|unique|nonempty"
default="Fragment"
help="The name of the fragment class to create" /> -->
<!-- fragment 布局文件名 -->
<parameter
id="fragment_layoutName"
name="fragment_layout"
type="string"
constraints="layout|nonempty"
default="fragment_layout"
help="The name of the row layout to use" />
<!-- 128x128 thumbnails relative to template.xml -->
<thumbs>
<!-- default thumbnail is required -->
<thumb>template_basic_activity_fragment.png</thumb>
</thumbs>
<globals file="globals.xml.ftl" />
<execute file="recipe.xml.ftl" />
</template>
剩下的模板資源文件就不一一列舉貼代碼了 ,這些文件大家可以根據(jù)自己的需求去自己定義。
.效果圖:
代碼上傳到了github:
https://github.com/zhaodaizheng/AndroidTemplates
這個(gè)demo自動(dòng)生成的文件,是根據(jù)我自己的框架生成的,大家本地生成后的文件肯定會(huì)出現(xiàn)錯(cuò)誤,大家可以根據(jù)自己封裝的框架進(jìn)行修改。
對(duì)FreeMarker不太了解 有錯(cuò)誤的地方歡迎反饋。