studio模板,一鍵生成多個類,mvp黨福利

  人類進步的根源是什麼?是懶惰,是的,沒有錯,就是懶惰,正是當你想偷懶時,你纔會去尋找更便捷的方法搞定一件事。寫代碼也是同樣的,不想偷懶的程序猿不是好程序猿,下面咱們來看看如何「偷懶」。php

  首先,聲明一下,本文的做用純屬拋磚引玉,並不會太詳細的介紹具體使用方法,僅僅介紹大概使用思路及踩坑日記。雖然本文以mvp爲例,可是本文所講的內容不侷限於此,基本上全部的模板代碼,你均可以生成模板,方便後面使用。java

  使用mvp模式開發安卓項目的人都知道,建立一個activity一般須要建立包含接口在內的5個類,寫一兩個界面還好,若是真是寫完整個項目,光是建立這些類都讓人心煩,那麼有沒有快捷的方法呢?固然是有的,最簡單的方法就是使用studio 自帶的file template。android


下面來寫一個簡單的Preseter類模板:

#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end
#parse("File Header.java")
public class ${NAME}Presenter extends StyleActivityPresenter<I${NAME}View,I${NAME}Model>{
    @Override
    protected void initPresenter (Bundle savedInstanceState) {

    }
}複製代碼

使用效果:android-studio

public class TestPresenter extends StyleActivityPresenter<ITestView, ITestModel> {
    @Override
    protected void initPresenter (Bundle savedInstanceState) {

    }
}複製代碼

  能夠看出,使用上仍是很是簡單的,${NAME}就是你建立文件時輸入的名字,其餘的相信不用解釋你們都看的懂。app

  上面的模板功能雖然已經可以很方便的讓我建立一個類而不用去寫過多的重複代碼了,可是依然不夠好用,由於上面說了,mvp模式一般包含5個類,還有佈局文件,還有activity註冊代碼,這些能夠說是每次建立activity的必須代碼,而若是僅僅使用上面的file template功能,依然須要屢次在不一樣包下建立文件,還有沒有更偷懶的方法呢?固然有,就是studio強大的activity模板功能了。
  其實這個功能,你們常常都在使用,只是不少人並無注意罷了,就好比咱們新建項目時:maven

  這其實就是studio自帶的activity模板,咱們知道當咱們選中某個類型的模板後,生成了項目以後,項目中就會有相應的java代碼和佈局,而且他會幫你在manifest註冊好這個activity。ide

下面咱們須要的也就是自定義這個功能,讓他實現輸入一個類名後在你指定的包下面自動生成5個mvp相關類和佈局文件已經manifest註冊。佈局

首先,咱們須要知道,系統自帶的模板位置:XXX\android-studio\plugins\android\lib\templates\activities
這個目錄下就是上面咱們看到的全部activity模板的文件目錄,先來簡單介紹一下模板的目錄下幾個重要的文件及其做用,咱們以LoginActivity這個模板爲例:測試

root:這個目錄下面放的是咱們咱們的代碼模板,和file template代碼相似,可是有必定區別。我喜歡叫他們模板輸出原型。ui

globals.xml:這個文件是用來配置某些特殊屬性的,好比是不是啓動頁面之類的屬性。

recipe.xml:這個文件主要是配置須要生成哪些文件,用哪一個模板生成,生成後要輸出到哪一個目錄。

template.xml這個文件主要是用來定義咱們的一些文件名和包名之類的變量屬性,看看LoginActivity的配置界面效果,相信你們就懂這個文件的做用了:

template_login_activity.png:這個是上面圖中那個界面示意圖,一般不須要管它,固然你也能夠放一張本身的圖,替換掉。

下面說說怎麼自定義本身的模板,(文章開始已經說了,本文並不會詳細介紹如何進行自定義模板<我能說是本身也是才學這個東西嗎?>,這裏直接介紹我本身自定義時遇到的坑,和一些比較重要的注意事項):

  • 首先,建議你們從最簡單的模板開始嘗試,不要一開始就徹底以本身的mvp類去寫,等熟悉了相關屬性和規則後再去寫mvp相關的模板,這是由於模板這個東西不是咱們的項目代碼,若是你配置錯了,使用時雖然會有報錯提示,可是並不許確,因此若是你一次性寫太多東西的話,排查錯誤時很慢。
  • 其次,建議直接先複製一份系統的模板代碼好比(LoginActivity模板),而後在此基礎上修改,不要本身去建立每一個文件,理由和第一條相似,容易出錯。

而後開始咱們的模板建立之旅:

1.建立Demo項目用於測試

很是簡單,只包含了一個默認的自動生成的MainActivity類

2.複製LoginActivity模板

在咱們的對應目錄XXX\android-studio\plugins\android\lib\templates\activities中複製LoginActivity文件夾並重命名爲MVPTestActivity,接着進入咱們複製的文件夾,打開template.xml這個文件,改掉name的值,最好是和你的目錄名保持一致,咱們這裏就叫MVPTestActivity,其餘幾個屬性能夠按照本身的須要進行修改。

<template format="5" revision="6" name="Login Activity"-->此屬性更名爲MVPTestActivity
    description="Creates a new login activity, allowing users to optionally sign in with Google+ or enter an email address and password to log in to or register with your application."
    minApi="8"
    minBuildApi="14">複製代碼

parameter,這個標籤是咱們配置界面上的字段,我這裏只截圖兩個重要字段:

<parameter id="activityClass" name="Activity Name" type="string" constraints="class|unique|nonempty" default="LoginActivity" help="The name of the activity class to create" />

    <parameter id="layoutName" name="Layout Name" type="string" constraints="layout|unique|nonempty" suggest="${activityToLayout(activityClass)}" default="activity_login" help="The name of the layout to create for the activity" />複製代碼

兩張圖一塊兒看的話就很好理解了,id爲activityClass的這個標籤就是咱們的類名,id爲layoutName就是咱們的佈局名,id這個字段是咱們在其餘配置文件中引用name的查找依據,type天然就是類型,constraints是一些輸入限制信息,default固然就是默認實如今配置界面上的值了,每一個屬性的具體用法,你們本身搜索一下相關博文,這裏不是本文的重點。

咱們這裏先開始寫最簡單的,那麼咱們確定是不須要Title之類的屬性的,因此咱們先把不須要顯示或配置的字段註釋掉。

<!-- <parameter id="activityTitle" name="Title" type="string" constraints="nonempty" default="Sign in" help="The name of the activity." /> -->
    ...複製代碼

其餘的一些字段咱們也能夠根據咱們的須要稍做調整,這樣這個文件基本就修改完成了。

recipe.xml,打開此文件,咱們能夠看到

<?xml version="1.0"?>
<#import "root://activities/common/kotlin_macros.ftl" as kt>
<recipe>
   <#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>

    <#include "../common/recipe_theme.xml.ftl" />

    <merge from="root/AndroidManifest.xml.ftl" to="${escapeXmlAttribute(manifestOut)}/AndroidManifest.xml" />

    <merge from="root/res/values/dimens.xml" to="${escapeXmlAttribute(resOut)}/values/dimens.xml" />

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

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

<#if generateKotlin>
    <@kt.addAllKotlinDependencies />
    <instantiate from="root/src/app_package/LoginActivity.kt.ftl" to="${escapeXmlAttribute(srcOut)}/${activityClass}.kt" />
    <open file="${escapeXmlAttribute(srcOut)}/${activityClass}.kt" />
<#else>
    <instantiate from="root/src/app_package/LoginActivity.java.ftl" to="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />
    <open file="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />
</#if>

</recipe>複製代碼

是否是看的一頭霧水,其實不難,ifelse相信不用過多解釋,咱們重點講講其餘幾個比較重要的標籤的做用。

  • merge 顧名思義就是合併的意思,這個主要是用在資源文件或者manifest文件,由於咱們一般是須要把咱們新建的xml文件和項目中的進行合併,這裏稍微講解一下manifest的合併,由於其餘的資源文件合併都很簡單,就不說了。

打開MVPTestActivity\root目錄下的manifest文件,主要代碼以下:

<activity android:name=".${activityClass}"
            <#if isNewProject>
            android:label="@string/app_name"
            <#else>
            android:label="@string/title_${simpleName}"
            </#if>
            <#if hasNoActionBar>
            android:theme="@style/${themeNameNoActionBar}"
            <#elseif !(hasApplicationTheme!false)>
            android:theme="@style/${themeName}"
            </#if>
            <#if buildApi gte 16 && parentActivityClass != "">android:parentActivityName="${parentActivityClass}"</#if>>
            <#if parentActivityClass != "">
            <meta-data android:name="android.support.PARENT_ACTIVITY"
                android:value="${parentActivityClass}" />
            </#if>
            <@manifestMacros.commonActivityBody />
        </activity>複製代碼

基本上不是很難理解,相似於這種${}代碼,都是對其餘文件中屬性的引用,下面咱們去掉咱們不須要的屬性,修改後:

<activity android:name=".${activityClass}"
            <#if isNewProject>
            android:label="@string/app_name"
            </#if>
            <#if hasNoActionBar>
            android:theme="@style/${themeNameNoActionBar}"
            <#elseif !(hasApplicationTheme!false)>
            android:theme="@style/${themeName}"
            </#if>>

            <@manifestMacros.commonActivityBody />
        </activity>複製代碼
  • instantiate 就是建立文件
  • open file固然就是在咱們生成好類後,打開相關類

這裏重點說說from和to這兩個屬性,看名字你們應該都能猜出一點了,from就是指的咱們的模板文件路徑,to固然就是咱們生成文件的路徑,以簡單的佈局文件爲例,能夠看到一個instantiate標籤內容以下:

<instantiate from="root/res/layout/activity_login.xml.ftl" to="${escapeXmlAttribute(resOut)}/layout/${layoutName}.xml" />複製代碼

其中root/res/layout/activity_login.xml.ftl這個路徑,明顯就是咱們當前模板目錄下面的文件,而to的屬性,咱們也能夠理解下,${escapeXmlAttribute(resOut)}多半就是指的當前項目的res資源主目錄,而後${layoutName}明顯就是引用的template.xml中配置的,用戶輸入的layoutName這個String。
明白了這些屬性後,咱們作出以下修改:

  • 刪掉MVPTestActivity\root\res目錄下的values文件夾
  • 註釋掉recipe.xml文件中兩個關於資源合併的標籤(不去掉會報空指針,由於咱們已經刪掉了資源模板)
  • 最後修改MVPTestActivity\root\src\app_package目錄下的LoginActivity.java文件(其實還須要同步修改LoginActivity.kt,不過由於我暫時沒有使用kotlin,因此沒作它的適配),這個文件內容太多,爲了方便查看,咱們簡化成以下代碼:
package ${packageName};

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
<#if applicationPackage??>
import ${applicationPackage}.R;
</#if>
/**
 * A login screen that offers login via email/password.
 */
public class ${activityClass} extends AppCompatActivity {

    @Override
    protected void onCreate (Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.${layoutName});
    }

}複製代碼

好了,到此爲止,其實咱們已經完成了一些最簡單的修改,下面咱們來看看運行效果,保存每一個文件後,重啓studio(一開始寫的時候千萬不要想一次改完,否則多半會由於失敗而放棄這個):

能夠看到咱們修改了模板介紹,修改了默認的activity名字,去掉了title和parent兩個輸入框,讓咱們點擊完成試一下效果:

TestActivity和activity_test都是咱們經過模板自動生成的文件,而且mainfest中已經註冊了這個activity了,具體截圖就不放了,你們能夠本身嘗試。

上面的步驟已經算是完成了模板的最基本使用了,可是離咱們的要求還差點,由於咱們想要的是生成多個文件,而且多個文件有可能再也不同一個包下,那麼咱們怎麼實現呢?

首先,假如咱們的項目結構以下:

那麼我如今須要的就是:

  • 在presenter包下建立一個TestActivityPresenter類
  • 在model包下建立一個TestActivityModel
  • 在view包下建立一個TestActivityView
  • 在port包下建立一個ITestActivityModel接口和ITestActivityView接口
  • 而且TestActivityView應該實現ITestActivityView接口,TestActivityModel應該實現ITestActivityModel接口
  • 生成對應佈局文件,而且註冊activity

最後一條,基本上不用再說了,咱們開始的改動已經知足了,重點說說下面幾條怎麼實現,主要是說說怎麼在不一樣目錄下生成對應文件。

回到咱們的recipe.xml文件中,咱們注意看這裏

<instantiate from="root/src/app_package/LoginActivity.java.ftl" to="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />複製代碼

這裏其實就是用來生成咱們剛剛那個TestActivity的配置代碼,明顯的咱們想多生成幾個文件的話,只須要多寫幾個這種標籤就好,好比像下面這樣:

<!--IView -->
    <instantiate from="root/src/app_package/LoginActivity.java.ftl" to="${escapeXmlAttribute(srcOut)}/I${activityClass}View.java" />
  <!--View -->
    <instantiate from="root/src/app_package/LoginActivity.java.ftl" to="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />
  <!--IModel -->
    <instantiate from="root/src/app_package/LoginActivity.java.ftl" to="${escapeXmlAttribute(srcOut)}/I${activityClass}Model.java" />   
  <!--Model -->  
    <instantiate from="root/src/app_package/LoginActivity.java.ftl" to="${escapeXmlAttribute(srcOut)}/${activityClass}Model.java" />    
  <!--Presenter -->
    <instantiate from="root/src/app_package/LoginActivity.java.ftl" to="${escapeXmlAttribute(srcOut)}/${activityClass}Presenter.java" />                                    
    <open file="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />複製代碼

注意看to的值的最後一點,我採用拼接的方式規定了每一個文件的名字格式,這樣全部mvp下的類名都是符合必定命名規範的,固然不必定和個人同樣,可是你必定要有本身的格式,不要隨意取名字,這是基礎,不過多解釋緣由。
這樣咱們生成的時候就會獲得5個文件,可是還不夠,由於他們如今都在同一個目錄下,怎麼讓他們在本身的目錄下生成呢,再來改改:

<!--IView -->
    <instantiate from="root/src/app_package/LoginActivity.java.ftl" to="${escapeXmlAttribute(srcOut)}/port/I${activityClass}View.java" />
  <!--View -->
    <instantiate from="root/src/app_package/LoginActivity.java.ftl" to="${escapeXmlAttribute(srcOut)}/view/${activityClass}.java" />
  <!--IModel -->
    <instantiate from="root/src/app_package/LoginActivity.java.ftl" to="${escapeXmlAttribute(srcOut)}/port/I${activityClass}Model.java" />   
  <!--Model -->  
    <instantiate from="root/src/app_package/LoginActivity.java.ftl" to="${escapeXmlAttribute(srcOut)}/model/${activityClass}Model.java" />    
  <!--Presenter -->
    <instantiate from="root/src/app_package/LoginActivity.java.ftl" to="${escapeXmlAttribute(srcOut)}/${activityClass}Presenter.java" />                                    
    <open file="${escapeXmlAttribute(srcOut)}/presenter/${activityClass}.java" />
</#if>複製代碼

是否是很簡單?只須要在${escapeXmlAttribute(srcOut)}後面跟上具體的包路徑就行了,那麼咱們還缺點什麼?咱們還缺模板源文件,由於上面from的文件都是LoginActivity這個文件,也就是說生成的每一個類的代碼都是相同的。

下面讓咱們來完成最後一步,編寫模板代碼了,這個就相對簡單了,畢竟基本上都是java代碼,難不倒你們的,咱們在MVPTestActivity\root\src\app_package路徑下把LoginActivity.java.ftl這個文件複製四份,kt那個文件無論,那個是適配kotlin的,若是你是用的Kotlin的話,也能夠複製那個。
而後依次更名,效果以下:

每一個類的代碼以下:
MvpView.java

package ${packageName}.view;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
<#if applicationPackage??>
import ${applicationPackage}.R;
</#if>
import com.xujl.demo.port.I${activityClass}View;
import com.xujl.demo.presenter.${activityClass}Presenter;

class ${activityClass}View extends AppCompatActivity implements I${activityClass}View{
    private ${activityClass}Presenter mPresenter;

    @Override
    protected void onCreate (Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.${layoutName});
        mPresenter = new ${activityClass}Presenter(this);
    }

}複製代碼

MvpPresenter.java

package ${packageName}.presenter;

import com.xujl.demo.model.${activityClass}Model;
import com.xujl.demo.port.I${activityClass}Model;
import com.xujl.demo.port.I${activityClass}View;

public class ${activityClass}Presenter  {
    private I${activityClass}View mView;
    private I${activityClass}Model mModel;

    public ${activityClass}Presenter(I${activityClass}View view){
        mView = view;
        mModel = new ${activityClass}Model();
    }


}複製代碼

MvpModel.java

package ${packageName}.model;

import com.xujl.demo.port.I${activityClass}Model;

public class ${activityClass}Model implements I${activityClass}Model {



}複製代碼

IMvpModel.java

package ${packageName}.port;

public interface I${activityClass}Model{

}複製代碼

IMvpView.java

package ${packageName}.port;


public interface I${activityClass}View{

}複製代碼

最後不要忘記修改recipe.xml中from的模板文件名和模板mainfest中的activity名字

<!--IView -->
    <instantiate from="root/src/app_package/IMvpView.java.ftl"
                   to="${escapeXmlAttribute(srcOut)}/port/I${activityClass}View.java" />
  <!--View -->
    <instantiate from="root/src/app_package/MvpView.java.ftl"
                   to="${escapeXmlAttribute(srcOut)}/view/${activityClass}.java" />
  <!--IModel -->
    <instantiate from="root/src/app_package/IMvpModel.java.ftl"
                   to="${escapeXmlAttribute(srcOut)}/port/I${activityClass}Model.java" />   
  <!--Model -->  
    <instantiate from="root/src/app_package/MvpModel.java.ftl"
                   to="${escapeXmlAttribute(srcOut)}/model/${activityClass}Model.java" />    
  <!--Presenter -->
    <instantiate from="root/src/app_package/MvpPresenter.java.ftl"
                   to="${escapeXmlAttribute(srcOut)}/${activityClass}Presenter.java" />   
----------------------------------------------------
 <activity android:name=".view.${activityClass}View"
            <#if isNewProject>
            android:label="@string/app_name"
            </#if>
            <#if hasNoActionBar>
            android:theme="@style/${themeNameNoActionBar}"
            <#elseif !(hasApplicationTheme!false)>
            android:theme="@style/${themeName}"
            </#if>>

            <@manifestMacros.commonActivityBody />
        </activity>複製代碼

最後來看看運行效果(右鍵點擊的時候必定要在主包上面點擊,否則生成路徑可能會出錯):


若是你不是在主包上點擊的,也不要緊,記得修改這裏爲主包路徑就好:


至此咱們就完成了一個完整的模板了,固然這裏不管是包結構仍是命名方式,仍是mvp結構,都是我本身定義的,你們徹底能夠根據本身的項目實際狀況的來寫,我這樣定義的結構,好處是隻須要鍵入類名,其餘均可以生成了,固然你也能夠多設置幾個包名字段,用來動態配置model,view,presenter,和接口的包路徑。

最後附上整個MVPTestActivity的模板文件連接: pan.baidu.com/s/1skW4nTV 密碼: kbve

相關文章
相關標籤/搜索