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层级的模板须要必定的时间成本,属于磨刀不误砍柴工,一旦完成可以节省不少复制粘贴再修改的工做,但须要平衡好模板化和自定义化的程度,编写符合本身需求同时也具备足够复用价值的模板代码。

相关文章
相关标签/搜索