在编写android项目中,我们难免会重复的去写一些东西或者写一些浪费时间但又和我们项目逻辑关系不大的代码,所以学会编写模板代码变得很重要,我这里指得模板有两种:

单个文件

某个功能

对于第一种情况,如下:

4f6c2d62b410

sigle.png

4f6c2d62b410

singleton.png

在上面我们仿照系统文件新建了两个模板类,RecycleViewAdapter和Singleton,在我们新建类列表中可以看到。我们得代码是仿照着class文件编写得,class文件内容如下:

#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end

#parse("File Header.java")

public class ${NAME} {

}

PACKAGE_NAME

NAME

File Header.java

包名

类名

头部文件说明

头部文件如下:

4f6c2d62b410

header.png

这里单例实现用静态内部类实现得,因为静态内部类初始化是由java虚拟机管理的,classloder加载,线程安全,下面是这两个类的代码:

singleton.java

#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end

#parse("File Header.java")

public class ${NAME}{

public static ${NAME} getInstance() {

return ${NAME}Holder.sInstance;

}

private ${NAME}() {

}

private static class ${NAME}Holder {

private static final ${NAME} sInstance = new ${NAME}();

}

}

RecycleViewAdapter.java

#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end

import android.content.Context;

import android.support.annotation.LayoutRes;

import android.support.v7.widget.RecyclerView;

import android.view.LayoutInflater;

import android.view.View;

import android.view.ViewGroup;

import java.util.List;

#parse("File Header.java")

public class ${NAME} extends RecyclerView.Adapter {

private Context ctx; private List objects;

public ${NAME}(Context ctx, List objects) {

this.ctx = ctx; this.objects = objects;

}

private @LayoutRes int provideItemLayout() {

// todo

return 0;

}

@Override

public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

View view = LayoutInflater.from(ctx).inflate(provideItemLayout(), parent, false);

return new ViewHolder(view);

}

@Override

public void onBindViewHolder(ViewHolder holder, int position) {

holder.bind(objects.get(position));

}

@Override

public int getItemCount() {

return objects == null ? 0 : objects.size();

}

public static class ViewHolder extends RecyclerView.ViewHolder {

public ViewHolder(View itemView) {

super(itemView);

//todo

}

public void bind(Object o) {

// todo

} } }

最后使用的话就很简单了,在我们第一张图中新建模板上面两个类就是我们新建的模板,直接点击就可以生成我们想要的类了,我们只需要输入相应的类名即可。

第二种情况是针对某个功能,我们可能会新建很多上面的那种类,就是我们新建项目的时候系统提供给我们提供的各种模板,在Android studio中新建activity、fragment时用到。会同时新建相应的布局,注册manifest等等,如下:

4f6c2d62b410

yy.gif

上面这个模板本来是MVPArm作者写的,是一个mvp+dagger的架构实现,我这里对里面的部分内容进行了修改,用以适应我自己的demo,整个模板的核心是freemarker,实现思路如下:

4f6c2d62b410

free.png

freemarker作用是把我们写的.jva.ftl文件生成我们自己需要的.java文件,而我们需要生成的文件名,布局名、包名等内容在初始化界面时输入即可,整个功能是一个单独的包,现在我们来分析一下这个组件的包里面的内容:

4f6c2d62b410

ftl.png

4f6c2d62b410

layout.png

4f6c2d62b410

t.png

4f6c2d62b410

template.png

如上图,我们需要的所有内容就在MVPTemplate里面,其中最重要的是template.xml,这里面进行我们需要关键字的注册,如包名、类名啥的:

template.xml

format="5"

revision="1"

name="MVP Template"

minApi="9"

minBuildApi="15"

description="mvp demo">

id="pageName"

name="Page Name"

type="string"

constraints="unique|nonempty"

default="Main"

help="请填写页面名,如填写 Main,会自动生成 MainActivity, MainPresenter 等文件" />

id="packageName"

name="Root Package Name"

type="string"

constraints="package"

default="com.mycompany.myapp"

help="请填写你的项目包名,请认真核实此包名是否是正确的项目包名,不能包含子包"

/>

id="needActivity"

name="Generate Activity"

type="boolean"

default="true"

help="是否需要生成 Activity ? 不勾选则不生成" />

id="activityLayoutName"

name="Activity Layout Name"

type="string"

constraints="layout|nonempty"

suggest="${activityToLayout(pageName)}"

default="activity_main"

visibility="needActivity"

help="Activity 创建之前需要填写 Activity 的布局名,若布局已创建就直接填写此布局名,若还没创建此布局,请勾选下面的单选框" />

id="generateActivityLayout"

name="Generate Activity Layout"

type="boolean"

default="true"

visibility="needActivity"

help="是否需要给 Activity 生成布局? 若勾选,则使用上面的布局名给此 Activity 创建默认的布局" />

id="ativityPackageName"

name="Ativity Package Name"

type="string"

constraints="package"

suggest="${packageName}.mvp.ui.activity"

visibility="needActivity"

help="Activity 将被输出到此包下,请认真核实此包名是否是你需要输出的目标包名"

/>

id="needFragment"

name="Generate Fragment"

type="boolean"

default="false"

help="是否需要生成 Fragment ? 不勾选则不生成" />

id="fragmentLayoutName"

name="Fragment Layout Name"

type="string"

constraints="layout|nonempty"

suggest="fragment_${classToResource(pageName)}"

default="fragment_main"

visibility="needFragment"

help="Fragment 创建之前需要填写 Fragment 的布局名,若布局已创建就直接填写此布局名,若还没创建此布局,请勾选下面的单选框" />

id="generateFragmentLayout"

name="Generate Fragment Layout"

type="boolean"

default="true"

visibility="needFragment"

help="是否需要给 Fragment 生成布局? 若勾选,则使用上面的布局名给此 Fragment 创建默认的布局" />

id="fragmentPackageName"

name="Fragment Package Name"

type="string"

constraints="package"

suggest="${packageName}.mvp.ui.fragment"

visibility="needFragment"

help="Fragment 将被输出到此包下,请认真核实此包名是否是你需要输出的目标包名"

/>

id="needContract"

name="Generate Contract"

type="boolean"

default="true"

help="是否需要生成 Contract ? 不勾选则不生成" />

id="contractPackageName"

name="Contract Package Name"

type="string"

constraints="package"

suggest="${packageName}.mvp.contract"

visibility="needContract"

help="Contract 将被输出到此包下,请认真核实此包名是否是你需要输出的目标包名"

/>

id="needPresenter"

name="Generate Presenter"

type="boolean"

default="true"

help="是否需要生成 Presenter ? 不勾选则不生成" />

id="presenterPackageName"

name="Presenter Package Name"

type="string"

constraints="package"

suggest="${packageName}.mvp.presenter"

visibility="needPresenter"

help="Presenter 将被输出到此包下,请认真核实此包名是否是你需要输出的目标包名"

/>

id="needModel"

name="Generate Model"

type="boolean"

default="true"

help="是否需要生成 Model ? 不勾选则不生成" />

id="modelPackageName"

name="Model Package Name"

type="string"

constraints="package"

suggest="${packageName}.mvp.model"

visibility="needModel"

help="Model 将被输出到此包下,请认真核实此包名是否是你需要输出的目标包名"

/>

id="needDagger"

name="Generate Dagger (Moudle And Component)"

type="boolean"

default="true"

help="是否需要生成 Dagger 组件? 不勾选则不生成" />

id="componentPackageName"

name="Component Package Name"

type="string"

constraints="package"

suggest="${packageName}.di.component"

visibility="needDagger"

help="Component 将被输出到此包下,请认真核实此包名是否是你需要输出的目标包名"

/>

id="moudlePackageName"

name="Moudle Package Name"

type="string"

constraints="package"

suggest="${packageName}.di.module"

visibility="needDagger"

help="Moudle 将被输出到此包下,请认真核实此包名是否是你需要输出的目标包名"

/>

template_blank_activity.png

上面大部分属性猜大概都能猜出来什么意思,比较重要的是

MvpActivity.java.ftl

package ${ativityPackageName};

import android.content.Intent;

import android.os.Bundle;

import android.support.annotation.Nullable;

import android.support.v7.app.AppCompatActivity;

import android.util.Log;

import ${packageName}.R;

import ${componentPackageName}.Dagger${pageName}Component;

import ${moudlePackageName}.${pageName}Module;

import ${contractPackageName}.${pageName}Contract;

import ${presenterPackageName}.${pageName}Presenter;

import javax.inject.Inject;

public class ${pageName}Activity extends AppCompatActivity implements ${pageName}Contract.View {

@Inject

${pageName}Presenter ${pageName}Presenter;

@Override

protected void onCreate(@Nullable Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.${activityLayoutName});

Dagger${pageName}Component.builder()

.${extractLetters(pageName[0]?lower_case)}${pageName?substring(1,pageName?length)}Module(new ${pageName}Module(this))

.build()

.inject(this);

${pageName}Presenter.getData();

}

@Override

public void showLoading() {

}

@Override

public void hideLoading() {

}

@Override

public void showMessage(String message) {

}

@Override

public void launchActivity(Intent intent) {

}

@Override

public void killMyself() {

}

@Override

public void showData(String string) {

Log.e("yy", string);

}

}

代码中用${}符号表示EL表达式,目的是为了在代码中能够调用到template中传入的内容,仔细看我们的template.xml文件结尾其实还有两个类,globals.xml.ftl和recipe.xml.ftl,也包含一个生成案例的图片展示thumb字段,globals.xml.ftl中主要是声明一些属性格式:

recipe.xml.ftl文件主要是告诉freemarker文件从什么地方来(from xxx.java.ftl),到什么地方去(to xxx.java),如下:

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

#if>

to="${escapeXmlAttribute(resOut)}/layout/${activityLayoutName}.xml" />

#if>

to="${escapeXmlAttribute(resOut)}/layout/${fragmentLayoutName}.xml" />

#if>

to="${projectOut}/src/main/java/${slashedPackageName(ativityPackageName)}/${pageName}Activity.java" />

#if>

to="${projectOut}/src/main/java/${slashedPackageName(fragmentPackageName)}/${pageName}Fragment.java" />

#if>

to="${projectOut}/src/main/java/${slashedPackageName(contractPackageName)}/${pageName}Contract.java" />

#if>

to="${projectOut}/src/main/java/${slashedPackageName(presenterPackageName)}/${pageName}Presenter.java" />

#if>

to="${projectOut}/src/main/java/${slashedPackageName(modelPackageName)}/${pageName}Model.java" />

#if>

to="${projectOut}/src/main/java/${slashedPackageName(componentPackageName)}/${pageName}Component.java" />

to="${projectOut}/src/main/java/${slashedPackageName(moudlePackageName)}/${pageName}Module.java" />

#if>

可以看到,来源就是我们已经编写好的.java.ftl文件,去向是还未命名的.java文件,我改这个东西是复制一份包,直接进行改的,改好后放进去就行了,如果没改好,你是生不成代码的,好好去检查一下包下面的文件。

自定义类和模块模板的内容到这里就基本上结束了,希望对大家的开发有所帮助,最后说一个题外话,第一次转到简书来,熟悉markdown编辑器的语法花了一些时间,最后下载了一个有界面的markdown编辑器小书匠写的本篇文章。

模板包下载:百度网盘

密码:5lk7

推荐文章

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐