Android Template學習筆記

Android Studio的Template,能夠從複雜程度上分爲三種:Live Template,File Template和Android Studio工程模板,前兩種模板在Jetbrain家的其餘IDE中也可使用,能夠看作是依賴於IDE自己的功能,最後一個則須要複雜的模板代碼支持。php

Live Template

用幾個字母+tab展開成一段代碼,而後在須要的位置填上自定義內容。java

設置入口:Settings->Editor->Live Templatesandroid

右側點擊+號以後填寫下面的信息,代碼中的$name$表示這段代碼中能夠自定義的部分,在第一個$name$處輸入,全部的$name$佔位部分都會同時變化。第一處佔位符輸入完成按enter後會自動跳轉到第二處佔位符輸入下一個變量。 代碼和縮寫字母定義完畢後,必定要選左下角的生效語言範圍纔會生效,右下角是設置觸發的按鍵(通常是tabbash

File Template

以模板生成文件 設置入口:Settings->Editor->File Templatesapp

點擊 +建立新模板。編寫模板會使用一些佔位符語法,在建立文件時自動轉換成代碼。

預約義的變量

預約義的變量能夠直接在文件模板中使用:dom

  • ${PACKAGE_NAME}:新文件被建立的包名
  • ${NAME}:文件名
  • ${USER}:當前用戶系統登陸的用戶名
  • ${DATE}:當前系統日期
  • ${TIME}:當前系統時間
  • ${YEAR}:當前年份
  • ${MONTH}:當前月份
  • ${MONTH_NAME_SHORT}:月份前三個字母。Example: Jan, Feb, etc.
  • ${MONTH_NAME_FULL}:月份全稱。 Example: January, February, etc.
  • ${DAY}:current day of the month
  • ${DAY_NAME_SHORT}:first 3 letters of the current day name. Example: Mon, Tue, etc.
  • ${DAY_NAME_FULL}:full name of the current day. Example: Monday, Tuesday, etc.
  • ${HOUR}:當前小時
  • ${MINUTE}:當前分鐘
  • ${PROJECT_NAME}:project名

include其餘模板

使用#parse能夠在模板中包括進其餘模板的內容,例如通用的文件建立人和建立時間信息,能夠放在一個header文件中,在第二個Includestab中定義:ide

代碼以下

/**
 *
 * Created by ${USER} on ${DATE}.
 */
複製代碼

在使用時就能夠在其餘模板中使用#parse("Kt File Header.kt")引入這個header,生成文件時自動轉換成這樣的註釋代碼。佈局

自定義變量

除了以上預約義的變量,用${xxx}的格式自定義一些變量名,在建立文件時會同時提示填寫這些變量:ui

#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME}
#end

import androidx.room.Entity

#parse("Kt File Header.kt")
@Entity(tableName = "${tableName}s")
data class ${NAME}(var ${props}: String): BaseModel()
複製代碼

在這段代碼中自定義了tableName和props兩個變量,在新建文件時就會要求填寫: this

最後生成的代碼:

package info.zhufree.windwhite.bean

import androidx.room.Entity

/**
 *
 * Created by zhufree on 2019/4/26.
 */
@Entity(tableName = "tests")
data class TestModel(var title: String) : BaseModel()
複製代碼

ActivityTemplate

最後一種也是最複雜的模板,能夠直接生成多個配套文件,典型的例子就是新建Activity,會直接生成一個Activity文件+layout文件,若是是複雜的帶Fragment的,帶ViewModel的,則會自動生成Fragment以及ViewModel文件等:

除了Activity,也能夠直接建立Project等等:

這種模板不能直接在設置中定義,須要本身編寫,放在AndroidStudio的模板文件夾中,重啓Studio便可使用。編寫模板須要用到Free Marker語言。 文件夾路徑: MacOS:/Applications/Android Studio.app/Contents/plugins/android/lib Windows:[Android Studio安裝目錄]/plugins/android/lib/templates

模板文件夾中不一樣文件的做用:

template.xml

這個文件能夠看作定義Activity模板的清單文件,列出在建立Activity時須要填的全部變量,對應這個頁面:

<?xml version="1.0"?>
<template
    format="5"
    revision="5"
    name="Empty Activity"
    minApi="9"
    minBuildApi="14"
    description="Creates a new empty activity">
    <!--定義模板自己的一些屬性-->

	<!--模板類型-->
    <category value="Activity" />
    <!--適用於手機,相對的還有平板,TV,可穿戴設備等-->
    <formfactor value="Mobile" />

	<!--parameter標籤訂義須要填的參數-->
	<!--id 在後面引用時使用-->
	<!--name 解釋這個參數意義的名字-->
	<!--type 填的數據類型-->
	<!--constraints 填的內容限制(類名,獨一,不可爲空)-->
	<!--suggest 在填寫其餘參數時能夠根據其餘參數的變化自動填充(根據layoutName進行下劃線轉駝峯命名生成)-->
	<!--default 默認值-->
	<!--help 顯示在左下角的提示文字-->
    <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" />

	<!--boolean類型顯示的是一個勾選框+Name-->
    <parameter
        id="generateLayout"
        name="Generate Layout File"
        type="boolean"
        default="true"
        help="If true, a layout file will be generated" />

	<!--visibility 可見性,這裏意思是勾選了上面的generateLayout纔會顯示填寫佈局文件名這一行-->
    <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="backwardsCompatibility"
        name="Backwards Compatibility (AppCompat)"
        type="boolean"
        default="true"
        help="If false, this activity base class will be Activity instead of AppCompatActivity" />

    <parameter
        id="packageName"
        name="Package name"
        type="string"
        constraints="package"
        default="com.mycompany.myapp" />

    <parameter
        id="includeInstantAppUrl"
        name="Associate a URL with this Activity"
        type="boolean"
        default="false"
        visibility="isInstantApp!false"
        help="If true, this activity will be associated with URL, improving discovery of your Instant App" />

    <parameter
        id="instantAppActivityHost"
        name="Instant App URL Host"
        type="string"
        suggest="${companyDomain}"
        default="instantapp.example.com"
        visibility="isInstantApp!false"
        enabled="includeInstantAppUrl"
        help="The domain to use in the Instant App route for this activity"/>

    <parameter
        id="instantAppActivityRouteType"
        name="Instant App URL Route Type"
        type="enum"
        default="pathPattern"
        visibility="isInstantApp!false"
        enabled="includeInstantAppUrl"
        help="The type of route to use in the Instant App route for this activity" >
        <option id="path">Path</option>
        <option id="pathPrefix">Path Prefix</option>
        <option id="pathPattern">Path Pattern</option>
    </parameter>

    <parameter
        id="instantAppActivityRoute"
        name="Instant App URL Route"
        type="string"
        default="/.*"
        visibility="isInstantApp!false"
        enabled="includeInstantAppUrl"
        help="The route to use in the Instant App route for this activity"/>

    <!-- 128x128 thumbnails relative to template.xml -->
    <!-- 顯示在左邊的縮略圖 -->
    <thumbs>
        <!-- default thumbnail is required -->
        <thumb>template_blank_activity.png</thumb>
    </thumbs>

	<!-- 聲明一些全局定義的global變量 -->
    <globals file="globals.xml.ftl" />
    <!-- 執行recipe.xml.ftl文件來生成目標文件 -->
    <execute file="recipe.xml.ftl" />
</template>
複製代碼

在這個頁面用戶填寫的變量值,將和globals.xml.ftl中預約義的全局變量值一塊兒做爲可用的變量在後面編寫模板代碼的過程當中使用。

recipe.xml

recipe文件中定義以哪一個文件爲模板來生成哪一個文件:

<?xml version="1.0"?>
<#import "root://activities/common/kotlin_macros.ftl" as kt>
<recipe>
    <#include "../common/recipe_manifest.xml.ftl" />
    <@kt.addAllKotlinDependencies />
<!-- 根據generateLayout判斷是否要生成layout文件 -->
<#if generateLayout>
	<!-- 在recipe_simple中處理生成layout文件的邏輯,和下面的instantiate同樣 -->
    <#include "../common/recipe_simple.xml.ftl" />
    <!-- 文件建立完以後用open打開 -->
    <open file="${escapeXmlAttribute(resOut)}/layout/${layoutName}.xml" />
</#if>
	<!-- from 模板文件位置 to 目標文件位置 -->
    <instantiate from="root/src/app_package/SimpleActivity.${ktOrJavaExt}.ftl" to="${escapeXmlAttribute(srcOut)}/${activityClass}.${ktOrJavaExt}" />
    <open file="${escapeXmlAttribute(srcOut)}/${activityClass}.${ktOrJavaExt}" />
</recipe>
複製代碼

以上這些文件和png圖片都在模板文件夾的根目錄下,對於較複雜的模板,root文件夾下分爲srcres文件夾,以及清單文件模板AndroidManifest.xml.ftl等等,和真實的項目結構相似,src文件夾中app_package則表明包名路徑,以後是activity等類文件的模板,通常有.java.ftl.kt.ftl兩種後綴的,分別對應生成.java文件和.kt文件。 res文件夾中則可能會有layout/menu/values等文件夾,放置佈局,菜單,資源值等類型的模板文件。

Activity.ftl

src文件夾下的ActivityFragment等類的模板文件,同理可添加Adapter之類的模板。 以最簡單的Activity+kotlin語言爲例:

// 聲明包名
package ${escapeKotlinIdentifiers(packageName)}
// 導入一些必要的包,這裏的${superClassFqcn}在common_global.xml.ftl中定義,根據是不是appCompatActivity,是否useAndroidX導入了不一樣的Activity
import ${superClassFqcn}
import android.os.Bundle
// 判斷是否導入佈局控件
<#if (includeCppSupport!false) && generateLayout>
import kotlinx.android.synthetic.main.${layoutName}.*
</#if>

// 定義activity類名,繼承父類
class ${activityClass} : ${superClass}() {

	// 自動生成onCreate方法
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
		// 若是生成了layout文件,調用setContentView方法
<#if generateLayout>
        setContentView(R.layout.${layoutName})
        <#include "../../../../common/jni_code_usage.kt.ftl">
<#elseif includeCppSupport!false>

        // Example of a call to a native method
        android.util.Log.d("${activityClass}", stringFromJNI())
</#if>
    }
<#include "../../../../common/jni_code_snippet.kt.ftl">
}
複製代碼

superClassFqcn的定義(common_global.xml.ftl):

<#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" />
    <global id="kotlinActionBar" type="string" value="actionBar" />
    <global id="kotlinFragmentManager" type="string" value="fragmentManager" />
<#elseif appCompatActivity>
    <global id="superClass" type="string" value="AppCompatActivity"/>
    <global id="superClassFqcn" type="string" value="${getMaterialComponentName('android.support.v7.app.AppCompatActivity', useAndroidX)}"/>
    <global id="Support" value="Support" />
    <global id="actionBarClassFqcn" type = "string" value="${getMaterialComponentName('android.support.v7.app.ActionBar', useAndroidX)}" />
    <global id="kotlinActionBar" type="string" value="supportActionBar" />
    <global id="kotlinFragmentManager" type="string" value="supportFragmentManager" />
<#else>
    <global id="superClass" type="string" value="ActionBarActivity"/>
    <global id="superClassFqcn" type="string" value="${getMaterialComponentName('android.support.v7.app.ActionBarActivity', useAndroidX)}"/>
    <global id="Support" value="Support" />
    <global id="actionBarClassFqcn" type = "string" value="${getMaterialComponentName('android.support.v7.app.ActionBar', useAndroidX)}" />
    <global id="kotlinActionBar" type="string" value="supportActionBar" />
    <global id="kotlinFragmentManager" type="string" value="supportFragmentManager" />
</#if>
複製代碼

layout.xml.ftl

做爲模板的佈局文件在root/res/layout文件夾下,以一個最簡單的simple.xml.ftl爲例:

<?xml version="1.0" encoding="utf-8"?>
<!-- 判斷根佈局用哪一個版本的ConstraintLayout -->
<${getMaterialComponentName('android.support.constraint.ConstraintLayout', useAndroidX)}
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
<#if hasAppBar && appBarLayoutName??>
app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:showIn="@layout/${appBarLayoutName}"
</#if>
<!--若是有appbar,須要設置behavior-->
    tools:context="${packageName}.${activityClass}">
<!-- 添加一個示例的TextView -->
<#if isNewProject!false>
    <TextView
<#if includeCppSupport!false>
        android:id="@+id/sample_text"
</#if>
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</#if>
</${getMaterialComponentName('android.support.constraint.ConstraintLayout', useAndroidX)}>
複製代碼

整體來講,這一類模板結構可能比較複雜,但原理都是根據預約義的變量和自定義的變量,加上事先編寫好的模板代碼,經過不一樣的條件判斷,填充變量名等操做最終生成目標文件代碼。 編寫模板文件須要瞭解一些經常使用的預約義變量和free marker語法等,能夠參考上面的官方手冊。 使用ActivityProject層級的模板須要必定的時間成本,屬於磨刀不誤砍柴工,一旦完成可以節省不少複製粘貼再修改的工做,但須要平衡好模板化和自定義化的程度,編寫符合本身需求同時也具備足夠複用價值的模板代碼。

相關文章
相關標籤/搜索