Android Studio自定义模板之MVPActivity
前言
Android开发中经常需要创建Activity。一般情况下,咱们都是"New"->Java Class/Activity。但是Android Studio自带的Activity模板都比较简单,未必符合我们所需的模板样式。例如在MVP框架下,需创建Activity、Present、Contract、Model等文件,并关联关系,初始化、author、统一的网络请求写法。所以我们需要自定义模板,帮我们省去这一系列重复操作。
举个栗子,创建自定义MVP模板(本文较长,赶时间的小伙伴可以到底部下载源码)
图解1 配置创建信息
MVP Name为红框标记部分统一命名(Activity、Layout、Contract、Model、Present)。由Generate MVP File选择框决定是否在生成activity的同时生成Contract、Model和Presenter等Java类。
图解2 文件结构
点击Finish完成创建后,可以看到自动生成的文件(如下图)
这里我们主要看自动生成的HomePageActivity。其他仨请看开头Gif,这里就不一一截图了
以上内容均为自动生成,使用过MVP的小伙伴是不是很熟悉?是不是很方便?妈妈再也不用担心我的CV大法导致代码区域乱七八糟了。
PS:每个项目MVP有或多或少差异,大家可以根据项目实际编写合适的模板。
附上Activity分区注释
/*-----------------------静态Activity启动方法区-------------------*/
/*-----------------------常量声明区-------------------------------*/
/*-----------------------UI控件成员变量声明区---------------------*/
/*-----------------------普通成员变量声明区-----------------------*/
/*-----------------------初始化相关方法区-------------------------*/
/*-----------------------生命周期回调方法区(除onCreate()方法外)- */
/*-----------------------事件响应方法区---------------------------*/
/*-----------------------重载的逻辑方法区-------------------------*/
/*-----------------------普通逻辑方法区---------------------------*/
/*-----------------------内部接口声明区---------------------------*/
/*-----------------------内部类声明区-----------------------------*/
如何自定义模板
1.熟悉安卓模板
在Android Studio的安装目录下 \plugins\android\lib\templates\activities
(同Mac)保存着系统自带的activity模板和我们自定义的模板
在编写自定义模板前,需要熟悉下模板的结构和组成,这里我们从最简单的模板EmptyActivity入手
src:代码文件,生成对应的文件模板
globals.xml.ftl:Java类库,主要用于提供参数。可存储全局变量以供其他模板文件统一引用
recipe.xml.ftl:用于组合生成我们实际需要的代码文件和布局文件等。
template.xml:相当于Android中的布局文件,用于图形化提供参数,布局等。
template_blank_activity.png:模板照片
自定义模板通常都是复制已有,然后再修改修改
globals.xml.ftl
存储全局变量。每一个变量定义,由id 唯一标识,type 类型,value 实际值组成
recipe.xml.ftl
即便不懂freemarker引擎也不影响,也能依样画葫芦,这里就简单讲下用到的标签及含义:
-
instantiate
将 ftl->java文件,中间会通过一个步骤,将ftl中的变量都换成对应的值,完整的流程是 ftl->freemarker process -> java 。 -
open
用于转换完成时在项目中对应的包下生成对应文件,例如Activity -
if
if指令,这里if判断是否生成layout文件- <#if >
<#elseif >
<#else >
</#if >
- <#if >
template.xml
对应用户输入的模板界面,<parameter>
获取用户输入参数
-
id
唯一标识,通过该属性,获取输入值,也可用于其他文件查找引用 -
name
标签名称,类似label展示给用户看 -
type
输入值类型 -
constraints
填写值的约束 -
suggest
建议值,例如填写Activity Name,给出一个建议值 -
help
底部提示语言,对应图形化界面操作
几个文件的整体的关系类似下图:
图片来源:http://www.slideshare.net/murphonic/custom-android-code-templates-15537501
2.定义MVPActivity模板
了解完以上简单介绍,基本足够我们写自(zhào)定(yàng)义(huà)模(hú)板(lū)
2.1创建MVPActivity文件夹
一般都是复制现有的(例如EmptyActivity,或者文章末尾的源码),再改改。
添加MVP所对应文件
2.2编写MVP相关文件
activity_mvp.xml.ftl
<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="${packageName}.${activityClass}">
</RelativeLayout>
IContract.java.ftl
package ${packageName};
import com.xx.mvp.base.BaseModel;
import com.xx.mvp.base.BasePresenter;
import com.xx.mvp.base.BaseView;
import rx.Observable;
<#assign aDateTime = .now>
/**
* Model: {@link ${modelName}} View:{@link ${activityClass}} Presenter:{@link ${presenterName}}
* @Author: ${author}
* @Description:
* @Date: Create in ${aDateTime}
* @Modified By:
*/
public interface ${contractName} {
interface Model extends BaseModel {
/**
* 更新数据
*
* @return
*/
Observable<Object> update();
}
interface View extends BaseView {
}
interface Presenter extends BasePresenter<Model, View> {
/**
* 更新数据
*/
void update();
}
}
Model.java.ftl
package ${packageName};
import com.xx.bean.base.BaseResult;
import com.xx.http.rx.TransformUtils;
import com.xx.mvp.base.BaseModelImpl;
import retrofit2.http.POST;
import rx.Observable;
<#assign aDateTime = .now>
/**
* Present: {@link ${presenterName}}
* @Author: ${author}
* @Description:
* @Date: Create in ${aDateTime}
* @Modified By:
*/
public class ${modelName} extends BaseModelImpl<${modelName}.Service> implements ${contractName}.Model {
public ${modelName}() {super(${modelName}.Service.class);}
@Override
public Observable<Object> update() {
return getRequestService()
.update()
.compose(TransformUtils.defaultSchedulers());
}
public interface Service {
/**
* 更新数据
*
* @return
*/
@POST("pos-web/updateAllData")
Observable<BaseResult<Object>> update();
}
}
MVPActivity.java.ftl
package ${packageName};
import ${superClassFqcn};
import android.os.Bundle;
<#if includeCppSupport!false>
import android.widget.TextView;
</#if>
<#assign aDateTime = .now>
/**
<#if generateMVP>
* Model: {@link ${modelName}} Presenter:{@link ${presenterName}}
</#if>
* @Author: ${author}
* @Description:
* @Date: Create in ${aDateTime}
* @Modified By:
*/
<#if generateMVP>
public class ${activityClass} extends BaseActivity<${contractName}.Model, ${contractName}.View, ${contractName}.Presenter> implements ${contractName}.View {
<#else>
public class ${activityClass} extends BaseActivity{
</#if>
<#include "../../../../common/jni_code_usage.java.ftl">
<#include "../../../../common/jni_code_snippet.java.ftl">
/*-----------------------静态Activity启动方法区-------------------*/
public static void startActivity(Activity activity) {
Intent intent = new Intent(activity, ${activityClass}.class);
activity.startActivity(intent);
}
/*-----------------------常量声明区-------------------------------*/
/*-----------------------UI控件成员变量声明区---------------------*/
/*-----------------------普通成员变量声明区-----------------------*/
/*-----------------------初始化相关方法区-------------------------*/
@Override
public int getContentView() {
return R.layout.${layoutName};
}
@Override
public void initView(Bundle savedInstanceState, View titleLayout) {
}
@Override
public void initData(Intent intent) {
getPresenter().update();
}
<#if generateMVP>
@Override
public ${contractName}.Model createModel() {
return new ${modelName}();
}
@Override
public ${contractName}.View createView() {
return this;
}
@Override
public ${contractName}.Presenter createPresenter() {
return new ${presenterName}();
}
<#else>
@Override
public BasePresenter createPresenter() {
return null;
}
</#if>
/*-----------------------生命周期回调方法区(除onCreate()方法外)-*/
/*-----------------------事件响应方法区---------------------------*/
/*-----------------------重载的逻辑方法区-------------------------*/
/*-----------------------普通逻辑方法区---------------------------*/
/*-----------------------内部接口声明区---------------------------*/
/*-----------------------内部类声明区-----------------------------*/
}
Presenter.java.ftl
package ${packageName};
import android.os.Bundle;
import com.xx.http.rx.BaseSubscribe;
import com.xx.mvp.base.BasePresenterImpl;
import rx.Subscription;
<#assign aDateTime = .now>
/**
* Model: {@link ${modelName}} View:{@link ${activityClass}}
* @Author: ${author}
* @Description:
* @Date: Create in ${aDateTime}
* @Modified By:
*/
class ${presenterName} extends BasePresenterImpl<${contractName}.Model, ${contractName}.View>
implements ${contractName}.Presenter {
@Override
public void onCreate(Bundle savedInstanceState) {
}
/**
* 更新数据
*/
@Override
public void update() {
Subscription subscribe = getModel().update()
.subscribe(new BaseSubscribe<Object>(this) {
@Override
protected void onSuccess(Object bean) {
}
});
addSubscribeRequest(subscribe);
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
2.3编写配置文件
template.xml 定义界面
<?xml version="1.0"?>
<template
format="5"
revision="5"
name="MVPActivity"
minApi="9"
minBuildApi="14"
description="Creates a new empty activity">
<category value="Activity" />
<formfactor value="Mobile" />
<parameter
id="instantAppActivityHost"
name="Instant App URL Host"
type="string"
suggest="${companyDomain}"
default="instantapp.example.com"
visibility="isInstantApp!false"
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"
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"
help="The route to use in the Instant App route for this activity"/>
<parameter
id="activityName"
name="MVP Name"
type="string"
constraints="nonempty"
default="MVP"
help="The name of the MVP class to create" />
<parameter
id="activityClass"
name="MVPActivity Name"
type="string"
constraints="class|unique|nonempty"
suggest="${activityName}Activity"
default="MainActivity"
help="The name of the activity class to create" />
<parameter
id="generateLayout"
name="Generate Layout File"
type="boolean"
default="true"
help="If true, a layout file will be generated" />
<parameter
id="layoutName"
name="Layout Name"
type="string"
constraints="layout|unique|nonempty"
suggest="${activityToLayout(activityName)}"
default="activity_main"
visibility="generateLayout"
help="The name of the layout to create for the activity" />
<parameter
id="generateMVP"
name="Generate MVP File"
type="boolean"
default="true"
help="If true, a mvp file will be generated" />
<parameter
id="contractName"
name="MVP Contract"
type="string"
suggest="${activityName}Contract"
help="The name of the contract to create for the activity" />
<parameter
id="modelName"
name="MVP Model"
type="string"
suggest="${activityName}Model"
help="The name of the model to create for the activity" />
<parameter
id="presenterName"
name="MVP Presenter"
type="string"
suggest="${activityName}Presenter"
help="The name of the presenter 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" />
<!-- 128x128 thumbnails relative to template.xml -->
<thumbs>
<!-- default thumbnail is required -->
<thumb>template_blank_activity.png</thumb>
</thumbs>
<globals file="globals.xml.ftl" />
<execute file="recipe.xml.ftl" />
</template>
recipe.xml.ftl
注意其中$变量,引用temlplate.xml中各控件的变量id,方便创建的文件名字已命名好。
<?xml version="1.0"?>
<#import "root://activities/common/kotlin_macros.ftl" as kt>
<recipe>
<#include "../common/recipe_manifest.xml.ftl" />
<@kt.addAllKotlinDependencies />
<#if generateLayout>
<instantiate from="root/res/layout/activity_mvp.xml.ftl"
to="${escapeXmlAttribute(resOut)}/layout/${layoutName}.xml"/>
</#if>
<#if generateMVP>
<instantiate from="root/src/app_package/IContract.java.ftl"
to="${escapeXmlAttribute(srcOut)}/${contractName}.java" />
<open file="${escapeXmlAttribute(srcOut)}/${contractName}.java" />
<instantiate from="root/src/app_package/Model.java.ftl"
to="${escapeXmlAttribute(srcOut)}/${modelName}.java" />
<open file="${escapeXmlAttribute(srcOut)}/${modelName}.java" />
<instantiate from="root/src/app_package/Presenter.java.ftl"
to="${escapeXmlAttribute(srcOut)}/${presenterName}.java" />
<open file="${escapeXmlAttribute(srcOut)}/${presenterName}.java" />
</#if>
<#if generateKotlin>
<instantiate from="root/src/app_package/MVPActivity.kt.ftl"
to="${escapeXmlAttribute(srcOut)}/${activityClass}.kt" />
<open file="${escapeXmlAttribute(srcOut)}/${activityClass}.kt" />
<#else>
<instantiate from="root/src/app_package/MVPActivity.java.ftl"
to="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />
<open file="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />
</#if>
</recipe>
template.xml
想要让id activityName统一命名Activity、Present、Contract、Model、layout,需要通过suggest属性统一引用它的id
<?xml version="1.0"?>
<template
format="5"
revision="5"
name="MVPActivity"
minApi="9"
minBuildApi="14"
description="Creates a new empty activity">
<category value="Activity" />
<formfactor value="Mobile" />
<parameter
id="instantAppActivityHost"
name="Instant App URL Host"
type="string"
suggest="${companyDomain}"
default="instantapp.example.com"
visibility="isInstantApp!false"
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"
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"
help="The route to use in the Instant App route for this activity"/>
<parameter
id="activityName"
name="MVP Name"
type="string"
constraints="nonempty"
default="MVP"
help="The name of the MVP class to create" />
<parameter
id="activityClass"
name="MVPActivity Name"
type="string"
constraints="class|unique|nonempty"
suggest="${activityName}Activity"
default="MainActivity"
help="The name of the activity class to create" />
<parameter
id="generateLayout"
name="Generate Layout File"
type="boolean"
default="true"
help="If true, a layout file will be generated" />
<parameter
id="layoutName"
name="Layout Name"
type="string"
constraints="layout|unique|nonempty"
suggest="${activityToLayout(activityName)}"
default="activity_main"
visibility="generateLayout"
help="The name of the layout to create for the activity" />
<parameter
id="generateMVP"
name="Generate MVP File"
type="boolean"
default="true"
help="If true, a mvp file will be generated" />
<parameter
id="contractName"
name="MVP Contract"
type="string"
suggest="${activityName}Contract"
help="The name of the contract to create for the activity" />
<parameter
id="modelName"
name="MVP Model"
type="string"
suggest="${activityName}Model"
help="The name of the model to create for the activity" />
<parameter
id="presenterName"
name="MVP Presenter"
type="string"
suggest="${activityName}Presenter"
help="The name of the presenter 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" />
<!-- 128x128 thumbnails relative to template.xml -->
<thumbs>
<!-- default thumbnail is required -->
<thumb>template_blank_activity.png</thumb>
</thumbs>
<globals file="globals.xml.ftl" />
<execute file="recipe.xml.ftl" />
</template>
2.4小结,集(tì)成(huàn)为自己项目MVP
以上自定义MVP模板已经全部结束。最后需要把MVPActivity挪移到Android Studio的安装目录下 \plugins\android\lib\templates\activities
(同Mac),再重启Android Studio,大功告成!
1.java.ftl模板中使用变量。
例如自动生成的IContract.java文件中顶部含Header 信息,Present、Activity等信息(记得修改globals.xml.ftl
中author
值;java import com.xx.等导包)。如图
2.内联的顺序
内联的顺序在代码中是从上往下执行的,因此要想生成文件后焦点窗口定位在新的Activity内,则要把Activity的内联代码放在最下面,这样所有文件生成完毕后才会优先定位到Activity窗口
PS:若编码的时候出现语法错误,那么在Android studio中点击finish生成Activity的时候会直接报错,查看点击log可以看到详细的报错位置,自己再进行修改就可以了。
附上模板源码:MVP模板下载地址
参考链接
原文 https://blog.csdn.net/u013452460/article/details/106671562
本文地址:https://blog.csdn.net/u013452460/article/details/106671562
上一篇: mongodb数据库实验之增删查改
下一篇: 超详细讲解Java异常