成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Android Studio模板之文件組

移動開發 Android
文件組模板是基于FreeMarker模板語言的一個功能很強大的Android開發模板,可以這樣說,代碼片段模板和文件模板是一種提高編碼效率的工具,而文件組模板可以算是一種模板引擎。

文件組模板是基于FreeMarker模板語言的一個功能很強大的Android開發模板,可以這樣說,代碼片段模板和文件模板是一種提高編碼效率的工具,而文件組模板可以算是一種模板引擎。

效果圖展示  

效果圖展示 

已有工程中使用模板效果圖  

 

創建工程時使用模板 

[[185259]] 

 

示例場景

在進行Android開發時,我們經常會創建一個Demo工程,目的可能有很多種,可能是為了驗證一個問題,可能是為了學習一個框架的使用,可能為了測試自己寫的一個lib庫等等。這個時候我們可能會創建一個Activity,然后再在xml寫一些按鈕,再在Activity里寫該按鈕的事件監聽邏輯,也就是說為了執行一段代碼我們要做這么多操作。為了簡化這段重復操作,我這邊寫了一個DebugActivity類,然后支持我們只需要寫個子類來繼承它,然后像下面這樣寫幾個方法即可,運行的時候會根據方法動態創建按鈕,并在點擊按鈕時執行該方法的代碼邏輯。

  1. public void _test() { 
  2.  
  3. T("彈出Toast"); 
  4.  
  5.  

由于本文主要介紹模板相關的,所以該場景相關的具體代碼技術細節就不多說了,有興趣的可以看下,DebugActivity的代碼,這里提出來只是為模板開發簡單的做個鋪墊。

模板位置

Android Studio Template中有系統預設的一些模板,我們可以直接修改,也可以另行添加新的模板。打開Android Studio安裝目錄/Contents/plugins/android/lib/templates這個文件夾我們能看到下面的目錄結構,這里便是AS中模板存放的位置。 

 

 

 

我們接下來的工作也就在這里,保險起見我們在這里新建一個目錄,我們自己寫的模板都放在自己新建的目錄里,例如我這里就創建了一個叫pk的目錄。

模板規范

在上面的基礎上,我們可以直接打開/activies/EmptyActivity目錄,如下圖 

 

 

 

我們可以看到上面紅色區域便是Template的文件結構,大致說下各個文件(夾)的含義

  • globals.xml.ftl 模板中參數配置的地方(可選)
  • recipe.xml.ftl 模板行為執行處,引入這個模板之后,接下來要做什么事情,就是它說的算(可選,但是不選就沒有意義了,因為模板引入是要要行為驅動的)
  • root 存放模板文件及引入資源的目錄,模板文件可以是.xml、.java、.gradle等任何一個文本格式的文件,資源一般是我們引入的.png資源文件(可選,不選同上)
  • template_blank_activity.png 引入模板時的引導圖(可選)
  • template.xml 面向模板引擎的配置文件(必選)

我們可以看到,真正核心的部分就是root、recipe.xml.ftl和template.xml,接下來這重點說明這三部分。

我們可以打開root目錄,能夠看到里面的文件除了圖片資源文件都是以.ftl結尾的,而.ftl是標準的FreeMarker的文件。FreeMarker是類似于Velocity的一種模板框架,據說對于多文件處理時它具有更好的性能,大概也是Android Studio選擇Velocity作為單文件模板,選擇FreeMarker作為文件組模板的原因吧。有興趣的可以去FreeMarker官網學習一下,它的自定義標簽功能還是很強大的,個人感覺比Velocity的更加接地氣。

接下來我們看一下recipe.xml.ftl 的內容,打開如下 

 

 

 

這里以<#開頭的都是FreeMarker的語法,基本上比葫蘆畫瓢就能看明白,就不多說了。其實對于這個文件最重要的部分是下面四個標簽:

  • copy 就是簡單的copy,把模板root目錄下的某個文件copy到目標工程的某個目錄下
  • instantiate 跟copy很類似,***多的一點功能就是并不只簡單的走IO流進行copy,而是通過FreeMarker框架按照模板中的FreeMarker能識別的邏輯判斷和數據引入來生成最終的目標文件
  • merge 目標項目中有了某文件,而我們還要想該文件合并一些我們的模板的部分時,就選用merge,例如我們添加一個Activity時需要mergeAndroidManifest.xml的配置。目前支持的merge格式有.xml和.gradle,但是對.gradle支持的不怎么好,不過不影響該模板的開發,對于這套模板引擎的開發者來說,這可能是最麻煩的部分了,但是對于我們使用者就不用考那么多了,直接使用吧
  • open 這個很簡單,就是指定模板引入之后要IDE打開的文件

然后看下template.xml內容

  1. <?xml version="1.0"?> 
  2.  
  3. <template 
  4.  
  5.     format="5" 
  6.  
  7.     revision="5" 
  8.  
  9.     name="Empty Activity" 
  10.  
  11.     minApi="7" 
  12.  
  13.     minBuildApi="14" 
  14.  
  15.     description="Creates a new empty activity"
  16.  
  17.     <category value="Activity" /> 
  18.  
  19.     <formfactor value="Mobile" /> 
  20.  
  21.     <parameter 
  22.  
  23.         id="activityClass" 
  24.  
  25.         name="Activity Name" 
  26.  
  27.         type="string" 
  28.  
  29.         constraints="class|unique|nonempty" 
  30.  
  31.         suggest="${layoutToActivity(layoutName)}" 
  32.  
  33.         default="MainActivity" 
  34.  
  35.         help="The name of the activity class to create" /> 
  36.  
  37.     <parameter 
  38.  
  39.         id="generateLayout" 
  40.  
  41.         name="Generate Layout File" 
  42.  
  43.         type="boolean" 
  44.  
  45.         default="true" 
  46.  
  47.         help="If true, a layout file will be generated" /> 
  48.  
  49.     <parameter 
  50.  
  51.         id="layoutName" 
  52.  
  53.         name="Layout Name" 
  54.  
  55.         type="string" 
  56.  
  57.         constraints="layout|unique|nonempty" 
  58.  
  59.         suggest="${activityToLayout(activityClass)}" 
  60.  
  61.         default="activity_main" 
  62.  
  63.         visibility="generateLayout" 
  64.  
  65.         help="The name of the layout to create for the activity" /> 
  66.  
  67.     <parameter 
  68.  
  69.         id="isLauncher" 
  70.  
  71.         name="Launcher Activity" 
  72.  
  73.         type="boolean" 
  74.  
  75.         default="false" 
  76.  
  77.         help="If true, this activity will have a CATEGORY_LAUNCHER intent filter, making it visible in the launcher" /> 
  78.  
  79.      
  80.  
  81.     <parameter 
  82.  
  83.         id="packageName" 
  84.  
  85.         name="Package name" 
  86.  
  87.         type="string" 
  88.  
  89.         constraints="package" 
  90.  
  91.         default="com.mycompany.myapp" /> 
  92.  
  93.     <!-- 128x128 thumbnails relative to template.xml --> 
  94.  
  95.     <thumbs> 
  96.  
  97.         <!-- default thumbnail is required --> 
  98.  
  99.         <thumb>template_blank_activity.png</thumb> 
  100.  
  101.     </thumbs> 
  102.  
  103.     <globals file="globals.xml.ftl" /> 
  104.  
  105.     <execute file="recipe.xml.ftl" /> 
  106.  
  107. </template>  

當我們進行模板引入時,AS會彈出一個如下圖的UI界面,要我們來填入或選擇一些數據,例如輸入Activity的的名稱,選擇SDK的版本之類的。而這個界面就是根據由該文件而來的。 

 

 

 

內容比較多,為減少篇幅我挑些重要的說

  • template標簽
    • name 引入模板時的模板名稱,就死根據他選擇哪個模板的
    • description 彈出Dialog的標題,對應上去的區域1
  • category 表示該模板屬于哪種分類,在引入的時候會有個分類的選擇
  • parameter 每個該標簽就對應Dialog界面的一個輸入項
    • id 該參數的***標識符,也是我們在.ftl中引入的值,例如定義的id為username,引用時就是$username
    • name 對應Dialog上面該輸入項的名稱
    • type 對應該參數的類型,Dialog就是根據這個來決定對應輸入是選擇框、輸入框還是下拉框等等
    • constraints 對應該參數的約束,如果有多個要用|分割開
    • suggest 建議值,這個輸入部分是由級聯效應的,可能你改了A參數,B參數也會跟著改變,就是根據這個參數決定的
    • default 參數的默認值
    • visibility 可見性,要配置一個boolean類型的參數,一般指向另一個輸入源
    • help 當焦點在某個輸入源上面時,上圖的區域3的就限制這兒的內容

操刀實戰

了解了模板規范之后,我們編寫模板時就不會那么被動了,下面我們來自己動手編寫文章開始部分展示的模板。

首先在剛才提到的自定義的模板下創建如下圖所示的目錄結構 

 

 

 

 

然后將下面的代碼對應貼進去(圖片部分隨便找一張代替好了…)

globals.xml.ftl 

 

 

 

recipe.xml.ftl 

 

 

 

template.xml

  1. <?xml version="1.0"?> 
  2.  
  3. <template 
  4.  
  5.     format="5" 
  6.  
  7.     revision="5" 
  8.  
  9.     name="Debug Activity" 
  10.  
  11.     minApi="7" 
  12.  
  13.     minBuildApi="14" 
  14.  
  15.     description="創建一個Debug的Activity"
  16.  
  17.     <category value="Activity" /> 
  18.  
  19.     <formfactor value="Mobile" /> 
  20.  
  21.     <parameter 
  22.  
  23.         id="activityClass" 
  24.  
  25.         name="Activity名稱" 
  26.  
  27.         type="string" 
  28.  
  29.         constraints="class|unique|nonempty" 
  30.  
  31.         default="SetupActivity" 
  32.  
  33.         help="創建Activity的名稱" /> 
  34.  
  35.     <parameter 
  36.  
  37.         id="addExample" 
  38.  
  39.         name="是否添加按鈕使用示例" 
  40.  
  41.         type="boolean" 
  42.  
  43.         default="false" 
  44.  
  45.         help="選擇時會自動生成測試按鈕;否則不生成" /> 
  46.  
  47.      
  48.  
  49.     <parameter 
  50.  
  51.         id="addJumpActivity" 
  52.  
  53.         name="是否添加跳轉Activity示例" 
  54.  
  55.         type="boolean" 
  56.  
  57.         default="false" 
  58.  
  59.         help="選擇時會自動生成跳轉Activity相關邏輯;否則不生成" /> 
  60.  
  61.     <parameter 
  62.  
  63.         id="isLauncher" 
  64.  
  65.         name="設為啟動頁面" 
  66.  
  67.         type="boolean" 
  68.  
  69.         default="true" 
  70.  
  71.         help="選擇時設置該頁面為啟動頁面;否則不設" /> 
  72.  
  73.      
  74.  
  75.     <parameter 
  76.  
  77.         id="packageName" 
  78.  
  79.         name="包名" 
  80.  
  81.         type="string" 
  82.  
  83.         constraints="package" 
  84.  
  85.         default="com.mycompany.myapp" 
  86.  
  87.         help="輸入Application包名" /> 
  88.  
  89.     <!-- 128x128 thumbnails relative to template.xml --> 
  90.  
  91.     <thumbs> 
  92.  
  93.         <!-- default thumbnail is required --> 
  94.  
  95.         <thumb>template_debug_activity.png</thumb> 
  96.  
  97.     </thumbs> 
  98.  
  99.     <globals file="globals.xml.ftl" /> 
  100.  
  101.     <execute file="recipe.xml.ftl" /> 
  102.  
  103. </template>  

AndroidManifest.xml.ftl 

 

 

 

DebugActivity.java.ftl

  1. package ${packageName}; 
  2.  
  3. import android.app.Activity; 
  4.  
  5. import android.content.Context; 
  6.  
  7. import android.content.Intent; 
  8.  
  9. import android.os.Bundle; 
  10.  
  11. import android.util.Log; 
  12.  
  13. import android.view.View
  14.  
  15. import android.widget.Button; 
  16.  
  17. import android.widget.LinearLayout; 
  18.  
  19. import android.widget.ScrollView; 
  20.  
  21. import android.widget.Toast; 
  22.  
  23. import java.lang.annotation.ElementType; 
  24.  
  25. import java.lang.annotation.Retention; 
  26.  
  27. import java.lang.annotation.RetentionPolicy; 
  28.  
  29. import java.lang.annotation.Target; 
  30.  
  31. import java.lang.reflect.Method; 
  32.  
  33. import java.util.ArrayList; 
  34.  
  35. import java.util.List; 
  36.  
  37. /** 
  38.  
  39.  * Debug測試類,快速調試Demo工程<hr /> 
  40.  
  41.  * 使用姿勢:<br /> 
  42.  
  43.  * 1. 新建一個子類繼承該類<br /> 
  44.  
  45.  * 2. 跳轉Activity: 在子類配置{@link Jump}注解, 然后在注解中配置跳轉Activity的類型<br /> 
  46.  
  47.  * 3. 點擊按鈕觸發方法: 在子類聲明一個名稱以"_"開頭的方法(支持任意修飾符),最終生成按鈕的文字便是改方法截去"_"<br /> 
  48.  
  49.  * 4. 方法參數支持缺省參數和單個參數<br /> 
  50.  
  51.  * 5. 如果是單個參數,參數類型必須是Button或Button的父類類型,當方法執行時,該參數會被賦值為該Buttom對象<br /> 
  52.  
  53.  * https://github.com/puke3615/DebugActivity<br /> 
  54.  
  55.  * <p> 
  56.  
  57.  * 
  58.  
  59.  * @author zijiao 
  60.  
  61.  * @version 16/10/16 
  62.  
  63.  */ 
  64.  
  65. public abstract class DebugActivity extends Activity { 
  66.  
  67.     protected static final String FIXED_PREFIX = "_"
  68.  
  69.     private final String TAG = getClass().getName(); 
  70.  
  71.     private final List<ButtonItem> buttonItems = new ArrayList<>(); 
  72.  
  73.     protected LinearLayout linearLayout; 
  74.  
  75.     protected Context context; 
  76.  
  77.     @Target(ElementType.TYPE) 
  78.  
  79.     @Retention(RetentionPolicy.RUNTIME) 
  80.  
  81.     public @interface Jump { 
  82.  
  83.         Class<? extends Activity>[] value() default {}; 
  84.  
  85.     } 
  86.  
  87.     @Override 
  88.  
  89.     protected void onCreate(Bundle savedInstanceState) { 
  90.  
  91.         super.onCreate(savedInstanceState); 
  92.  
  93.         this.context = this; 
  94.  
  95.         ScrollView scrollView = new ScrollView(this); 
  96.  
  97.         setContentView(scrollView); 
  98.  
  99.         this.linearLayout = new LinearLayout(this); 
  100.  
  101.         this.linearLayout.setOrientation(LinearLayout.VERTICAL); 
  102.  
  103.         scrollView.addView(linearLayout); 
  104.  
  105.         try { 
  106.  
  107.             resolveConfig(); 
  108.  
  109.             createButton(); 
  110.  
  111.         } catch (Throwable e) { 
  112.  
  113.             error(e.getMessage()); 
  114.  
  115.         } 
  116.  
  117.     } 
  118.  
  119.     private void createButton() { 
  120.  
  121.         for (ButtonItem buttonItem : buttonItems) { 
  122.  
  123.             linearLayout.addView(buildButton(buttonItem)); 
  124.  
  125.         } 
  126.  
  127.     } 
  128.  
  129.     protected View buildButton(final ButtonItem buttonItem) { 
  130.  
  131.         final Button button = new Button(this); 
  132.  
  133.         button.setText(buttonItem.name); 
  134.  
  135.         button.setOnClickListener(new View.OnClickListener() { 
  136.  
  137.             @Override 
  138.  
  139.             public void onClick(View v) { 
  140.  
  141.                 if (buttonItem.target != null) { 
  142.  
  143.                     to(buttonItem.target); 
  144.  
  145.                 } else { 
  146.  
  147.                     Method method = buttonItem.method; 
  148.  
  149.                     method.setAccessible(true); 
  150.  
  151.                     Class<?>[] parameterTypes = method.getParameterTypes(); 
  152.  
  153.                     int paramSize = parameterTypes.length; 
  154.  
  155.                     switch (paramSize) { 
  156.  
  157.                         case 0: 
  158.  
  159.                             try { 
  160.  
  161.                                 method.invoke(DebugActivity.this); 
  162.  
  163.                             } catch (Throwable e) { 
  164.  
  165.                                 e.printStackTrace(); 
  166.  
  167.                                 error(e.getMessage()); 
  168.  
  169.                             } 
  170.  
  171.                             break; 
  172.  
  173.                         case 1: 
  174.  
  175.                             if (parameterTypes[0].isAssignableFrom(Button.class)) { 
  176.  
  177.                                 try { 
  178.  
  179.                                     method.invoke(DebugActivity.this, button); 
  180.  
  181.                                 } catch (Throwable e) { 
  182.  
  183.                                     e.printStackTrace(); 
  184.  
  185.                                     error(e.getMessage()); 
  186.  
  187.                                 } 
  188.  
  189.                                 break; 
  190.  
  191.                             } 
  192.  
  193.                         default
  194.  
  195.                             error(method.getName() + "方法參數配置錯誤."); 
  196.  
  197.                             break; 
  198.  
  199.                     } 
  200.  
  201.                 } 
  202.  
  203.             } 
  204.  
  205.         }); 
  206.  
  207.         return button; 
  208.  
  209.     } 
  210.  
  211.     private void resolveConfig() { 
  212.  
  213.         Class<?> cls = getClass(); 
  214.  
  215.         //讀取跳轉配置 
  216.  
  217.         if (cls.isAnnotationPresent(Jump.class)) { 
  218.  
  219.             Jump annotation = cls.getAnnotation(Jump.class); 
  220.  
  221.             for (Class<? extends Activity> activityClass : annotation.value()) { 
  222.  
  223.                 buttonItems.add(buildJumpActivityItem(activityClass)); 
  224.  
  225.             } 
  226.  
  227.         } 
  228.  
  229.         //讀取方法 
  230.  
  231.         for (Method method : cls.getDeclaredMethods()) { 
  232.  
  233.             handleMethod(method); 
  234.  
  235.         } 
  236.  
  237.     } 
  238.  
  239.     protected void handleMethod(Method method) { 
  240.  
  241.         String methodName = method.getName(); 
  242.  
  243.         if (methodName.startsWith(FIXED_PREFIX)) { 
  244.  
  245.             methodName = methodName.replaceFirst(FIXED_PREFIX, ""); 
  246.  
  247.             ButtonItem buttonItem = new ButtonItem(); 
  248.  
  249.             buttonItem.method = method; 
  250.  
  251.             buttonItem.name = methodName; 
  252.  
  253.             buttonItems.add(buttonItem); 
  254.  
  255.         } 
  256.  
  257.     } 
  258.  
  259.     protected ButtonItem buildJumpActivityItem(Class<? extends Activity> activityClass) { 
  260.  
  261.         ButtonItem buttonItem = new ButtonItem(); 
  262.  
  263.         buttonItem.name = "跳轉到" + activityClass.getSimpleName(); 
  264.  
  265.         buttonItem.target = activityClass; 
  266.  
  267.         return buttonItem; 
  268.  
  269.     } 
  270.  
  271.     public void L(Object s) { 
  272.  
  273.         Log.i(TAG, s + ""); 
  274.  
  275.     } 
  276.  
  277.     public void error(String errorMessage) { 
  278.  
  279.         T("[錯誤信息]\n" + errorMessage); 
  280.  
  281.     } 
  282.  
  283.     public void T(Object message) { 
  284.  
  285.         Toast.makeText(context, String.valueOf(message), Toast.LENGTH_SHORT).show(); 
  286.  
  287.     } 
  288.  
  289.     public void to(Class<? extends Activity> target) { 
  290.  
  291.         try { 
  292.  
  293.             startActivity(new Intent(this, target)); 
  294.  
  295.         } catch (Exception e) { 
  296.  
  297.             e.printStackTrace(); 
  298.  
  299.             error(e.getMessage()); 
  300.  
  301.         } 
  302.  
  303.     } 
  304.  
  305.     public void T(String format, Object... values) { 
  306.  
  307.         T(String.format(format, values)); 
  308.  
  309.     } 
  310.  
  311.     protected static class ButtonItem { 
  312.  
  313.         public String name
  314.  
  315.         public Method method; 
  316.  
  317.         public Class<? extends Activity> target; 
  318.  
  319.     } 
  320.  
  321.  

JumpActivity.java.ftl 

 

 

 

SimpleActivity.java.ftl

  1. package ${packageName}; 
  2.  
  3. @DebugActivity.Jump({ 
  4.  
  5. <#if addJumpActivity> 
  6.  
  7.     JumpActivity.class, 
  8.  
  9. <#else
  10.  
  11. </#if> 
  12.  
  13. }) 
  14.  
  15. public class ${activityClass} extends DebugActivity { 
  16.  
  17. <#if addExample> 
  18.  
  19.     private int number = 0; 
  20.  
  21.     public void _無參方法調用() { 
  22.  
  23.     T("無參方法調用"); 
  24.  
  25.     } 
  26.  
  27.     public void _有參方法調用(Button button) { 
  28.  
  29.         button.setText("number is " + number++); 
  30.  
  31.     } 
  32.  
  33.     //代碼執行不到,直接彈出toast提示報錯 
  34.  
  35.     public void _錯誤參數調用(String msg) { 
  36.  
  37.         T("test"); 
  38.  
  39.     } 
  40.  
  41.     //方法名沒有以"_"開頭,按鈕無法創建成功 
  42.  
  43.     public void 無效調用() { 
  44.  
  45.         T("test"); 
  46.  
  47.     } 
  48.  
  49.     //crash會被會被catch住,以toast方式彈出 
  50.  
  51.     public void _Crash測試() { 
  52.  
  53.         int a = 1 / 0; 
  54.  
  55.     } 
  56.  
  57. </#if> 
  58.  
  59.  

ok,到此對于該模板的編寫過程就結束了,接下來重啟下Android Studio,然后New Project一路next下去,直到這個界面,這里就是我們自定義的DebugActivity模板了

 

責任編輯:龐桂玉 來源: 安卓巴士Android開發者門戶
相關推薦

2020-04-09 13:23:29

Nginx配置文件模板

2009-12-14 15:46:38

visual stud

2009-12-09 14:11:57

Visual Stud

2011-07-07 13:48:35

Smarty

2013-05-20 15:04:02

Android調試查看日志Android開發

2011-07-19 16:11:36

組策略安全模板

2023-12-13 10:51:49

C++函數模板編程

2010-06-09 12:39:41

世界杯服務器

2011-03-14 17:50:34

PIM-DM

2010-08-05 13:04:05

路由器

2011-03-14 17:38:31

PIM-SM

2016-11-21 16:23:24

Android StGoogle

2017-03-02 10:30:57

AndroidAndroid Stu技巧

2013-05-17 10:48:40

GoogleAndroid Stu

2013-05-21 10:17:28

GoogleAndroid Stu

2014-06-24 10:34:08

Android Stu教程

2014-07-01 11:18:37

Android Stu問題匯總

2014-07-14 10:54:13

Android Stu

2012-07-11 08:51:51

設計模式

2017-03-10 18:29:17

Androidfreemarker應用
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 欧美多人在线 | 羞羞视频免费观看 | 国产我和子的乱视频网站 | 日本高清不卡视频 | 中午字幕在线观看 | 亚洲国产精品一区在线观看 | av香港经典三级级 在线 | 国产乱码精品1区2区3区 | 日韩精品久久久 | 国产高清在线精品 | 91精品国产综合久久久久蜜臀 | 中文字幕一区二区三区精彩视频 | 国产精品久久久久aaaa | 色必久久| 成人影院av | 亚洲精品一区二区 | 色综合久久88色综合天天 | 在线午夜 | 亚洲性在线 | 日本高清精品 | 中文字幕视频在线观看 | 爱爱小视频 | 美女福利网站 | 日韩在线视频播放 | 精品国产一区二区国模嫣然 | 国产极品91 | 欧美日韩成人在线 | 欧美在线观看免费观看视频 | 福利电影在线 | 日韩播放 | 黄色一级大片在线免费看产 | 日本精品久久久久久久 | 日韩国产一区二区三区 | 成人久久网 | 欧美日本韩国一区二区三区 | 久久99久久| 精品久久久久久久久久久下田 | 日本精品视频 | 国产午夜精品久久久久 | 国内自拍偷拍一区 | 久久久久久国模大尺度人体 |