[译] 使用自定义文件模板加快你的应用开发速度

使用自定义文件模板加快你的应用开发速度

感谢:Google Inc.,维基共享资源和 Vexelsphp

Wishfie 开发 Android 应用时,咱们常常须要编写大量的样板代码以用于建立新的 Activity 和 Fragment。我会举一个例子来讲明个人意思:html

当咱们遵循 MVP 架构时,每一个新增的 Activity 或 Fragment 都须要一个 Contract 类,一个 Presenter 类,一个 Dagger 模板及 Activity 类自身,这致使咱们每次都须要编写大量的类似代码。前端

下面即是咱们的 Activity、Module、Contract 和 Presenter:java

public class DemoActivity extends DemoBaseActivity<DemoContract.Presenter> implements DemoContract.View {

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_demo);
    }

}
复制代码
@Module
public abstract class DemoActivityModule {
    @Binds
    @PerActivity
    abstract DemoContract.Presenter providesPresenter(DemoPresenter demoPresenter);

    @Binds
    @PerActivity
    abstract DemoContract.View providesView(DemoActivity demoActivity);
}
复制代码
public interface DemoContract {
    interface View extends DemoBaseContract.ActivityView {

    }

    interface Presenter extends DemoBaseContract.Presenter {

    }
}
复制代码
public class DemoPresenter extends DemoBasePresenter<DemoContract.View> implements DemoContract.Presenter {

    @Inject
    public DemoPresenter(DemoContract.View view) {
        super(view);
    }

    @Override
    public void unSubscribe() {

    }

    @Override
    public void subscribe() {

    }
}
复制代码

这是 android 中常见的模式,不少人可能都在使用它。这就是咱们所遇到的问题,它的解决方案来源于 Android Studio 中一个很棒的功能(自定义模板)。linux

在本文的最后,咱们将建立一个根据不一样后缀一次建立全部必须文件的模板。那么,让咱们开始吧:android

Android Studio 中的模板是什么?

Android Studio activity 建立模板ios

IntelliJ 描述以下:git

文件模板是建立新文件时要生成的默认内容规范。根据你建立的文件类型,模板提供了在该类型文件中所预期的初始化代码和格式(根据行业标准,你的公司政策或其余内容)。github

简单来讲,模板用于建立包含一些样板代码的文件。大多数状况下,当你从预约义选项集中建立 Activity、Fragment 和 Service 等文件时,它已经为你编写了许多样板代码,这些代码基本上都是由 Android Studio 团队建立的一组预先编写好的模板建立的。例如,从上图显示菜单建立的 empty activity 默认包含如下样板代码,XML 文件以及 manifest 文件的入口配置。apache

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class EmptyActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
    }
}
复制代码

你能建立什么类型的模板?

  1. 你能够建立 .java.xml.cpp 等类型的文件模板。

  2. 你能够建立你本身的实时模板。若是你曾经用过 Toast 模板或用于定义 public static final intpsfi,这些被称为实时模板。

  3. 你能够建立一组文件模板。好比,查看 Android Studio 如何为 Activity 建立 .xml.java 文件,而且在 manifest 文件中添加该 activity 的详细信息。

用什么语言建立模板?

使用 Apache Velocity Template Language 建立这些模板。

本文章节:

  1. 咱们将首先建立一个基本文件模板,该模板将建立一个 RecyclerView Adapter 以及一个内部 ViewHolder 类,由于它是最经常使用的类之一。

  2. 咱们将建立咱们本身的实时模板。

  3. 咱们将经过编写用于建立上述 4 个文件的模板来结束此操做,以便在咱们的应用中遵循 mvp 架构。

章节 1:

  • 右键单击任何包目录,而后选择 New -> Edit File Templates

  • 单击 + 按钮建立一个新模板,并将其命名为你想要的任何名称。我将它命名为 RecyclerViewAdapter。

  • 将下面的模板代码粘贴到名称字段下方的区域中。我会一步一步解释代码中发生了什么:

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

import android.content.Context;
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<${VIEWHOLDER_CLASS}> {
    private final Context context;
    private List<${ITEM_CLASS}> items;

    public ${NAME}(List<${ITEM_CLASS}> items, Context context) {
        this.items = items;
        this.context = context;
    }

    @Override
    public ${VIEWHOLDER_CLASS} onCreateViewHolder(ViewGroup parent,
                                             int viewType) {
        View v = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.${LAYOUT_RES_ID}, parent, false);
        return new ${VIEWHOLDER_CLASS}(v);
    }

    @Override
    public void onBindViewHolder(${VIEWHOLDER_CLASS} holder, int position) {
        ${ITEM_CLASS} item = items.get(position);
        holder.set(item);
    }

    @Override
    public int getItemCount() {
        if (items == null){
            return 0;
        }
        return items.size();
    }

    public class ${VIEWHOLDER_CLASS} extends RecyclerView.ViewHolder {

        public ${VIEWHOLDER_CLASS}(View itemView) {
            super(itemView);
        }

        public void set(${ITEM_CLASS} item) {
            //UI setting code
        }
    }
 }
复制代码
  • 若是你快速阅读 android studio 中代码输入字段下面的 Description 面板,上面的大部分代码都很容易理解。

  • ${<VARIABLE_NAME>} 用于建立在整个模板中使用的变量,而且当你使用模板建立代码时,系统会提示你为它们输入值。这还有一些预约义的变量,好比 ${PACKAGE_NAME}${DATE}等。

  • #if 指令用来检查包名是否为空,若是不为空,则将名称添加到做为 ${PACKAGE_NAME} 变量传递的包语句中。

  • #parse 指令用于插入另外一个名为 File Header.java 模板的内容,你能够在同一窗口的 includes 选项卡下找到该模板。看起来像这样:

  • 其他代码使用这些变量和静态文本,代码和注释来建立文件。

  • 如今右键单击任何目录,而后单击 New,你将在那里找到你的模板。单击它将打开一个提示框,输入咱们以前定义的占位符的值。

  • 如下是咱们生成的模板:
package io.github.rajdeep1008.templatedemo;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import java.util.List;

public class SchoolData extends RecyclerView.Adapter<SchoolData> {
    private final Context context;
    private List<SchoolItem> items;

    public SchoolData(List<SchoolItem> items, Context context) {
        this.items = items;
        this.context = context;
    }

    @Override
    public SchoolData onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.R.layout.item_school, parent, false);
        return new SchoolData(v);
    }

    @Override
    public void onBindViewHolder(SchoolData holder, int position) {
        SchoolItem item = items.get(position);
        holder.set(item);
    }

    @Override
    public int getItemCount() {
        if (items == null) {
            return 0;
        }
        return items.size();
    }

    public class SchoolData extends RecyclerView.ViewHolder {

        public SchoolData(View itemView) {
            super(itemView);
        }

        public void set(SchoolItem item) {
            //UI setting code
        }
    }
}
复制代码

使用咱们的 Android Studio 模板生成文件。

章节 2:

  • 这个章节与咱们为 mvp 源文件建立模板的最终目的没什么关系,但知道 Android Studio 为咱们提供的每一个选项是有好处的。

  • 实时模板是你在代码中快速获取代码段的快捷方式。你还能够添加参数来快速标记它们。

在 Android Studio 中播放实时模板。

  • 对于 mac 用户,导航到 Android Studio -> Preferences -> Editor -> Live Templates,在这里你将看到一个包含已有实时模板的列表框,好比 fbc 用于 findViewById 映射,foreach 用于建立 loop 等。

  • 如今点击 Android -> + ->LiveTemplate,你能够选择添加缩写来使用模板,说明模板的功能以及模板的模板文本。

  • 如今点击 Define 并选择弹框中的 XML 选项来选择模板可用的文件类型。

Android Studio 中实时模版建立向导

  • 单击肯定保存并开始使用它。打开 XML 布局文件并开始输入 rv 并按 Tab 以适用新建立的模板。

咱们新建立的实时模板

章节 3:

Pheww!咱们已经介绍了不少东西,如今是时候开始建立咱们的 mvp 模板了。咱们须要建立一个 Activity、DaggerModule、Contract 和 Presenter。前缀将做为用户输入,剩下的将采用本文开头所述的格式。

  • 导航到你的 Windows/Linux/Mac 文件系统中的 Android Studio 目录,而后转到 plugins -> android -> lib -> templates -> other,用你但愿在菜单中看到的名称建立一个空目录,我将其命名为 MVP Template。

  • 在 mac 中,目录的位置应该为 /Applications/Android/Studio.app/Contents/plugins/android/lib/templates/other/,对于 windows 或 linux,你能够在 {ANDROID_STUDIO_LOCATION}/plugins/android/lib/templates/other/ 中找到它。

  • 确保检查模板中的 activities 目录,看看如何模板建立 EmptyActivity、BasicActivity 以及其余文件,这将有助于编写本身的模板。

  • 如今,在新建立的 MVP Template 目录中,建立 template.xml、recipe.xml.ftlglobals.xml.ftl。而且建立一个名为 root 的目录,它将保存咱们建立的实际模板文件。我将逐一解释每一个文件的做用:

  1. template.xml — 它用来处理屏幕配置的 UI 部分。 它定义了用户在使用模板建立文件时看到的用户输入字段、复选框和下拉列表等。

  2. recipe.xml.ftl — 这是使用的文件,你的根目录中的模板将转换为 Android Studio 中真实的 java 文件。它包含有关要建立哪些文件以及从哪些模板建立等信息。

  3. globals.xml.ftl — 这包含全部全局变量。在这里为 src 和 res 定义目录路径是一个很好的作法。

  • 在 template.xml 文件中,粘贴如下代码:
<template format="4" revision="1" name="MVP Template Activity" description="Creates a new MVP classes - Presenter, View, Contract and Dagger Module.">

    <category value="Other"/>

    <parameter id="className" name="Functionality Name" type="string" constraints="class|unique|nonempty" default="MvpDemo" help="The name of the functionality that requires MVP views"/>

    <globals file="globals.xml.ftl" />
    <execute file="recipe.xml.ftl" />

</template>
复制代码

template.xml 描述了应该从用户那里得到的参数:

  1. id 是该元素的惟一 id。
  2. name 只是向用户显示的提示(就像在 EditText 中的提示同样)。
  3. type 定义用户应该显示文本输入仍是下拉控件中的枚举值,或在布尔值的状况下显示复选框。
  4. default 用户输入为空时的默认值。
  5. globalsexecute 属性连接咱们的全局变量和配置文件。
  • 在 recipe.xml.ftl 文件中,粘贴如下代码:
<?xml version="1.0"?>
<recipe>

    <instantiate from="src/app_package/Contract.java.ftl" to="${escapeXmlAttribute(srcOut)}/${className}Contract.java" />
    <instantiate from="src/app_package/Activity.java.ftl" to="${escapeXmlAttribute(srcOut)}/${className}Activity.java" />
    <instantiate from="src/app_package/Presenter.java.ftl" to="${escapeXmlAttribute(srcOut)}/${className}Presenter.java" />
    <instantiate from="src/app_package/ActivityModule.java.ftl" to="${escapeXmlAttribute(srcOut)}/${className}ActivityModule.java" />


    <open file="${srcOut}/${className}Presenter.java"/>
    <open file="${srcOut}/${className}Contract.java"/>
    <open file="${srcOut}/${className}Activity.java"/>
    <open file="${srcOut}/${className}ActivityModule.java"/>
</recipe>
复制代码

recipe.xml.ftl 定义从哪一个模板建立哪些文件以及建立后打开哪些文件。它还能够将代码从咱们的模板复制到 manifest.xml 或 string.xml 等文件中。请务必查看用于建立 activities 的默认模板示例。

className 变量是咱们从用户那里获取的输入的 id,其代码用 template.xml 编写,srcOut 在 globals.xml.ftl 中定义。文件的其余部分具备很好的自我解释能力。

  • 在 globals.xml.ftl 中:
<?xml version="1.0"?>
<globals>
 <global id="resOut" value="${resDir}" />
 <global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
</globals>
复制代码
  • 如今,在根目录中,建立 src/app_package/ 目录并将如下四个文件复制到该目录中:
package ${packageName};

public class ${className}Activity extends DemoBaseActivity<${className}Contract.Presenter> implements ${className}Contract.View {

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_demo);
    }

}
复制代码
package ${packageName};

@Module
public abstract class ${className}ActivityModule {
    @Binds
    @PerActivity
    abstract ${className}Contract.Presenter providesPresenter(${className}Presenter presenter);

    @Binds
    @PerActivity
    abstract ${className}Contract.View providesView(${className}Activity activity);
}
复制代码
package ${packageName};

public interface ${className}Contract{

    interface View extends DemoBaseContract.ActivityView {

    }

    interface Presenter extends DemoBaseContract.Presenter {

    }
}
复制代码
package ${packageName};

public class ${className}Presenter extends DemoBasePresenter<${className}Contract.View> implements ${className}Contract.Presenter {

    @Inject
    public ${className}Presenter(${className}Contract.View view){
        super(view);
    }

    @Override
    public void subscribe() {

    }

    @Override
    public void unSubscribe() {

    }
}
复制代码

这些文件包含将彻底转换为 java 或 xml 代码的模板,参数将被实际值替换。

咱们终于完成了全部步骤。只须要重启 Android Studio 便可启用此模板,并显示在菜单中。

咱们新建立的 MVP 模板

若是使用得当,Android Studio 模板是加快应用开发速度的强大功能。这些模板能够分布在整个 Android 团队中,以便简化样板代码的建立。

以上即是本文的全部内容。若是你喜欢这篇文章并发现它有用,请不要忘记点赞并与其余 Android 开发者分享它。Happy coding 💗。

顺便说一句我开通了每周简报 thedevweekly我将经过网站、移动设备和系统上精心挑选文章,并在有关新技术学习及一些大科技公司内部学习文章之间取得平衡。

所以,不管你是初学者仍是专家,若是你正在寻找精心策划的科技文章的每周摘要,请在 这里 注册 .


参考资料:

若是发现译文存在错误或其余须要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可得到相应奖励积分。文章开头的 本文永久连接 即为本文在 GitHub 上的 MarkDown 连接。


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏

相关文章
相关标签/搜索