本文已授權(quán)微信公眾號:鴻洋(hongyangAndroid)在微信公眾號平臺原創(chuàng)首發(fā)。
轉(zhuǎn)載請標明出處:
http://blog.csdn.net/lmj623565791/article/details/51635533;
本文出自:【張鴻洋的博客】
上一篇文章,已經(jīng)初步對Android Studio的模板有了初步的介紹與使用,以及一些開源模板的推薦:
本文將對如何編寫Template,進行詳細的介紹(以activity摸版為例)。
學習編寫模板最好的方式呢,就是參考IDE中已經(jīng)提供的最簡單的模板,那么在android Studio中最簡單的activity模板就是:Empty Activity
了,我們打開該模板文件,首先對文件結(jié)構(gòu)有個直觀的了解,如圖:
可以看到每個插件對應(yīng)一個文件夾,文件夾包含
下面我們逐一對上述每個文件的作用就行介紹。
打開該文件,發(fā)現(xiàn)其主要內(nèi)容如下:
<?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
屬性,對應(yīng)新建Activity
時顯示的名字<category>
對應(yīng)New的類別為Activity剩下的,對應(yīng)我們AndroidStudio新建Empty Activity
的界面就非常好理解了,如圖:
看到這個界面,大部分屬性都應(yīng)該能才出來了,我們重點看parameter,界面上每一個紫色框出來的部分都對應(yīng)一個parameter
,部分屬性介紹:
這個部分對應(yīng)界面還是非常好理解的,大家可以簡單的修改一些字符串,或者添加一個<parameter>
,重啟AS,看看效果。
template.xml的最下面的部分引入了globals.xml.ftl
和recipe.xml.ftl
。
這兩個我們會詳細介紹。
<?xml version="1.0"?><globals> <global id="hasNoActionBar" type="boolean" value="false" /> <#include "../common/common_globals.xml.ftl" /></globals>
通過名稱可以猜到它是用于定義一些全局的變量,可以看到其內(nèi)部有<global>
標簽,分別定義id,type,默認值。
同理,我們可以通過id的值訪問到該值,例如:
${hasNoActionBar}
的值為false。
<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中比較重要的幾個標簽都列出來了:
ftl->freemarker process -> java
。在介紹instantiate時,涉及到了freemarker,不可避免的需要對它進行簡單的介紹。
目前我們已經(jīng)基本了解了一個模板其內(nèi)部的文件結(jié)構(gòu)了,以及每個文件大致包含的東西,我們簡單做個總結(jié):
那么整體的關(guān)系類似下圖:
圖片來源:http://www.slideshare.net/murphonic/custom-android-code-templates-15537501
上面我們已經(jīng)基本了解模板生成的大致的流程以及涉及到的文件,大致了解了我們生成的源碼或者xml文件,需要經(jīng)過:
ftl->freemarker process->java/xml
這樣的流程,那么我們必須對freemarker有個簡單的了解。
其實非常簡單:
比如我們有個變量user=zhy;有個ftl文件內(nèi)容:helloL${user}最后經(jīng)過freemarker的輸出結(jié)果即為 hello:zhy
<#if generateLayout> //生成layout文件</#if>
看一眼就知道大概的意思了~有一定的編程經(jīng)驗,即使不知道這個叫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> }}
可以看到其內(nèi)部包含很多變量,這些變量的值一般來源于用戶輸入和global.xml.ftl中預定義的值,經(jīng)過recipe.xml.ftl中instantiate標簽的處理,將變量換成實際的值,即可在我們的項目的指定位置,得到我們期望的Activity。
流程大致可用下圖說明:
圖片來源:http://www.slideshare.net/murphonic/custom-android-code-templates-15537501
看到這,最起碼理解了,當我們選擇創(chuàng)建不同的Activity類型,最終得到的不同的效果其中的原理原來在這。
了解了基本的理論之后,下面我們可以通過一個實例來將上面的知識點整合。
我們編寫一個Activity模板叫做:ViewPagerWithTabActivity
,用于創(chuàng)建一個攜帶TabLayout的ViewPager,效果如下:
當我們點擊New->Activity->ViewPagerWithTabActivity 就可能完成上面的Activity的創(chuàng)建,而避免了編寫布局文件,引入design庫,以及一些簡單的編碼。
是不是感覺還是不錯的,大家可以針對自己的需求,按照規(guī)范的格式隨意定制模板。
建議大家copy一個現(xiàn)有的模板,再其基礎(chǔ)上修改即可,比如本例是在Empty Activity基礎(chǔ)之上修改的。
下面我們看上例的具體的實現(xiàn)。
通過上面的學習我們知道template.xml中可以定義我們創(chuàng)建面板的控件布局等,本例我們創(chuàng)建Activity的界面如下:
對應(yīng)的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>
經(jīng)過前面的學習應(yīng)該很好理解,每個parameter對應(yīng)界面上的一個控件,控件的這個id最終可以得到用戶輸入值,后面會用于渲染ftl文件。
本例中最終需要生成Fragment和Activity,也就是說對應(yīng)會有兩個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文件,可以看到上面的代碼基礎(chǔ)上和Java代碼沒什么區(qū)別,實際上就是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是動態(tài)的,其他都寫好了,主要用于作為ViewPager的Fragment Item.
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>
發(fā)現(xiàn)和我們真正編寫的Activity并無多大區(qū)別。
看完用到的類和布局文件的ftl,大家心里應(yīng)該有個底了,這模板幾乎就和我們平時寫的Java類一樣,只是根據(jù)用戶在新建Actiivty界面所輸入的參數(shù)進行替換一些變量或者做一些簡單的操作而已。
除了template.xml還有g(shù)lobals.xml.ftl和recipe.xml.ftl,globals.xml.ftl中基本上沒有修改任何內(nèi)容就不介紹了。
recipe.xml.ftl中定義的東西比較關(guān)鍵,例如將ftl->java,copy、合并資源文件等。
內(nèi)容較長,我們拆開描述。
<?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標簽,該標簽很明顯是將我們內(nèi)置的ftl轉(zhuǎn)化為當前項目有中的java類。
上例,轉(zhuǎn)化了
${activityClass}.java
fragment/SimpleFragment.java"
/layout/${activityLayoutName}.xml
剩下是兩個open標簽,主要就是用于新建完成后,自動打開該文件。
本例新建完成后,Activity和其對應(yīng)的布局文件都會自動打開。
恩,這里沒用到merge
標簽,不過也很簡單,假設(shè)你fragment上顯示的文本,你可以定義到一個strings.xml里面,最后你需要將這個strings.xml合并到當前項目的strings.xml就可以使用merge標簽(內(nèi)置模板很多用了merge標簽,參考下,抄一抄就搞定了)。
ok,到這,我們整個模板的編寫介紹就結(jié)束了。
本文我們首先詳細介紹了一個模板文件夾下各個文件以及其內(nèi)部的標簽的作用,然后通過一個具體的實例,來演示如何編寫一個activity模板。
如果你看的足夠仔細,再花點時間動手,根據(jù)需求編寫幾個模板應(yīng)該是不成問題的。
當然,文中一些細節(jié)并沒有談到,對于這些不要擔心,你有什么需求,你就想哪個內(nèi)置的模板好像有類似的需求,看它的實現(xiàn),copy它的相關(guān)代碼改一改就好了,沒有必要去被各種文件的編寫,這種東西copy修改就好了。
測試過程中,需要重啟Android Studio,如果有問題,記得查看Event Log面板的信息。
此外,模板這個東西我覺得最好的集大家的力量,所以我在github建立了一個組織倉庫,https://github.com/WanAndroid/AndroidStudioTemplates,如果你對模板有興趣或者想要將自己的模板文件與他人共享,可以加入這個組織,然后分享你的代碼,本文的例子也在其中。
歡迎關(guān)注我的微博:
http://weibo.com/u/3165018720
群號: 497438697 ,歡迎入群
微信公眾號:hongyangAndroid
(歡迎關(guān)注,不要錯過每一篇干貨,支持投稿)