本文詳細介紹模板相關的知識和如何製做Android模版及使用,便於較少沒必要要的重複性工做。好比我在工做中若是要建立一個新的模塊,就不要須要建立MVP相關的幾個類:Model、View、Presenter、Entity等。html
本文專門介紹和模板
相關的知識,那麼問題來了:java
模板是什麼android
模板使用位置api
模板如何建立(包含模板存放位置)bash
模板如何使用架構
接下來,我就按照以上順序爲你們解讀看起來高大上的模板
。app
本文全部模板路徑均爲Mac下的路徑,Windows用戶也能夠查看路徑中的相關信息,進而快速定位。ide
我的理解:模板即爲了幫助人們簡化某些固定而繁瑣的操做而製做的工具,用於快速實現這些固定而繁瑣的操做。工具
當咱們在使用AndroidStudio進行開發的時候,將鼠標選中工程項目,而後右擊能夠在New選項下面看到不少AndroidStudio提供給咱們的模板類別,例如:Activity、AIDL等。具體可看下圖:佈局
細心的你會發如今這些模板的上面有一個選項:Edit File Templates...
,以下圖所示:
點擊這個選項,會進入自定義模板頁面,其中內置的變量在頁面下方都有解釋,是否是很方便,可是它有一個致命的缺點:一次只能建立一個java文件。具體可看下圖:
由於以爲這個很簡單,因此我就不作過多闡述了,接下來我就仔細闡述一下,如何一次建立多個java文件,並且還能夠選擇是否包含xml文件。
若是直接複製相關代碼的話,請注意其中的註釋,可能會帶來一些問題,若是出現問題,能夠把#開頭的註釋去除,再嘗試!!!
若是不懂上面這段話的意思的話,能夠先行跳過。
AndroidStudio的模板是使用FreeMarker模板引擎製做的,有興趣的能夠了解一下。
案例:
因爲如今的項目使用的是類MVP架構
,因此基本上每一個模塊都須要entity、request、activity、presenter、viewmodel這五個類,不管是登陸註冊模塊,仍是商品詳情頁、首頁、收益頁面等模塊,都沒法擺脫這幾個類,所以準備爲這個類MVP架構
製做一個通用模板。
解答:
製做好模板以後,我想說,其實很簡單,只是把會變化的部分用${...}
替換罷了,不過在這裏咱們仍是老老實實的從頭開始吧!
首頁咱們進入AndroidStudio安裝目錄下的/plugins/android/lib/templates
文件夾,這就是AndroidStudio模板文件的目錄了,到這裏你可能還有所迷惑,由於你沒有發現像我剛開始所說的Activity、AIDL等模板文件,不要緊,你再進入activities
文件夾下面就能夠看到Activity的相關模板了,進入other
文件夾就能夠看到AIDL的相關模板了。
這裏咱們選擇activities
文件夾,而後你是否是以爲手足無措,不知道如何下手?其實一開始我也不知道怎麼作,可是不要緊,AndroidStudio不是已經提供給咱們這麼多模板了麼,爲了簡單起見,咱們在這裏拷貝一份EmptyActivity
,並將其重命名爲MVPActivity
,放在當前目錄下。
打開文件夾後,咱們看到如下目錄結構:
EmptyActivity |----globals.xml.ftl # 全局變量文件 |----recipe.xml.ftl # 配置要引用的模板路徑以及生成文件的路徑 |----root |----src |----app_package |----SimpleActivity.java.ftl # 模板文件 |----template_blank_activity.png # 建立模板時界面左邊的預覽圖 |----template.xml # 模板的配置信息以及要輸入的參數
接下咱們能夠根據目錄結構順序(建議按如下順序看),打開看一下,這裏大體介紹一下:
globals.xml.ftl
中都是相似
<global id="hasNoActionBar" type="boolean" value="false" />
這樣的語句,顯然它的意思就是我定義了一個全局變量hasNoActionBar,它的類型是boolean,默認值爲false。
recipe.xml.ftl
稍微有些複雜,這裏講解如下instantiate、open等幾個重要參數:
copy:複製--將from中的文件複製到to路徑下,但並不會將ftl中得變量進行轉換,即若是源文件中的類名爲${activityClass},複製事後類名仍是${activityClass}轉換爲咱們須要的類名。
merge:合併--將from中的文件合併到to路徑下的文件中。
instantiate:和copy相似,也是將from中的文件複製到to路徑下,可是它會將${activityClass}轉換爲咱們須要的類名。其實有這樣一個過程:ftl->freemarker process -> java。
open:代碼生成後,打開file中指定的文件。
打開SimpleActivity.java.ftl
文件,會發現和咱們建立Activity類後及其相似,只是把包名、類名、佈局名等用${...}
替換了,其實${...}
中得內容都是id名,這裏不作過多闡述,咱們繼續往下看。
template.xml
:打開之後你會發現這個文件好長,看來是重頭戲了!!!是的,咱們來詳細解讀一下:
一眼看去是否是和AndroidManifest.xml中得Application節點中的內容結構很類似(包括Application節點)
<?xml version="1.0"?> <template format="5" # The template format version that this template adheres to. Should be 3 revision="5" # 可選,當你想更新模板的時候能夠以整數的形式增長此模板的版本號 name="Empty Activity" # 模板顯示的名字 minApi="7" # 可選,模板所需的最小API值,IDE將確保在實例化模板以前,目標工程的minSdkVersion不低於這個值 minBuildApi="14" # 可選,模板所需的最小編譯API,值爲API級別,IDE將確保在實例化模板以前,項目工程的API等級大於或等於這個值 description="Creates a new empty activity"> # 模板的描述信息 <category value="Activity" /> # 模板類型,用於在菜單欄File-New下顯示,如Activity、AIDL等 <formfactor value="Mobile" /> # 如同咱們在建立module時所顯示的類型,如:Wear、TV等。 <parameter id="activityClass" # 惟一標示,在ftl文件中能夠用${activityClass}獲取參數值 name="Activity Name" # 建立模板時在文本框左邊顯示的該文本框名稱 type="string" # 這個參數的類型,如:string, boolean, enum等 constraints="class|unique|nonempty" # 可選,這個參數的約束類型,可用|符號聯合使用,constraints值類型大全請看4.5 suggest="${layoutToActivity(layoutName)}" # 可選,自動提示,好比輸入layout的值能夠自動生成activityClass default="MainActivity" # 可選,參數默認值,建立模板時在文本框中顯示,至關於hint help="The name of the activity class to create" /> # 建立模板時,選中文本框後,在底部顯示的關於該文本框的幫助信息 <!-- 128x128 thumbnails relative to template.xml --> <thumbs> <!-- default thumbnail is required --> <thumb>template_blank_activity.png</thumb> # 可選,用於建立模板時,在左邊顯示名爲template_blank_activity的預覽圖片 </thumbs> <globals file="globals.xml.ftl" /> # 可選,將工程定義的全局變量包含進來 <execute file="recipe.xml.ftl" /> # 開始執行模板渲染 </template>
Valid constraint types are: nonempty — the value must not be empty apilevel — the value should represent a numeric API level package — the value should represent a valid Java package name class — the value should represent a valid Java class name activity — the value should represent a fully-qualified activity class name layout — the value should represent a valid layout resource name drawable — the value should represent a valid drawable resource name string — the value should represent a valid string resource name id — the value should represent a valid id resource name unique — the value must be unique; this constraint only makes sense when other constraints are specified, such as layout, which would mean that the value should not represent an existing layout resource name exists — the value must already exist; this constraint only makes sense when other constraints are specified, such as layout, which would mean that the value should represent an existing layout resource name
到這裏相信你們對template.xml文件有了必定的瞭解了,好了,讓咱們來大幹一場吧!
既然這裏詳細的講解了template.xml文件,咱們先從template.xml文件入手吧,這裏我就不一個個細說了,直接上完整代碼:
<?xml version="1.0"?> <template format="2" # 可修改,此處已修改 revision="2" # 可修改,此處已修改 name="MVP Activity" # 須要修改 minApi="7" # 可修改 minBuildApi="14" # 可修改 description="Creates a new MVP activity"> # 須要修改 <category value="AAShowJoyMVP" /> # 可修改,此處已修改 <formfactor value="Mobile" /> # 通常不修改 <parameter # Activity類 id="activityClass" # 可修改 name="Activity Name" # 可修改 type="string" # 通常不修改 constraints="class|unique|nonempty" # 通常不修改 default="TestActivity" # 可修改,此處已修改 help="The name of the activity class to create" /> # 可修改,此處未修改 <parameter # Activity類的佈局文件 id="layoutName" name="Layout Name" type="string" constraints="layout|unique|nonempty" suggest="${classToResource(activityClass)}_activity" # 可修改,此處已修改,若不明白能夠跳過,以後會有詳解!!! default="test_activity" help="The name of the layout to create for the activity" /> <parameter # 是否做爲啓動Activity id="isLauncher" name="Launcher Activity" type="boolean" default="false" # 默認非啓動Activity 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.showjoy.shop" /> <parameter # viewModel類 id="viewModelClass" name="View Model Name" type="string" constraints="class|nonempty|unique" default="TestViewModel" suggest="${underscoreToCamelCase(classToResource(activityClass))}ViewModel" # 此類同佈局文件,以後會有詳解!!! help="The name of the ViewModel to create" /> <parameter # presenter類 id="presenterClass" name="Presenter Name" type="string" constraints="class|nonempty|unique" default="TestPresenter" suggest="${underscoreToCamelCase(classToResource(activityClass))}Presenter" help="The name of the Presenter to create" /> <parameter # request類 id="requestClass" name="Request Name" type="string" constraints="class|nonempty|unique" default="TestRequest" suggest="${underscoreToCamelCase(classToResource(activityClass))}Request" help="The name of the Request to create" /> <parameter # entity類 id="entityClass" name="Entity Name" type="string" constraints="class|nonempty|unique" default="TestEntity" suggest="${underscoreToCamelCase(classToResource(activityClass))}Entity" help="The name of the Entity to create" /> <globals file="globals.xml.ftl" /> # 通常不修改 <execute file="recipe.xml.ftl" /> # 通常不修改 </template>
template.xml文件的使用到這裏就結束了,仍是比較簡單的,如下闡述以前所留下的兩個問題:
(1)
suggest="${classToResource(activityClass)}_activity"
classToResource(activityClass):這句話的意思是,當咱們在建立該模板後,在activityClass對應的文本框中輸入某個值,好比:test,它會直接在layoutName對應的文本框中顯示,即:test,因此以完整的語句(1)而言,此時layoutName對應的文本框中顯示的應該是test_activity。
(2)
suggest="${underscoreToCamelCase(classToResource(activityClass))}ViewModel"
classToResource(activityClass)在(1)中描述的已經很清楚了,即爲test,那麼underscoreToCamelCase又是什麼意思呢?其實就是將test轉換爲駝峯命名的方法,即Test。因此以完整的語句(2)而言,此時viewModelClass對應的文本框中顯示的應該是TestViewModel。
若是你以爲文字描述過於繁瑣,仍然看不懂的話,能夠查看如下gif:
接下來咱們就能夠把要製做成模板的類,拷貝到相應的文件夾中,此時的目錄結構爲:
MVPActivity |----globals.xml.filter |----recipe.xml.ftl |----activity_layout_recipe.xml.filter # 此文件與recipe相似,只是由於解耦思想,因此將class和layout分別引入 |----root |----src |----app_package |----classes |----Activity.java.ftl |----Entity.java.ftl |----Presenter.java.ftl |----Request.java.ftl |----ViewModel.java.ftl |----layout |----activity_layout.xml.ftl |----template.xml
爲了方便而又全面的進行講解,此處咱們以Request.java.ftl文件爲例,這裏我就直接上所有代碼了:
package ${packageName}.request; # ${packageName}對應的是template.xml文件中id爲packageName的參數設置的字符串,若是該類不在包名根目錄下,能夠在後面添加相應的module名。 import android.support.annotation.NonNull; # 若是包名中未涉及到在建立模板時設置的包名和類名,則無需修改 import ${packageName}.entities.${entityClass}; # 若是包名中涉及到在建立模板時設置的包名和類名,則只需相對應的進行修改便可 /** * 將如下涉及到在建立模板時設置的包名和類名,進行以下相對應的替換便可,佈局文件也是這樣替換的!!! */ public class ${requestClass} extends SHGetRequest<${entityClass}> { @Override protected Class<${entityClass}> getDataClass() { return ${entityClass}.class; } @Override protected TypeReference<${entityClass}> getDataTypeReference() { return null; } @NonNull @Override protected String getRequestUrl() { return null; } }
接下來咱們來看一下佈局文件的替換:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout 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" tools:context="${relativePackage}.${activityClass}"> </RelativeLayout>
雖說tools命名空間通常都是無關緊要的,這裏爲了全面,也講述如下,你應該發現了一個從未見過的id:relativePackage,不用迷惑,估計你也想到了,其實我就是在globals.xml.ftl文件中定義了一個全局變量而已,它的值默認爲包名,具體代碼以下:
<global id="relativePackage" type="string" value="${packageName}"/>
既然說到了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" /> <global id="relativePackage" type="string" value="${packageName}"/> <#include "../common/common_globals.xml.ftl" /> </globals>
其實並無什麼,global表明的都是全局變量,#include表明的是引用的文件,至關於繼承。
而後就只有recipe.xml.ftl文件了,也快結束了:
<?xml version="1.0"?> <recipe> <#include "../common/recipe_manifest.xml.ftl" /> # 引入同級目錄中的activity_layout_recipe.xml.ftl文件,其內容會在下一節中講述 <#include "activity_layout_recipe.xml.ftl" /> <instantiate from="src/app_package/classes/Activity.java.ftl" to="${escapeXmlAttribute(srcOut)}/${activityClass}.java" /> <instantiate from="src/app_package/classes/ViewModel.java.ftl" to="${escapeXmlAttribute(srcOut)}/${viewModelClass}.java" /> <instantiate from="src/app_package/classes/Entity.java.ftl" to="${escapeXmlAttribute(srcOut)}/entities/${entityClass}.java" /> <instantiate from="src/app_package/classes/Presenter.java.ftl" to="${escapeXmlAttribute(srcOut)}/${presenterClass}.java" /> <instantiate from="src/app_package/classes/Request.java.ftl" to="${escapeXmlAttribute(srcOut)}/request/${requestClass}.java" /> <open file="${escapeXmlAttribute(srcOut)}/${viewModelClass}.java" /> <open file="${escapeXmlAttribute(resOut)}/layout/${layoutName}.xml" /> </recipe>
instantiate
的做用在上面已經講的很清楚了,簡單來講就是將ftl文件轉換爲java文件,而open
指的是在建立模板成功後,打開指定的文件,很簡單吧,這裏只有一個注意點:路徑不要寫錯了!!!
${escapeXmlAttribute(srcOut)}表明的即爲包名所表明的路徑 ${escapeXmlAttribute(resOut)}表明的是res根目錄
以前由於解耦思想,因此把佈局文件的recipe文件單獨處理了,即爲activity_layout_recipe.xml.ftl,打開文件,其實很簡單:
<recipe> <instantiate from="src/app_package/layout/activity_layout.xml.ftl" to="${escapeXmlAttribute(resOut)}/layout/${layoutName}.xml" /> </recipe>
這裏就不作闡述了,你們看上一節就明白了。
模板建立好以後,咱們首先須要的是驗證是否可以正確建立出咱們須要的部分,且沒有錯誤發生,這個過程其實就是模板使用的過程,具體能夠參考模板使用位置。
至此,Android模板製做已經所有完成了,本文篇幅仍是比較長的,若是有什麼疑問能夠評論,我會盡力解決每個問題的,謝謝!!!