本文详细介绍模板相关的知识和如何制作Android模版及使用,便于较少不必要的重复性工作。比如我在工作中如果要创建一个新的模块,就不要需要创建MVP相关的几个类:Model、View、Presenter、Entity等。

本文专门介绍和模板相关的知识,那么问题来了:

  1. 模板是什么
  2. 模板使用位置
  3. 模板如何创建(包含模板存放位置)
  4. 模板如何使用

接下来,我就按照以上顺序为大家解读看起来高大上的模板

警告

本文所有模板路径均为Mac下的路径,Windows用户也可以查看路径中的相关信息,进而快速定位。

模板是什么

个人理解:模板即为了帮助人们简化某些固定而繁琐的操作而制作的工具,用于快速实现这些固定而繁琐的操作。

模板使用位置

当我们在使用AndroidStudio进行开发的时候,将鼠标选中工程项目,然后右击可以在New选项下面看到很多AndroidStudio提供给我们的模板类别,例如:Activity、AIDL等。具体可看下图:

细心的你会发现在这些模板的上面有一个选项:Edit File Templates...,如下图所示:

点击这个选项,会进入自定义模板页面,其中内置的变量在页面下方都有解释,是不是很方便,但是它有一个致命的缺点:一次只能创建一个java文件。具体可看下图:

因为觉得这个很简单,所以我就不做过多阐述了,接下来我就仔细阐述一下,如何一次创建多个java文件,而且还可以选择是否包含xml文件。

模板如何创建(包含模板存放位置)

警告

如果直接复制相关代码的话,请注意其中的注释,可能会带来一些问题,如果出现问题,可以把#开头的注释去除,再尝试!!!

如果不懂上面这段话的意思的话,可以先行跳过。

FreeMarker

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

globals.xml.ftl中都是类似

<global id="hasNoActionBar" type="boolean" value="false" />

这样的语句,显然它的意思就是我定义了一个全局变量hasNoActionBar,它的类型是boolean,默认值为false。

recipe.xml.ftl

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

打开SimpleActivity.java.ftl文件,会发现和我们创建Activity类后及其类似,只是把包名、类名、布局名等用${...}替换了,其实${...}中得内容都是id名,这里不做过多阐述,我们继续往下看。

template.xml

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>

constraints值类型大全

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文件有了一定的了解了,好了,让我们来大干一场吧!

MVP版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:

MVP版目录结构

接下来我们就可以把要制作成模板的类,拷贝到相应的文件夹中,此时的目录结构为:

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

为了方便而又全面的进行讲解,此处我们以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

既然说到了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

然后就只有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根目录

activity_layout_recipe.xml.ftl

之前因为解耦思想,所以把布局文件的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模板制作已经全部完成了,本文篇幅还是比较长的,如果有什么疑问可以评论,我会尽力解决每一个问题的,谢谢!!!

Android模板制作的更多相关文章

  1. phpwind9.0模板制作教程——制作论坛风格

    由于论坛模板机制和门户等模板机制不同,所以今天我就先重点讲讲论坛模板制作的大概过程. 一.先来熟悉下phpwind9.0的论坛模板机制. 其实phpwind9.0的模板机制和discuzx2.5差不多 ...

  2. Altium Designer 文档信息设置以及模板制作

    原理图文档模板制作方法一.在DXP原理图设计环境下,新建一个自由原理图文档.单击:文件→新建→原理图,或者使用快捷键Ctrl+N打开Files资源面板,在“新建”项目下的选择“Schematic Sh ...

  3. Android中制作自定义dialog对话框的实例

    http://www.jb51.net/article/83319.htm   这篇文章主要介绍了Android中制作自定义dialog对话框的实例分享,安卓自带的Dialog显然不够用,因而我们要继 ...

  4. magnetom模板制作

    我个人认为Magento模板制作的难点在于不了解Magento的架构,不会调动block.Magento的block调动几乎都是靠xml.在下面的内容会提及如何操作. 制作Magento模板的前提是: ...

  5. Xen虚拟机磁盘镜像模板制作(四)—CentOS 7

    在<Xen虚拟机磁盘镜像模板制作(三)—CentOS 7>一文中,我们已经成功制作出了 CentOS7 磁盘镜像.下面我们说明下如何通过它来生成目标虚拟机,同时测试下之前制作好的虚拟机磁盘 ...

  6. Xen虚拟机磁盘镜像模板制作(二)—Windows Server 2008(2012)

    在<Xen虚拟机磁盘镜像模板制作(一)—Windows Server 2008(2012)>一文中,我们已经成功制作出了Windows Server磁盘镜像.下面我们说明下如何通过它来生成 ...

  7. 【Ecmall】ECMall2.x模板制作入门系列(认识ECMall模板)

    ECMall2.x模板制作入门系列之1(认识ECMall模板) 从ECMall2.0全新架构发布以来,随着版本的不断更新,ECMall已经逐渐走向一个稳定时期,是时候整理一些实用教程了.下面给大家带来 ...

  8. Pencil-一个开源免费的UI原型工具,自带ios和android模板

    Pencil是一个开源免费的UI原型工具,自带ios和android模板,可以很方便的做mockup. 下图是一个官方展示的模板:         

  9. 虚拟机centos7 基础模板制作

    用于新模板制作,主要针对一些基本组件的安装 分区.安装不在此赘述 最小化安装centos7-minimal-1810 1.安装wget yum install wget -y 2.更换aliyun源 ...

随机推荐

  1. HDU1300 Pearls —— 斜率优化DP

    题目链接:https://vjudge.net/problem/HDU-1300 Pearls Time Limit: 2000/1000 MS (Java/Others)    Memory Lim ...

  2. 代码块、继承、this、super、final(java基础知识八)

    1.代码块的概述和分类 * A:代码块概述 * 在Java中,使用{}括起来的代码被称为代码块.* B:代码块分类 * 根据其位置和声明的不同,可以分为局部代码块,构造代码块,静态代码块,同步代码块( ...

  3. Com组件介绍

    COM组件简介 面向对象的思想难以适应这种分布式软件模型,于是组件化程序设计思想得到了迅速的发展. 按照组件化的程序设计的思想,复杂的应用程序被设计成一些小的,功能单一的组件模块,这些组件模块可以运行 ...

  4. MySQL_详细基本操作命令

    mysql 修改新密码:use mysql:update user set password='新密码' where user='用户名':flush privileges:  更新权限 增加新用户: ...

  5. hdu5829 Rikka with Subset

    首先考虑本题的$O(n^2)$做法. $Part1$ 对原序列从大到小排序后,考虑每个数字对最终答案的贡献,有第x个数字对答案的贡献十分难以计算,所以考虑计算数字x是集合第K大的方案数,作为数字x对$ ...

  6. Linux2.6 内核中结构体初始化(转载)

    转自:http://hnniyan123.blog.chinaunix.net/uid-29917301-id-4989879.html 在Linux2.6版本的内核中,我们经常可以看到下面的结构体的 ...

  7. [Vue 牛刀小试]:第十二章 - 使用 Vue Router 实现 Vue 中的前端路由控制

    一.前言 前端路由是什么?如果你之前从事的是后端的工作,或者虽然有接触前端,但是并没有使用到单页面应用的话,这个概念对你来说还是会很陌生的.那么,为什么会在单页面应用中存在这么一个概念,以及,前端路由 ...

  8. 洛谷 - P1355 - 神秘大三角 - 简单计算几何

    https://www.luogu.org/problemnew/show/P1355 判断一个点和三角形的位置关系,最简单的思路就是用向量. 首先排除掉和三角形顶点重合的情况. 把三角形设计成一个首 ...

  9. 洛谷 - P1309 - 瑞士轮 - 归并排序

    https://www.luogu.org/problemnew/show/P1309 一开始写的直接快排没想到真的TLE了. 想到每次比赛每个人前移的量不会很多,但是不知从哪里开始优化. 搜索一下原 ...

  10. jzoj5980. 【WC2019模拟12.27】字符串游戏

    首先发现双方可以有一个默契,不管谁刻意,都可以把串变为诸如\(...101010101...\)的形式 所以先手要赢的话就是要在上面的基础之上加一个字符使其变为要求的子串 那么就是要求的子串中相邻两个 ...