Android Studio自定义模板 写页面竟然可以如此轻松

学习编写模板最好的方式呢,就是参考IDE中已经提供的最简单的模板,那么在Android Studio中最简单的activity模板就是:Empty Activity了,我们打开该模板文件,首先对文件结构有个直观的了解,如图:

可以看到每个插件对应一个文件夹,文件夹包含
template.xml
globals.xml.ftl
recipe.xml.ftl
root文件夹 存放对应源码的ftl文件,以及资源文件
效果缩略图
下面我们逐一对上述每个文件的作用就行介绍。

2.1 template.xml
打开该文件,发现其主要内容如下:
template_blank_activity.pngthumb>thumbs>template>
其中的name属性,对应新建Activity时显示的名字
对应New的类别为Activity
剩下的,对应我们AndroidStudio新建Empty Activity的界面就非常好理解了,如图:

看到这个界面,大部分属性都应该能才出来了,我们重点看parameter,界面上每一个紫色框出来的部分都对应一个parameter,部分属性介绍:
id:唯一标识,最终通过该属性的值,获取用户输入值(文本框内容,是否选中)
name:界面上的类似label的提示语
type :输入值类型
constraints:填写值的约束
suggest:建议值,比如填写ActivityName的时候,会给出一个布局文件的建议值。
default:默认值
help:底部显示的提升语
这个部分对应界面还是非常好理解的,大家可以简单的修改一些字符串,或者添加一个,重启AS,看看效果。
template.xml的最下面的部分引入了globals.xml.ftl和recipe.xml.ftl。
这两个我们会详细介绍。

2.2 globals.xml.ftl
<#include "../common/common_globals.xml.ftl" />globals>
通过名称可以猜到它是用于定义一些全局的变量,可以看到其内部有标签,分别定义id,type,默认值。同理,我们可以通过id的值访问到该值,例如:
${hasNoActionBar}的值为false。

2.3 recipe.xml.ftl
from="root/res/drawable-hdpi"to="${escapeXmlAttribute(resOut)}/drawable-hdpi" />from="root/src/app_package/SimpleActivity.java.ftl"to="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />
为了介绍,我将该xml中比较重要的几个标签都列出来了:
copy :从root中copy文件到我们的目标目录,比如我们的模板Activity需要使用一些图标,那么可能就需要使用copy标签将这些图标拷贝到我们的项目对应文件夹。
merge :合并的意思,比如将我们使用到的strings.xml合并到我们的项目的stirngs.xml中
instantiate :和copy类似,但是可以看到上例试将ftl->java文件的,也就是说中间会通过一个步骤,将ftl中的变量都换成对应的值,那么完整的流程是ftl->freemarker process -> java。
open:在代码生成后,打开指定的文件,比如我们新建一个Activity后,默认就会将该Activity打开。
在介绍instantiate时,涉及到了freemarker,不可避免的需要对它进行简单的介绍。
目前我们已经基本了解了一个模板其内部的文件结构了,以及每个文件大致包含的东西,我们简单做个总结:
template中parameter标签,主要用于提供参数
global.xml.ftl主要用于提供参数
recipe.xml.ftl主要用于生成我们实际需要的代码,资源文件等;例如,利用参数+MainActivity.java.ftl -> MainActivity.java;其实就是利用参数将ftl中的变量进行替换。
那么整体的关系类似下图:

3、简单的freemarker语法
上面我们已经基本了解模板生成的大致的流程以及涉及到的文件,大致了解了我们生成的源码或者xml文件,需要经过:ftl->freemarker process->java/xml
这样的流程,那么我们必须对freemarker有个简单的了解。
其实非常简单:
比如我们有个变量user=zhy;有个ftl文件内容:helloL${user}最后经过freemarker的输出结果即为 hello:zhy
if语法
<#if generateLayout>//生成layout文件#if>
看一眼就知道大概的意思了~有一定的编程经验,即使不知道这个叫freemarker,对于这些简单的语法还是能看懂的。
我们最后以Empty Activity模板的中的SimpleActivity为例:root/src/app_package/SimpleActivity.java.ftl
package ${packageName};import ${superClassFqcn};import android.os.Bundle;publicclass ${activityClass} extends ${superClass} {    @Override    protectedvoid onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);<#if generateLayout>        setContentView(R.layout.${layoutName});if>    }}

可以看到其内部包含很多变量,这些变量的值一般来源于用户输入和global.xml.ftl中预定义的值,经过recipe.xml.ftl中instantiate标签的处理,将变量换成实际的值,即可在我们的项目的指定位置,得到我们期望的Activity。
流程大致可用下图说明:

4、具体的模板实例
了解了基本的理论之后,下面我们可以通过一个实例来将上面的知识点整合。我们编写一个Activity模板叫做:ViewPagerWithTabActivity,用于创建一个携带TabLayout的ViewPager,效果如下:

当我们点击New->Activity->ViewPagerWithTabActivity就可能完成上面的Activity的创建,而避免了编写布局文件,引入design库,以及一些简单的编码。是不是感觉还是不错的,大家可以针对自己的需求,按照规范的格式随意定制模板。
建议大家copy一个现有的模板,再其基础上修改即可,比如本例是在Empty Activity基础之上修改的。
下面我们看上例的具体的实现。
4.1 template.xml的编写
通过上面的学习我们知道template.xml中可以定义我们创建面板的控件布局等,本例我们创建Activity的界面如下:

对应的template.xml如下:
经过前面的学习应该很好理解,每个parameter对应界面上的一个控件,控件的这个id最终可以得到用户输入值,后面会用于渲染ftl文件。
4.2用到的类
本例中最终需要生成Fragment和Activity,也就是说对应会有两个ftl文件分别用于最终生成这两个类。
root/src/app_package/MainActivity.java.ftl
package  ${packageName};import android.os.Bundle;import android.support.design.widget.TabLayout;import android.support.v4.app.Fragment;import android.support.v4.app.FragmentPagerAdapter;import android.support.v4.view.ViewPager;import android.support.v7.app.AppCompatActivity;import ${packageName}.fragment.SimpleFragment;publicclass ${activityClass} extends AppCompatActivity{    private TabLayout mTabLayout;    private ViewPager mViewPager;    privateint mTabCount = ${tabCount};    @OverrideprotectedvoidonCreate(Bundle savedInstanceState)    {        super.onCreate(savedInstanceState);        setContentView(R.layout.${activityLayoutName});        mTabLayout = (TabLayout) findViewById(R.id.id_tablayout);        mViewPager = (ViewPager) findViewById(R.id.id_viewpager);        mViewPager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager())        {            @Overridepublic Fragment getItem(int position)            {                returnnew SimpleFragment();            }            @OverridepublicintgetCount()            {                return mTabCount;            }            @Overridepublic CharSequence getPageTitle(int position)            {                return"Tab:" + position;            }        });        mTabLayout.setupWithViewPager(mViewPager);    }}

注意不是.java文件而是.ftl文件,可以看到上面的代码基础上和Java代码没什么区别,实际上就是Java代码,把可变的部分都换成了${变量名}的方式而已。
例如:类名是用户填写的,我们就使用${activityClass}替代,其他同理。root/src/app_package/SimpleFragment.java.ftl
package ${packageName}.fragment;import android.os.Bundle;import android.support.annotation.Nullable;import android.support.v4.app.Fragment;import android.view.Gravity;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.TextView;/** * Created by zhy on 16/6/6. */publicclassSimpleFragmentextendsFragment{@Nullable@Overridepublic View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)    {        TextView tv = new TextView(getActivity());        tv.setGravity(Gravity.CENTER);        tv.setTextSize(40);        tv.setText("just test");        return tv ;    }}
这个类更简单,除了package是动态的,其他都写好了,主要用于作为ViewPager的Fragment Item.

4.3用到的布局文件
root/res/layout/activity_main.xml.ftl
android.support.design.widget.TabLayout>LinearLayout>
发现和我们真正编写的Activity并无多大区别。
看完用到的类和布局文件的ftl,大家心里应该有个底了,这模板几乎就和我们平时写的Java类一样,只是根据用户在新建Actiivty界面所输入的参数进行替换一些变量或者做一些简单的操作而已。

4.4 recipe.xml.ftl的编写
除了template.xml还有globals.xml.ftl和recipe.xml.ftl,globals.xml.ftl中基本上没有修改任何内容就不介绍了。
recipe.xml.ftl中定义的东西比较关键,例如将ftl->java,copy、合并资源文件等。
内容较长,我们拆开描述。

引入依赖
<#include "../common/recipe_manifest.xml.ftl" /><#if !appCompat && !(hasDependency('com.android.support:support-v4'))>#if><#ifappCompat && !(hasDependency('com.android.support:appcompat-v7'))>#if><#if (buildApigte22) && appCompat && !(hasDependency('com.android.support:design'))>#if>//省略其他recipe>

本例依赖v4、v7、和design库,我们需要在这里定义引入;
你可能会问,这个引入的代码看起来挺复杂,你怎么知道这样写呢?
其实我也不知道怎么写,但是我可以打开IDE自带模板参考参考,copy过来就好了。
剩下的内容
 //省略依赖    recipe>
可以看到包含多个instantiate标签,该标签很明显是将我们内置的ftl转化为当前项目有中的java类。上例,转化了
${activityClass}.java
fragment/SimpleFragment.java"
/layout/${activityLayoutName}.xml

剩下是两个open标签,主要就是用于新建完成后,自动打开该文件。
本例新建完成后,Activity和其对应的布局文件都会自动打开。
恩,这里没用到merge标签,不过也很简单,假设你fragment上显示的文本,你可以定义到一个strings.xml里面,最后你需要将这个strings.xml合并到当前项目的strings.xml就可以使用merge标签(内置模板很多用了merge标签,参考下,抄一抄就搞定了)。ok,到这,我们整个模板的编写介绍就结束了。

5、总结
本文我们首先详细介绍了一个模板文件夹下各个文件以及其内部的标签的作用,然后通过一个具体的实例,来演示如何编写一个activity模板。如果你看的足够仔细,再花点时间动手,根据需求编写几个模板应该是不成问题的。
当然,文中一些细节并没有谈到,对于这些不要担心,你有什么需求,你就想哪个内置的模板好像有类似的需求,看它的实现,copy它的相关代码改一改就好了,没有必要去被各种文件的编写,这种东西copy修改就好了。
测试过程中,需要重启Android Studio,如果有问题,记得查看Event Log面板的信息

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,290评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,399评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,021评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,034评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,412评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,651评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,902评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,605评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,339评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,586评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,076评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,400评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,060评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,851评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,685评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,595评论 2 270

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,569评论 25 707
  • 由于项目用上了 mvp 架构,基本上一个页面就至少需要新创建6个类,分别是 model view presente...
    大空ts翼阅读 1,871评论 0 4
  • 到课间了, 真的好吵。 唉,我有什么办法呢? 在座位上, 等着上课。
    借酒消愁阅读 101评论 0 0
  • 其实画画就是由点线面这三个要素构成画面。点的集群汇聚成线,线的集群汇聚成面。 只要掌握了点线面的运用,构图不再是难...
    温迪画画阅读 1,555评论 4 9