欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Android Studio自定义模板之MVPActivity

程序员文章站 2022-06-25 09:39:25
前言Android开发中经常需要创建Activity。一般情况下,咱们都是右键"New"->Java Class/Activity。但是生成的Activity模板都比较简单,未必符合我们所需的模板样式。例如MVP框架下,创建完Activity,需要创建Present,Contract等文件,还需要编写Model、View、Present的关联等一系列代码。所以我们需要自定义模板,帮我们省去这一系列操作。接下来有人肯定要说,我直接copy一下以前的Activity、Present多好(手动狗头.j...

前言

Android开发中经常需要创建Activity。一般情况下,咱们都是"New"->Java Class/Activity。但是Android Studio自带的Activity模板都比较简单,未必符合我们所需的模板样式。例如在MVP框架下,需创建Activity、Present、Contract、Model等文件,并关联关系,初始化、author、统一的网络请求写法。所以我们需要自定义模板,帮我们省去这一系列重复操作。

举个栗子,创建自定义MVP模板(本文较长,赶时间的小伙伴可以到底部下载源码)
Android Studio自定义模板之MVPActivity

图解1 配置创建信息

MVP Name为红框标记部分统一命名(Activity、Layout、Contract、Model、Present)。由Generate MVP File选择框决定是否在生成activity的同时生成Contract、Model和Presenter等Java类。
Android Studio自定义模板之MVPActivity

图解2 文件结构

点击Finish完成创建后,可以看到自动生成的文件(如下图)
Android Studio自定义模板之MVPActivity
这里我们主要看自动生成的HomePageActivity。其他仨请看开头Gif,这里就不一一截图了
Android Studio自定义模板之MVPActivity
以上内容均为自动生成,使用过MVP的小伙伴是不是很熟悉?是不是很方便?妈妈再也不用担心我的CV大法导致代码区域乱七八糟了。

PS:每个项目MVP有或多或少差异,大家可以根据项目实际编写合适的模板。

附上Activity分区注释

    /*-----------------------静态Activity启动方法区-------------------*/
    /*-----------------------常量声明区-------------------------------*/
    /*-----------------------UI控件成员变量声明区---------------------*/
    /*-----------------------普通成员变量声明区-----------------------*/
    /*-----------------------初始化相关方法区-------------------------*/
    /*-----------------------生命周期回调方法区(除onCreate()方法外)- */
    /*-----------------------事件响应方法区---------------------------*/
    /*-----------------------重载的逻辑方法区-------------------------*/
    /*-----------------------普通逻辑方法区---------------------------*/
    /*-----------------------内部接口声明区---------------------------*/
    /*-----------------------内部类声明区-----------------------------*/

如何自定义模板

1.熟悉安卓模板

在Android Studio的安装目录下 \plugins\android\lib\templates\activities(同Mac)保存着系统自带的activity模板和我们自定义的模板

Android Studio自定义模板之MVPActivity
在编写自定义模板前,需要熟悉下模板的结构和组成,这里我们从最简单的模板EmptyActivity入手
Android Studio自定义模板之MVPActivity
src:代码文件,生成对应的文件模板
globals.xml.ftl:Java类库,主要用于提供参数。可存储全局变量以供其他模板文件统一引用
recipe.xml.ftl:用于组合生成我们实际需要的代码文件和布局文件等。
template.xml:相当于Android中的布局文件,用于图形化提供参数,布局等。
template_blank_activity.png:模板照片

自定义模板通常都是复制已有,然后再修改修改

globals.xml.ftl

Android Studio自定义模板之MVPActivity
存储全局变量。每一个变量定义,由id 唯一标识,type 类型,value 实际值组成

recipe.xml.ftl

Android Studio自定义模板之MVPActivity
即便不懂freemarker引擎也不影响,也能依样画葫芦,这里就简单讲下用到的标签及含义:

  • instantiate 将 ftl->java文件,中间会通过一个步骤,将ftl中的变量都换成对应的值,完整的流程是 ftl->freemarker process -> java 。
  • open 用于转换完成时在项目中对应的包下生成对应文件,例如Activity
  • if if指令,这里if判断是否生成layout文件
    • <#if >
      <#elseif >
      <#else >
      </#if >

template.xml

Android Studio自定义模板之MVPActivity

Android Studio自定义模板之MVPActivity
对应用户输入的模板界面,
<parameter>获取用户输入参数

  • id 唯一标识,通过该属性,获取输入值,也可用于其他文件查找引用
  • name 标签名称,类似label展示给用户看
  • type 输入值类型
  • constraints 填写值的约束
  • suggest 建议值,例如填写Activity Name,给出一个建议值
  • help 底部提示语言,对应图形化界面操作Android Studio自定义模板之MVPActivity

几个文件的整体的关系类似下图:

Android Studio自定义模板之MVPActivity

图片来源:http://www.slideshare.net/murphonic/custom-android-code-templates-15537501


2.定义MVPActivity模板

了解完以上简单介绍,基本足够我们写自(zhào)定(yàng)义(huà)模(hú)板(lū)

2.1创建MVPActivity文件夹

一般都是复制现有的(例如EmptyActivity,或者文章末尾的源码),再改改。
Android Studio自定义模板之MVPActivity
添加MVP所对应文件
Android Studio自定义模板之MVPActivity

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,方便创建的文件名字已命名好。
Android Studio自定义模板之MVPActivity

<?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
Android Studio自定义模板之MVPActivity

<?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.等导包)。如图
Android Studio自定义模板之MVPActivity
Android Studio自定义模板之MVPActivity
Android Studio自定义模板之MVPActivity
Android Studio自定义模板之MVPActivity
2.内联的顺序
内联的顺序在代码中是从上往下执行的,因此要想生成文件后焦点窗口定位在新的Activity内,则要把Activity的内联代码放在最下面,这样所有文件生成完毕后才会优先定位到Activity窗口
Android Studio自定义模板之MVPActivity
PS:若编码的时候出现语法错误,那么在Android studio中点击finish生成Activity的时候会直接报错,查看点击log可以看到详细的报错位置,自己再进行修改就可以了。

附上模板源码:MVP模板下载地址

参考链接

原文 https://blog.csdn.net/u013452460/article/details/106671562

本文地址:https://blog.csdn.net/u013452460/article/details/106671562

相关标签: 模板 android