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

本文已授权微信公众号:鸿洋(hongyangAndroid)在微信公众号平台原创首发。

转载请标明出处:

http://blog.csdn.net/lmj623565791/article/details/51635533

本文出自:【张鸿洋的博客】

1、概述

上一篇文章,已经初步对Android Studio的模板有了初步的介绍与使用,以及一些开源模板的推荐:

本文将对如何编写Template,进行详细的介绍(以activity摸版为例)。

2、模板的文件结构

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

可以看到每个插件对应一个文件夹,文件夹包含

  • template.xml
  • globals.xml.ftl
  • recipe.xml.ftl
  • root文件夹 存放对应源码的ftl文件,以及资源文件
  • 效果缩略图

下面我们逐一对上述每个文件的作用就行介绍。

2.1 template.xml

打开该文件,发现其主要内容如下:

<?xml version="1.0"?>
<template
    name="Empty Activity"
    minApi="7"
    minBuildApi="14"
    description="Creates a new empty activity">
    <category value="Activity" />
    <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" />

    <thumbs>
        <!-- default thumbnail is required -->
        <thumb>template_blank_activity.png</thumb>
    </thumbs>

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

其中

  • <template>中的name属性,对应新建Activity时显示的名字
  • <category>对应New的类别为Activity

剩下的,对应我们AndroidStudio新建Empty Activity的界面就非常好理解了,如图:

看到这个界面,大部分属性都应该能才出来了,我们重点看parameter,界面上每一个紫色框出来的部分都对应一个parameter,部分属性介绍:

  • id :唯一标识,最终通过该属性的值,获取用户输入值(文本框内容,是否选中)
  • name:界面上的类似label的提示语
  • type : 输入值类型
  • constraints:填写值的约束
  • suggest:建议值,比如填写ActivityName的时候,会给出一个布局文件的建议值。
  • default:默认值
  • help:底部显示的提升语

这个部分对应界面还是非常好理解的,大家可以简单的修改一些字符串,或者添加一个<parameter>,重启AS,看看效果。

template.xml的最下面的部分引入了globals.xml.ftlrecipe.xml.ftl

这两个我们会详细介绍。

2.2 globals.xml.ftl

<?xml version="1.0"?>
<globals>
    <global id="hasNoActionBar" type="boolean" value="false" />
    <#include "../common/common_globals.xml.ftl" />
</globals>

通过名称可以猜到它是用于定义一些全局的变量,可以看到其内部有<global>标签,分别定义id,type,默认值。

同理,我们可以通过id的值访问到该值,例如:

${hasNoActionBar}的值为false。

2.3 recipe.xml.ftl

<recipe>

<copy from="root/res/drawable-hdpi"
            to="${escapeXmlAttribute(resOut)}/drawable-hdpi" />

<merge from="root/${resIn}/values/strings.xml.ftl"
             to="${escapeXmlAttribute(resOut)}/values/strings.xml" />

<instantiate from="root/src/app_package/SimpleActivity.java.ftl"
               to="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />

<open file="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />

</recipe>

为了介绍,我将该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中的变量进行替换。

那么整体的关系类似下图:

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

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;

public class ${activityClass} extends ${superClass} {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
<#if generateLayout>
        setContentView(R.layout.${layoutName});
</#if>
    }
}

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

流程大致可用下图说明:

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

看到这,最起码理解了,当我们选择创建不同的Activity类型,最终得到的不同的效果其中的原理原来在这。

4、具体的模板实例

了解了基本的理论之后,下面我们可以通过一个实例来将上面的知识点整合。

我们编写一个Activity模板叫做:ViewPagerWithTabActivity,用于创建一个携带TabLayout的ViewPager,效果如下:

当我们点击New->Activity->ViewPagerWithTabActivity 就可能完成上面的Activity的创建,而避免了编写布局文件,引入design库,以及一些简单的编码。

是不是感觉还是不错的,大家可以针对自己的需求,按照规范的格式随意定制模板。

建议大家copy一个现有的模板,再其基础上修改即可,比如本例是在Empty Activity基础之上修改的。

下面我们看上例的具体的实现。

4.1 template.xml的编写

通过上面的学习我们知道template.xml中可以定义我们创建面板的控件布局等,本例我们创建Activity的界面如下:

对应的template.xml如下:

<?xml version="1.0"?>
<template>

    <category value="Activity" />
    <formfactor value="Mobile" />

    <parameter id="activityClass"/>
    <parameter id="activityLayoutName"/>
    <parameter id="tabCount"/>
    <parameter id="isLauncher"/>

    <parameter id="packageName"/>
    <!-- 128x128 thumbnails relative to template.xml -->
    <thumbs>
        <!-- default thumbnail is required -->
        <thumb>template_vp_with_tab_activity.png</thumb>
    </thumbs>

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

</template>

经过前面的学习应该很好理解,每个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;

public class ${activityClass} extends AppCompatActivity
{
    private TabLayout mTabLayout;
    private ViewPager mViewPager;
    private int mTabCount = ${tabCount};

    @Override
    protected void onCreate(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())
        {
            @Override
            public Fragment getItem(int position)
            {
                return new SimpleFragment();
            }
            @Override
            public int getCount()
            {
                return mTabCount;
            }
            @Override
            public 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.
 */
public class SimpleFragment extends Fragment
{
    @Nullable
    @Override
    public 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
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    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"
    android:orientation="vertical"
    tools:context=" ${packageName}.${activityClass}">

    <android.support.design.widget.TabLayout
        android:id="@+id/id_tablayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    </android.support.design.widget.TabLayout>

    <android.support.v4.view.ViewPager
        android:id="@+id/id_viewpager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
    />
</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、合并资源文件等。

内容较长,我们拆开描述。

  • 引入依赖
<?xml version="1.0"?>
<recipe>
    <#include "../common/recipe_manifest.xml.ftl" />

    <#if !appCompat && !(hasDependency(‘com.android.support:support-v4‘))>
            <dependency mavenUrl="com.android.support:support-v4:${buildApi}.+"/>
     </#if>

    <#if appCompat && !(hasDependency(‘com.android.support:appcompat-v7‘))>
           <dependency mavenUrl="com.android.support:appcompat-v7:${buildApi}.+" />
    </#if>

    <#if (buildApi gte 22) && appCompat && !(hasDependency(‘com.android.support:design‘))>
        <dependency mavenUrl="com.android.support:design:${buildApi}.+" />
    </#if>

//省略其他
</recipe>

本例依赖v4、v7、和design库,我们需要在这里定义引入;

你可能会问,这个引入的代码看起来挺复杂,你怎么知道这样写呢?

其实我也不知道怎么写,但是我可以打开IDE自带模板参考参考,copy过来就好了。

  • 剩下的内容
<?xml version="1.0"?>
<recipe>
  //省略依赖
    <instantiate from="root/src/app_package/MainActivity.java.ftl"
        to="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />

    <instantiate from="root/src/app_package/SimpleFragment.java.ftl"
        to="${escapeXmlAttribute(srcOut)}/fragment/SimpleFragment.java" />   

    <instantiate from="root/res/layout/activity_main.xml.ftl"
        to="${escapeXmlAttribute(resOut)}/layout/${activityLayoutName}.xml" />

    <open file="${escapeXmlAttribute(resOut)}/layout/${activityLayoutName}.xml"/>        

    <open file="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />
</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面板的信息。

此外,模板这个东西我觉得最好的集大家的力量,所以我在github建立了一个组织仓库,https://github.com/WanAndroid/AndroidStudioTemplates,如果你对模板有兴趣或者想要将自己的模板文件与他人共享,可以加入这个组织,然后分享你的代码,本文的例子也在其中。



欢迎关注我的微博:

http://weibo.com/u/3165018720


群号: 497438697 ,欢迎入群

微信公众号:hongyangAndroid

(欢迎关注,不要错过每一篇干货,支持投稿)

参考链接

时间: 2024-10-11 04:29:53

Android Studio自定义模板 写页面竟然可以如此轻松的相关文章

Android Studio混淆模板及常用第三方混淆(看了都说好)

首先要在build.gradle中开启混淆,也就是minifyEnabled true,我用的build.gradle具体如下所示: def releaseTime() { return new Date().format("yyyy.MM.dd", TimeZone.getTimeZone("UTC")) } android { .... buildTypes { release { // 混淆 minifyEnabled true // Zipalign优化 z

Android Studio 自定义debug签名文件keystore

Android Studio 自定义debug签名文件keystore

Android Studio布局模板一之FloatButton和Toolbar

从今天开始就学Android Studio 推荐模板 ,今天发的本应该昨天发的,可同学刚睡醒连变压插座都没查就直接插上了电饭锅,然后过载了停了一天电这大星期天的宿舍没电.... 今天下午来电了,瞅了瞅加上昨天看的皮毛,就按照模板敲出来了,又加了点东西,还不错. 忘了一件事添加上    添加 compile 'com.android.support:design:23.2.1' 代码部分主要分几大块: 1.content_layout.xml  这里面就是主要的显示界面了,就是新建后的Hello

Android Studio自定义注释模板

博主是从Eclipse转来Android Studio 的,总体来说,Android Studio是比Eclipse优秀和智能许多.但是对于我们这些用习惯了Eclipse的开发者来说,或多或少还是一些不适应的地方.快捷键什么的额.但是,今天我们不是要说快捷键,我们要说一说自定义注释模板.用过Eclipse的开发者应该都喜欢在文件的头部或者其他地方加上一些我们自己的信息,当然,这些信息都是我们自定义编辑进去的.那么,转到Android Studio后,我们还能不能自定义注释模板呢?答案是肯定的.下

Android Studio自定义注释模板及生成JavaDoc

刚开始学习Android,使用了Android Studio IDE.为了将来生产JavaDoc,学习一下如何自定义注释模板. . 自定义注释模板 1. 通过 File –>Settings 或者 HotKey Ctrl + Alt + S 打开 Settings面板,如下图 2. 点击 Editor下的Live Templates.如下图,显示的是Android Studio为开发者提供的默认模板及快捷键. 3. 为了自定义注释模板,点击右上角的“+”,选择Template Group创建一个

Android Studio 注释模板

1.主题. File->Settings, 搜索Theme, 点开Appearance, 在右边面板的Theme中选择Darcula. 2.注释快捷键. File->Settings, 搜索keymap,点开Keymap,在右边面板上搜索,找到Other->Fix doc comment,双击,选择Add Keyboard Shortcut,按下你的快捷键.Eclipse中是alt + shift +J,但是在这里会冲突,所以我用了比较近的快捷键组合:alt + shift + K.在修

Android studio 自定义打包APK名称

Android Studio打包应用默认生成的apk名称是:app-release.apk .如果我们要让生成的apk名跟我们版本包名有联系的话,那我们就要自定义生成的apk名了,要怎么做呢. 我们只需要再build.gradle(Module:app)文件下android{ }中添加: 1 android.applicationVariants.all { variant -> 2 variant.outputs.each { output -> 3 output.outputFile =

android studio学习---模板

Android Studio还为开发人员提供多种模板选项,从而大大提升开发速度.这些模板能自动创建Activity以及必要的XML文件.大家还可以利用这些模板创建出较为基础的Android应用程序,并将其运行在实体设备或者模拟器当中. 在Android Studio当中,我们可以在创建新的Activity时一同创建出对应模板.右键点击窗口左侧项目浏览器中的“package name”并在菜单中选择“New”,而后从选项列表中点选“Activity”.Android Studio随后会为开发者列出

Android studio 自定义打包apk名

Android Studio打包应用默认生成的apk名称是:app-release.apk .如果我们要让生成的apk名跟我们版本包名有联系的话,那我们就要自定义生成的apk名了 需要在build.gradle(Module:app)文件下android{ }中添加: // apk name def android.applicationVariants.all { variant -> variant.outputs.each { output -> output.outputFile =