热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

android自定义模板下载,androidstudio自定义模板

由于项目用上了mvp架构,基本上一个页面就至少需要新创建6个类,分别是modelviewpresenter的接口以及其对应的实现类,再加上使用dagger的话就要更多了,所以这时候

由于项目用上了 mvp 架构,基本上一个页面就至少需要新创建6个类,分别是 model view presenter 的接口以及其对应的实现类,再加上使用 dagger 的话就要更多了,所以这时候 android studio 的自定义模板就派上用场了,可以节省很多编写模板代码的重复性工作

那么该如何入手呢?相信大部分用过 as 的人以及使用过一些自带的模板样式了,这些自带的模板就是最好的参照目标了,废话不多说,先看看它的结构

1.模板结构

这里参照的是 empty activity

ce5f144c5248

Empty Activity

它的位置就在 as的安装目录(mac的话右键as应用-> 显示包内容 -> content 里就是了)/plugins/android/lib/templates/activities,

ce5f144c5248

模板的结构

这里简单做个总结:

template:主要是给生成页面提供一些需要用户传入的参数

global.xml.ftl:主要提供一些全局参数

recipe.xml.ftl:主要用于生成实际需要的代码,资源文件等

root文件夹:包含 project 中一系列属性文件的模板

root 底下还有一些相关文件介绍

build.gradle.ftl:project 的 build.gradle 模板,如果需要添加 maven 库的地址,就在这里添加

gradle.properties.ftl:project 的 gradle.properties 的模板,如果需要添加工程的一些公用属性(版本号\版本名\签名信息\私有 maven 库的 group 和 id 信息等)就在这里面修改

local.properties.ftl:project 的 local.properties.ftl 模板,里面指定 SDK的路径,如果设置好环境变量,创建工程的时候就动态生成指定的路径,不需要手动修改

project_ignore:project 的.gitingore 模板,里面可以增删版本管理需要过滤的文件夹\文件

settings.gradle.ftl:project 的 settings.gradle 模板,里面可以指定真个工程需要编译的 module,这个建议不要修改,可以在工程中手动修改

1.1首先是 template.xml 文件,打开后的主要内容如下

format="5"

revision="5"

name="Empty Activity"

minApi="9"

minBuildApi="14"

description="Creates a new empty activity">

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" />

template_blank_activity.png

其中

1.的 name 属性,对应新建 Activity 时显示的名字

2.对应 New 的类别为 Activity

ce5f144c5248

页面和属于对照

现在来详解 parameter标签 的属性

id:唯一表示,最终通过这个属性来获取输入值(分为input 和 checkbox)

name:相当于 hint 了

type:属性的类型,分为 String 和 Boolean

constraints:填写值的约束

suggest:建议值

default:默认值

visibility:是否显示(一般就是根据其他类型为 checkbox 的 parameter 来确定了),例如上图的 layoutname,只有 generateLayout 为 true 时才显示

ce5f144c5248

generateLayout 为 false 时不显示 Layout Name

ce5f144c5248

generateLayout 为 true 时显示 Layout Name

help:鼠标悬浮在该 parameter 时显示的帮助提示

ce5f144c5248

help 属性的效果

然后是 thumbs 标签,也没啥,就是个缩略图罢了

ce5f144c5248

thumbs

最后还有两个标签,引用了外部文件,也是下面要讲的内容

1.2 globals.xml.ftl

里面定义的是一些全局变量,方便其他文件可以引用这里的值,引用的方式是&{id的值}

最后可以看到还引用了另外一个 ftl,这也说明了这个文件里定义的属性同时也可以被其他模板引用

1.3 recipe.xml.ftl

#if>

to="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />

跳过两个 include 引入的 ftl,先介绍能看到的标签

open:在代码生成后,打开指定的文件,这里写了两个 open,所以创建了一个 activity 后,就会把 activity 的 java 文件和layout.xml 同时打开

instantiate:就是把模板转换成实际目标文件的一个操作了,from 指定的是模板文件,to 指定的是生成文件,后面再详细介绍

然后可以看到前面还 include 了两个 ftl,实际上代表的就是 menifest 和 layout 的相关操作,下面是 recipe_manifest.xml.ftl 的内容

to="${escapeXmlAttribute(manifestOut)}/AndroidManifest.xml" />

to="${escapeXmlAttribute(resOut)}/values/strings.xml" />

这里又看到一个新的标签merge,字面意义就是合并,也就是把模板文件合并到项目中已经存在的对应文件中,这里是合并了 AndroidManifest.xml 和 string.xml

recipe中还有一个比较常见的标签,这个模板里没看到

copy :从root中copy文件到我们的目标目录,比如我们的模板Activity需要使用一些图标,那么可能就需要使用copy标签将这些图标拷贝到我们的项目对应文件夹。

2.代码生成的过程

模板里的文件基本都是 ftl 结尾的, 这里首先需要要解释一下 ftl 的概念

ftl是FreeMarker Template Language的缩写,它是简单的,专用的语言, 不是 像PHP那样成熟的编程语言。 那就意味着要准备数据在真实编程语言中来显示,比如数据库查询和业务运算, 之后模板显示已经准备好的数据。在模板中,你可以专注于如何展现数据, 而在模板之外可以专注于要展示什么数据。

而AS中的这些模板是就是通过这个FreeMarker模板引擎创建的

FreeMarker 是一款 模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。

ce5f144c5248

代码生成的简单示意图

3.简单的 freemarker 语法

1.插入属性

定义好一个属性,在模板文件中使用${定义好的属性名称},即可完成替换

2.if 语法

例如前面在 recipe.xml.ftl 里看到的, 这个generateLayout 是再 template 中定义的 boolean 的 parameter

#if>

下面以 Empty Activity模板中的 SimpleActivity.java.ftl 为例子

package ${packageName}; import ${superClassFqcn}; import android.os.Bundle; import android.widget.TextView; #if> public class ${activityClass} extends ${superClass} { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.${layoutName}); #if> } }

可以看到模板中有很多变量需要替换后才能生成为最终需要的代码,这些变量一般来自 globals.xml.ftl 中预先定义好的变量以及 template 中需要用户输入的变量,经过 recipe.xml.ftl 中的instantiate标签指定生成的路径即可完成这个过程

4.具体示例

最近的项目用到 mvp 和 dagger(这里就不细谈 dagger 的用法了),所以每个页面要多写很多接口以及实现类,下面是项目的分包:

ce5f144c5248

包结构

contract:定义了一个页面的 presenter 和 view 的接口,放在contract 里是为了方便查看

di.component.presenter:用于往目标类注入 presenter

di.module:提供presenter所依赖的组件

model.event:model的接口

model.impl:model 的实现类

presenter:contract 的 presenter 实现类

4.1 模板代码分析

下面分析一下,编写一个具体业务要生成哪些模板代码,例如要做一个登录的业务

4.1.1 mvp 的部分

首先要定义的是Contract, 包含整个业务逻辑与页面显示

public interface LoginContract {

// Contract 中肯定是要包括 View 和 Presenter 的

interface View {

// 具体的方法,这部分不是模板能解决的

void showLoginSuccess(UserInfo info);

void showLoginFailed(Throwable e);

}

interface Presenter {

// 具体的方法,这部分不是模板能解决的

void loginIn(String account, String password);

}

}

** View 的实现,一般就是让 activity 或者 fragment 实现 LoginContract.View 了,这部分不是模板能解决的,就不写了**

然后是 Presenter 的实现,当然实现 LoginContract.Presenter 即可

//项目有Presenter 的基类的话模板里还需要添加继承

public class LoginPresenter extends BasePresenter implement LoginContract.Presenter {

// 接口定义的方法,也不是模板能解决的

public void loginIn(String account, String password) {

// 具体的实现代码

}

}

4.1.2 Presenter的部分

这里presenter 的具体实现实际上也是包含很多重复代码的

首先,Presenter 里肯定需要持有 上面定义的Contract.View 的引用,这样才能再逻辑处理结束后回调 View 层代码

然后,Presenter 也需要持有 Model层的引用去处理数据,一般 Model 层也是需要定义接口的,所以又多了两个类:LoginModel 和 LoginModelImpl

public interface LoginModel {

void login(String account, String password);

}

public class LoginModel extends BaseModel implement LoginModel {

public void login(String account, String password) {

// 具体代码实现

}

}

修改后的 Presenter 代码为

public class LoginPresenter extends BasePresenter implement LoginContract.Presenter {

private LoginContract.View mView;

private LoginModel mModel;

public LoginPresenter(LoginContract.View view, LoginModel model) {

mView = view;

mModel = model;

}

// 接口定义的方法,也不是模板能解决的

public void loginIn(String account, String password) { // 具体的实现代码 }

}

由于一般的业务都是要通过请求或者本地数据库来处理的,所以这里抽取父类 BaseModel,项目里使用了 GreenDao 和 Retrofit,所以 BaseModel依赖于DaoMaster.DevOpenHelper和 Retrofit 两个对象

public class BaseModel {

protected final Retrofit retrofit;

protected final DaoMaster.DevOpenHelper dbOpenHelper;

public BaseModel(DaoMaster.DevOpenHelper helper, Retrofit retrofit) {

this.dbOpenHelper = helper;

this.retrofit = retrofit;

}

}

修改后的 LoginModelImpl的代码为

public class LoginModelImpl extends BaseModel implement LoginModel {

public LoginModelImpl(DaoMaster.DevOpenHelper helper, Retrofit retrofit) {

super(helper, retrofit);

}

public void login(String account, String password) {

// 具体代码实现

}

}

到这里登录业务的 P层和 M 层代码基本就写完了,一共需要 LoginContract/ LoginPresenter/LoginModel/LoginModelImpl四个文件

4.1.3 dagger 的部分

首先这里说一下 dagger 的好处,简单来说,dagger 就是将目标类与其依赖的对象的实例化过程隔离开来,例如这里的 LoginPresenter,一般在 activity 或者 fragment 中实例化

public class LoginActivity extends Activity implement LoginContract.View {

private LoginPresenter mPresenter;

public void onCreate(Bundle saveInstanceState) {

super.onCrate(saveInstanceState);

// 省略DaoMaster.DevOpenHelper 和 Retrofit 的实例化

....

mPresenter = new LoginPresenter(this, new LoginModelImpl(helper, retrofit));

}

void showLoginSuccess(UserInfo info){...}

void showLoginFailed(Throwable e){...}

}

实际上写这种 new 的代码是很 low 的,万一 LoginPresenter 的构造函数被修改了,就需要修改 LoginActivity 的代码,如果这个 LoginPresenter 到处都是的话,那就悲催了...

所以dagger就是为了解决这个问题而存在的,dagger 是一种依赖注入, 此处 LoginActivity 依赖于 LoginPresenter, dagger 可以把 LoginPresenter 的实例化放在一个独立的模块中去执行,而 LoginActivity 不必关心也不知晓 Presnter 的实例化过程,这样上面的问题就迎刃而解了.至于 dagger 的用法这里就忽略了

接下来讲使用 dagger 所需要创建的类

mPresenter = new LoginPresenter(this, new LoginModelImpl(helper, retrofit));

从这句代码就可以看出 LoginPresenter依赖于两个对象,一个是 View 接口,另一个是 LoginModel 接口,修改 LogingPresenter:

public class LoginPresenter extends BasePresenter implement LoginContract.Presenter {

private LoginContract.View mView;

private LoginModel mModel;

@Inject

public LoginPresenter(LoginContract.View view, LoginModel model) {

mView = view;

mModel = model;

}

// 接口定义的方法,也不是模板能解决的

public void loginIn(String account, String password) { // 具体的实现代码 }

}

这里给 LoginPresenter 的构造函数添加 @Inject 注解,这样 dagger 就能判断这是一个可用依赖注入实例化的目标

接下来,LoginPresenter 又有进一步的依赖,由于传入的参数都是接口,是不可能用 @Inject 标注在构造函数的了,所以这里又需要 dagger 中的Module提供实现类的对象,本着 m 层和 v 层分离的原则,这里就需要两个 Module

@Module

public class LoginViewModule {

LoginContract.View view;

public LoginViewModule(LoginContract.View view) {

this.view = view;

}

@Provide

public LoginContract.View provideLoginView() {

return view;

}

}

@Module

public class LoginModelModule {

Context context;

public LoginModelModule(Context context) {

this.context = context;

}

@Provide

public LoginModel provideLoginModel() {

// 省略DaoMaster.DevOpenHelper 和 Retrofit 的实例化

....

return new LoginModelImpl(helper, retrofit);

}

}

最后就是 Component 注入器了

@Component(dependencies = {LoginModelModule.class, LoginViewModule.class})

public interface LoginPresenterComponent {

void inject(LoginActivity activity);

}

到这里 dagger 部分的代码也就完了,下面开始编写自定义模板,这里列举一下所有需要的模板代码

Contract 类

package ${packageName}.contract;

public interface ${businessName}Contract {

interface View {}

interface Presenter{}

}

Presneter 实现类

package ${modulePackageName}.presenter;

import ${modulePackageName}.contract.${businessName}Contract;

import ${modulePackageName}.model.event.${businessName}Model;

import ${parentPresenterPackage}.${basePresenterClassName};

import javax.inject.Inject;

public class ${businessName}Presenter extends ${basePresenterClassName} implements ${businessName}Contract.Presenter {

private ${businessName}Contract.View view;

private ${businessName}Model model;

@Inject

public ${businessName}Presenter(${businessName}Contract.View view, ${businessName}Model model) {

this.view = view;

this.model = model;

}

}

Model 接口

package ${packageName}.model.event;

public interface ${businessName}Model {}

Model 实现类

package ${packageName}.model.impl;

import ${packageName}.model.event.${businessName}Model;

import ${daoPackage}.DaoMaster;

import ${projectPackage}.model.impl.${baseModelClassName};

import retrofit2.Retrofit;

public class ${businessName}ModelImpl extends ${baseModelClassName} implements ${businessName}Model {

public ${businessName}ModelImpl(DaoMaster.DevOpenHelper dataBaseHelper, Retrofit retrofit) {

super(dataBaseHelper, retrofit);

}

}

Model 接口 的 Module

package ${packageName}.di.module.model;

import android.content.Context;

import ${daoPackage}.DaoMaster;

import ${packageName}.model.event.${businessName}Model;

import ${packageName}.model.impl.${businessName}ModelImpl;

import retrofit2.Retrofit;

import dagger.Module;

import dagger.Provides;

@Module

public class ${businessName}ModelModule {

Context context;

public ${businessName}ModelModule(Context context) {

this.context = context;

}

@Provides

public ${businessName}Model provide${businessName}Model() {

// 省略DaoMaster.DevOpenHelper 和 Retrofit 的实例化

....

return new ${businessName}ModelImpl(helper, retrofit);

}

}

View层接口的 Module

package ${packageName}.di.module.view;

import ${packageName}.contract.${businessName}Contract;

import dagger.Module;

import dagger.Provides;

@Module

public class ${businessName}ViewModule {

${businessName}Contract.View view;

public ${businessName}ViewModule(${businessName}Contract.View view) {

this.view = view;

}

@Provide

public ${businessName}Contract.View provide${businessName}View() {

return view;

}

}

Presenter实例的注入器

package ${packageName}.di.component.presenter;

import ${packageName}.di.module.view.${businessName}ViewModule;

import @{packageName}.di.module.model.${businessName}ModelModule;

import dagger.Component;

@Component(dependencies = {${businessName}ViewModule.class, ${businessName}ModelModule.class})

public interface ${businessName}PresenterComponent {

void inject(t target);

}

模板代码准备好之后就可以开始制作模板了

首先复制整个 Empty Activity模板(推荐复制过来再修改的方式)

由于这个模板不涉及 activity 和manifest.xml 以及 layout,所以先删掉相关的标签

先从template.xml开始,删掉没用的 parameter,留下一个 packageName,然后添加一个业务名称,有这两个就够了

接着是设置 globals,设置一个 srcOut

最后是配置 recipe.xml.ftl, 根据自己想要的包修改一下路径即可, 只是简单的复制工作而已了

to="${escapeXmlAttribute(srcOut)}/contract/${businessName}Contract.java" />

to="${escapeXmlAttribute(srcOut)}/model/event/${businessName}Model.java" />

to="${escapeXmlAttribute(srcOut)}/model/impl/${businessName}ModelImpl.java" />

to="${escapeXmlAttribute(srcOut)}/di/module/model/${businessName}ModelModule.java" />

to="${escapeXmlAttribute(srcOut)}/di/module/view/${businessName}ViewModule.java"/>

to="${escapeXmlAttribute(srcOut)}/presenter/${businessName}Presenter.java"/>

to="${escapeXmlAttribute(srcOut)}/di/component/presenter/${businessName}PresenterComponent.java"/>

5.遇到的一些坑

1.模板一旦有错,as 跑起来就跪了,窗口关不掉只能强行关闭 as 再开过

2.前面提到要留下这个 packageName 本来想做成固定路径的,但不是报错就是路径不对.另外不指定这个 id 就没办法弄到当前的路径和包,不知道是为啥

3.这个是网上搜索的时候看到的,貌似自定义的模板会造成as 升级失败,如果遇到,把这份模板剪切出来,升级结束后再复制回去即可



推荐阅读
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • vue使用
    关键词: ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
author-avatar
手机用户2502929967
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有