当前位置: 移动技术网 > IT编程>开发语言>Java > Android Studio自定义模板之MVPActivity

Android Studio自定义模板之MVPActivity

2020年07月23日  | 移动技术网IT编程  | 我要评论

前言

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,这里就不一一截图了
HomeMvpActivity
以上内容均为自动生成,使用过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 >

template.xml

template.xml

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.ftlauthor值;java import com.xx.等导包)。如图
IContract.java
IContract.java.ftl
template.xml
globals.xml.ftl
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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网