android 自定义方形章,第二十四章 Android 自定义模板

本文介绍了如何在Android Studio中自定义Activity模板,包括模板结构、参数配置、FreeMarker语法和实践步骤。通过创建自定义模板,可以简化新项目中Activity的初始化过程,提升开发效率。
摘要由CSDN通过智能技术生成

1. 概述

在读鸿洋大神的博客的时候,看到了自定义模板,然后网上查了一些资料,自己了解学习一下,希望和大家分享。我们在创建项目的时候,看到这样一个界面,这里的初始Activity就是模板中的Activity。

dbd7aa565b9d

自定义模板

其实模板不只是只有activity 还包括图片自由 布局文件 fragment service 以及一个类都可以制作成模板。慢慢了解,这里只介绍Activity。Android Studio 自定义模板位于 \plugins\android\lib\templates\activities目录下。我们创建Activity的时候,也可以将自己定义的模板放在哪个目录下,然后创建Activity的时候,就可以出现创建的Activity。

dbd7aa565b9d

自定义模板

2. 自定义模板结构

学习自定义模板,我们需要参考IDE中的文件结构。Android Studio 中提供的最简单的Activity就是:Empty Activity 。

dbd7aa565b9d

自定义模板结构

从图中可以看到每个文件夹的对应的插件:

root文件夹 存放对应源码的ftl文件,以及资源文件

globals.xml.ftl

recipe.xml.ftl

template.xml

template_blank_activity.png 效果缩略图

2.1 template.xml 中parameter标签,主要用于提供参数

打开文件

format="5"

revision="5"

name="Empty Activity"

minApi="9"

minBuildApi="14"

description="Creates a new empty activity">

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" />

id="generateLayout"

name="Generate Layout File"

type="boolean"

default="true"

help="If true, a layout file will be generated" />

id="layoutName"

name="Layout Name"

type="string"

constraints="layout|unique|nonempty"

suggest="${activityToLayout(activityClass)}"

default="activity_main"

visibility="generateLayout"

help="The name of the layout to create for the activity" />

id="isLauncher"

name="Launcher Activity"

type="boolean"

default="false"

help="If true, this activity will have a CATEGORY_LAUNCHER intent filter, making it visible in the launcher" />

id="backwardsCompatibility"

name="Backwards Compatibility (AppCompat)"

type="boolean"

default="true"

help="If false, this activity base class will be Activity instead of AppCompatActivity" />

id="packageName"

name="Package name"

type="string"

constraints="package"

default="com.mycompany.myapp" />

template_blank_activity.png

中的name属性,对应新建Activity时显示的名字

对应New的类别为Activity

最外层的template的属性有模板名,描述,api版本,格式大小

format="5"

revision="5"

name="Empty Activity"

minApi="9"

minBuildApi="14"

description="Creates a new empty activity">

parameter标签, 看到这个界面,大部分属性都应该能才出来了,我们重点看parameter,界面上每一个红色框出来的部分都对应一个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" />

id :唯一标识,最终通过该属性的值,获取用户输入值(文本框内容,是否选中)

name:界面上的类似label的提示语

type : 输入值类型

constraints:填写值的约束

suggest:建议值,比如填写ActivityName的时候,会给出一个布局文件的建议值。

default:默认值

help:底部显示的提升语

dbd7aa565b9d

thumbs

这个是显示预览效果图,可以更换

最后指定两个引用文件

2.2 globals.xml.ftl 主要用于提供参数

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

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

${hasNoActionBar}的值为false。

2.3 recipe.xml.ftl 主要用于生成我们实际需要的代码,资源文件等。例如,利用参数+MainActivity.java.ftl -> MainActivity.java;其实就是利用参数将ftl中的变量进行替换。

//include语法,跟C中的include是一个意思,就是引用这个文件

//if语法,这里代表了假如id为generateLayout的值为true,则往if里面走

//open语法,这里指打开${escapeXmlAttribute(resOut)}/layout/目录下的${layoutName}.xml文件,其中${escapeXmlAttribute(resOut)}/输出的目录就是项目中的res目录

#if>

//instantiae语法,这里是将root/src/app_package/目录下的SimpleActivity.java.ftl解析成项目中${escapeXmlAttribute(srcOut)}/${activityClass}.java,其中${escapeXmlAttribute(srcOut)}/输出的目录就是项目中的src目录

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打开。

dbd7aa565b9d

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

2.4 Root文件夹

里面都是加了ftl的java文件和XML文件,所以我们用if来判断生成的方式。

两个步骤:

取值--->判断-->生成

xml文件中:

android:label="@string/app_name"

android:label="@string/title_${activityToLayout(activityClass)}"

#if>

>

#if>

获取值,包括设定值和全局变量:

设定值:activityClass获取,${activityClass}

全局变量:isNewProject,hasNoActionBar

if判断

设定值:activityClass获取,${activityClass}

全局变量:isNewProject,hasNoActionBar

3. FreeMark语法

上面我们已经基本了解模板生成的大致的流程以及涉及到的文件,大致了解了我们生成的源码或者xml文件,需要经过:

ftl->freemarker process->java/xml

比如我们有个变量user=zhy;

有个ftl文件内容:helloL${user}

最后经过freemarker的输出结果即为 hello:zhy

if语法

//生成layout文件

#if>

以SimpleActivity.java.ftl

\root\src\app_package

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

package ${packageName};

import ${superClassFqcn};

import android.os.Bundle;

import android.widget.TextView;

#if>

import ${applicationPackage}.R;

#if>

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

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

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

#if>

}

}

创建之后的java文件

package com.demo.mystyletemplate;

import android.support.v7.app.AppCompatActivity;

import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

}

}

流程大致可用如下图所示:

dbd7aa565b9d

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

4. 创建自己的模板

在创建自己模板的时候,我们最好从原来的Templates的activities中拷贝一个出来,然后在这个基础上更改。

dbd7aa565b9d

实现效果

4.1 globals.xml,这个文件可以直接拷贝过来,这里一个id simpleLayoutName,是Activity的layoutName

4.2 template.xml的编写

每个parameter对应界面上的一个控件,控件的这个id最终可以得到用户输入值,后面会用于渲染ftl文件

format="5"

revision="7"

name="Tab With ViewPager Activity"

minApi="7"

minBuildApi="14"

description="Creates a new activity with viewpager and tabs">

id="activityClass"

name="Activity Name"

type="string"

constraints="class|unique|nonempty"

suggest="${layoutToActivity(activityLayoutName)}"

default="MainActivity"

help="The name of the activity class to create" />

id="activityLayoutName"

name="Layout Name"

type="string"

constraints="layout|unique|nonempty"

suggest="${activityToLayout(activityClass)}"

default="activity_main"

help="The name of the layout to create for the activity" />

id="tabCount"

name="Tab Count"

type="string"

constraints="nonempty"

default="4"

help="The count of tabs for ViewPager" />

id="isLauncher"

name="Launcher Activity"

type="boolean"

default="false"

help="If true, this activity will have a CATEGORY_LAUNCHER intent filter, making it visible in the launcher" />

id="packageName"

name="Package name"

type="string"

constraints="package"

default="com.mycompany.myapp" />

template_tab_with_vp_activity.png

4.3 recipe.xml 中定义的东西比较关键,例如将ftl->java,copy、合并资源文件等。

引入依赖

#if>

#if>

#if>

//省略其他

这里的这部分直接拷贝过来,我们导入support-v4,-v7和design包

···

to="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />

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

to="${escapeXmlAttribute(srcOut)}/fragment/SimpleFragment.java" />

to="${escapeXmlAttribute(srcOut)}/fragment/Fragment1.java" />

to="${escapeXmlAttribute(srcOut)}/fragment/Fragment2.java" />

to="${escapeXmlAttribute(srcOut)}/fragment/Fragment3.java" />

to="${escapeXmlAttribute(srcOut)}/fragment/Fragment4.java" />

包含多个instantiate标签,该标签很明显是将我们内置的ftl转化为当前项目有中的java类。

4.4 接下来是编写类

root/src/app_package/MainActivity.java.ftl

注意不是.java文件而是.ftl文件,可以看到上面的代码基础上和Java代码没什么区别,实际上就是Java代码,把可变的部分都换成了${变量名}的方式而已。

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) {

switch (position){

case 0:

return new Fragment1();

case 1:

return new Fragment2();

case 2:

return new Fragment2();

case 3:

return new Fragment3();

}

return null;

}

@Override

public int getCount() {

return mTabCount;

}

@Override

public CharSequence getPageTitle(int position) {

switch (position){

case 0:

return "教育";

case 1:

return "科技";

case 2:

return "文化";

case 3:

return "军事";

}

return null;

}

});

mTabLayout.setupWithViewPager(mViewPager);

}

}

例如:类名是用户填写的,我们就使用${activityClass}替代,其他同理。

相应的编写四个fragment.

package ${packageName}.fragment;

import android.os.Bundle;

import android.support.annotation.Nullable;

import android.support.v4.app.Fragment;

import android.view.LayoutInflater;

import android.view.View;

import android.view.ViewGroup;

public class Fragment1 extends Fragment {

@Nullable

@Override

public View onCreateView(LayoutInflater inflater,

@Nullable ViewGroup container,

@Nullable Bundle savedInstanceState) {

return inflater.inflate(R.layout.${layoutName}, container, false);

}

}

4.5 编写的布局文件

root/res/layout/activity_main.xml.ftl

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:id="@+id/id_tablayout"

android:layout_width="match_parent"

android:layout_height="wrap_content">

android:id="@+id/id_viewpager"

android:layout_width="match_parent"

android:layout_height="0dp"

android:layout_weight="1"

/>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值