消灭模板代码,自定义AndroidStudio文件模板
Posted TellH
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了消灭模板代码,自定义AndroidStudio文件模板相关的知识,希望对你有一定的参考价值。
在上一篇讲到的自定义androidStudio文件模板的方法,但这种方法虽然简单却只能一次生成一个文件。有没有方法能一键生成一套文件呢?其实AndroidStudio已经提供了这样的功能,而且我们也经常在用。
如图红框上的文件就是AndroidStudio已经提供的模板文件组,最常用的就是Activity了。但我们使用这里的模板新建一个Activity,AndroidStudio就会自动为我们新建一个Activity的java类文件、和Layout布局xml文件,同时还会贴心地为我们在Manifest文件中注册我们的Activity。这一切是怎么做到的呢?
溯源
我想,这些模板肯定是被放在某些地方的文件集,说不定文件夹的名字就对应这里模板的名字呢!用强大的文件搜索软件EveryThing搜一下这里的其中一个模板的名字,就会发现,这些模板文件就存在我们AndroidStudio(IDEA)的安装目录中。
一下是在Windows下存放文件模板的目录:
<AndroidStudio安装目录>\\plugins\\android\\lib\\templates
在activitys目录下就存放着相应的Activity模板:
探秘
好奇心是学习的动力,我迫不及待地打开BasicActivity目录:
发现有一个root目录,若干.ftl文件和若干图片。
这些文件怎么关联在一起,怎么发挥作用,怎么接受用户输入并生成文件的呢?
经过Google后,我发现了这张图:
原来如此,FreeMarker接受用户输入的参数和两种.ftl文件,输出生成的代码文件。问题来了,what is FreeMarker?能吃吗?
FreeMarker是一款模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(html网页、电子邮件、配置文件、源代码等)的通用工具。--源自百度百科
FreeMarker很符合我们的需求,看来这个解析器是集成在AndroidStudio中的。有人可能会吐槽,搞个模板还要专门跑去学FreeMarker吗?其实,就算不懂FreeMarker语法(最好有一丁点了解)也可以轻松搞一个简单的文件模板,因为AndroidStudio提供很多现成的Demo是最好的学习资料,创造往往从模仿开始!
先从这个template.xml文件开始:
<?xml version="1.0"?>
<template
format="5"
revision="6"
name="Basic Activity"
minApi="7"
minBuildApi="14"
requireAppTheme="true"
description="Creates a new basic activity with an app bar.">
<category value="Activity"/>
<formfactor value="Mobile"/>
<parameter
id="activityClass"
name="Activity Name"
type="string"
constraints="class|unique|nonempty"
suggest="$layoutToActivity(layoutName)"
default="MainActivity"
help="The name of the activity class to create"/>
------------------此处省略若干<parameter/>标签-------------------------------
<parameter
id="useFragment"
name="Use a Fragment"
type="boolean"
default="false"
help="If true, the content will be a fragment"/>
<!-- 128x128 thumbnails relative to template.xml -->
<thumbs>
<!-- default thumbnail is required -->
<thumb>template_basic_activity.png</thumb>
<!-- attributes act as selectors based on chosen parameters -->
<thumb useFragment="true">template_basic_activity_fragment.png</thumb>
</thumbs>
<globals file="globals.xml.ftl"/>
<execute file="recipe.xml.ftl"/>
</template>
这个文件在AndroidStudio启动时加载的,所以要修改这个文件需要重启AndroidStudio才能生效。
< template>可以配置模板的名称、 描述等信息;
其子标签内:
<category value="Activity"/>
用于将模板分类<parameter>
用于添加用户输入参数,这个标签可以配置以下属性:
id
给参数设置一个唯一的标示,用于在后面编写单个文件模板时结合$
填充拼接到代码中。name
和help
给用户输入参数时起到提示的作用default
给参数设置默认值type
当值为string时该参数在输入对话框对应为一个文本输入框,当值为boolean时该参数在输入对话框对应一个勾选框。visibility
设置参数什么时候可见,什么时候不可见。比如我想id为a的参数勾选之后该参数才显示出来,就可以设置成visibility="a"
<thumbs>
是为模板设置图片的<globals file="globals.xml.ftl"/>
为这个模板设置一些全局参数<execute file="recipe.xml.ftl"/>
最关键的是这个标签,当用户输入完参数后点击OK后,第一时间就会触发这个文件。
你看,根据这个文件,AndroidStudio对应就会生成一个这样的对话框:
recipe.xml.ftl
<?xml version="1.0"?>
<recipe>
<#include "../common/recipe_manifest.xml.ftl" />
<#if useFragment>
<#include "recipe_fragment.xml.ftl" />
<#else>
<#include "../common/recipe_simple.xml.ftl" />
</#if>
<#if hasAppBar>
<#include "../common/recipe_app_bar.xml.ftl" />
</#if>
<instantiate from="root/src/app_package/SimpleActivity.java.ftl"
to="$escapeXmlAttribute(srcOut)/$activityClass.java" />
<open file="$escapeXmlAttribute(srcOut)/$activityClass.java" />
<open file="$escapeXmlAttribute(resOut)/layout/$simpleLayoutName.xml" />
</recipe>
这个文件可以理解成一个执行单元。
- 可以通过
<#include>
请其他执行单元来帮忙,共同完成生成模板的工作。 <#if booleanParam>
和<#else>
<#elseif>
可以通过前面用户输入参数的布尔值动态调整文件的输出。<instantiate from="" to="" />
给单个文件模板与实际生成文件建立一对一的映射<open file="" />
生成文件后,AndroidStudio会自动打开这些文件。
root目录
root目录下存放着单个模板文件,这些文件其实也是一个执行单元,这不过这些执行单元的产物只有一个文件。
比如这个SimpleActivity.java.ftl:
package $packageName;
import android.os.Bundle;
<#if hasAppBar>
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
<#else>
import $superClassFqcn;
</#if>
<#if isNewProject>
import android.view.Menu;
import android.view.MenuItem;
</#if>
<#if applicationPackage??>
import $applicationPackage.R;
</#if>
public class $activityClass extends $superClass
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.$layoutName);
<#if hasAppBar>
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View view)
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
);
</#if>
<#if parentActivityClass != "">
get$SupportActionBar().setDisplayHomeAsUpEnabled(true);
</#if>
<#if isNewProject>
@Override
public boolean onCreateOptionsMenu(Menu menu)
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.$menuName, menu);
return true;
@Override
public boolean onOptionsItemSelected(MenuItem item)
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings)
return true;
return super.onOptionsItemSelected(item);
</#if>
这里也有一些<#if>
等条件判断标签用于动态生成代码,$param
这些参数就来自于前面提到的template.xml和globals.xml.ftl文件中。
了解到这个程度,我们就可搞一个简单实用的模板了!
实践
需求:自定义一个文件模板,一键生成ListView或RecyclerView的Adapter。
我将这个模板归位Other类,所以在Other目录下,新建一个Adapter目录:
AndroidStudio的安装目录\\plugins\\android\\lib\\templates\\other\\Adapter
新建template.xml,设置用户输入的参数:
<?xml version="1.0"?>
<template
format="5"
revision="5"
name="Adapter"
description="Creates Adapter">
<category value="Other" />
<parameter
id="adapterName"
name="Adapter Name"
type="string"
constraints="unique|nonempty"
default="Main"
help="The name should'n contain 'Adapter'" />
<parameter
id="layoutId"
name="Layout id"
type="string"
help="this id should'n contain 'R.layout'" />
<parameter
id="itemType"
name="Data type of item"
type="string"
constraints="unique|nonempty"
default="String"
help="The data type of each item in adapter." />
<parameter
id="forRecyclerView"
name="For RecyclerView"
type="boolean"
default="true"
help="If true, it will generate adapter for RecyclerView."/>
<parameter
id="rvNative"
name="generate Native Adapter"
type="boolean"
default="true"
visibility="forRecyclerView"
help="If true, the Native Adapter for RecyclerView are generated automatically."/>
<parameter
id="rvWrap"
name="generate wrapped Adapter"
type="boolean"
default="false"
visibility="forRecyclerView"
help="If true, the wrapped Adapter for RecyclerView are generated automatically."/>
<parameter
id="rvWithBase"
name="With BaseAdapter"
type="boolean"
default="false"
visibility="rvWrap"
help="If true, it will generate BaseAdapter for RecyclerView."/>
<parameter
id="forListView"
name="For ListView"
type="boolean"
default="false"
help="If true, it will generate adapter for ListView."/>
<parameter
id="lvNative"
name="generate Native Adapter"
type="boolean"
default="true"
visibility="forListView"
help="If true, the Native Adapter for ListView are generated automatically."/>
<parameter
id="lvWrap"
name="generate wrapped Adapter"
type="boolean"
default="false"
visibility="forListView"
help="If true, the wrapped Adapter for ListView are generated automatically."/>
<parameter
id="lvWithBase"
name="With BaseAdapter"
type="boolean"
default="false"
visibility="lvWrap"
help="If true, it will generate BaseAdapter for ListView."/>
<!-- 128x128 thumbnails relative to template.xml -->
<thumbs>
<!-- default thumbnail is required -->
<thumb>template_adapter.png</thumb>
</thumbs>
<globals file="globals.xml.ftl" />
<execute file="recipe.xml.ftl" />
</template>
效果图:
recipe.xml.ftl:
<?xml version="1.0"?>
<recipe>
<#if forListView>
<#include "recipe_listview.xml.ftl" />
</#if>
<#if forRecyclerView>
<#include "recipe_recyclerview.xml.ftl" />
</#if>
<open file="$escapeXmlAttribute(srcOut)/$adapterNameAdapter.java" />
</recipe>
globals.xml.ftl:
<?xml version="1.0"?>
<globals>
<global id="resOut" value="$resDir" />
<global id="srcOut" value="$srcDir/$slashedPackageName(packageName)" />
</globals>
在recipe_listview.xml.ftl和recipe_recyclervewview.xml.ftl文件中,分别生成ListView和RecyclerView的Adapter。
在root目录中
一切就绪后,重启AndroidStudio后,在工程目录右键-> new :
具体代码已上传到Github。
总结
本文实现的Adapter只是抛砖引玉而已,AndroidStudio这个自定义文件模板的功能是十分强大又实用的,我们平时开发时可以发挥想象力,开发一些文件模板,甚至是工程模板,大大加快开发效率。
参考资料:
Tutorial How To Create Custom Android Code Templates
How to create a group of File Templates in Android Studio – Part 3
以上是关于消灭模板代码,自定义AndroidStudio文件模板的主要内容,如果未能解决你的问题,请参考以下文章