AndroidStudio模板之文件组

文件组模板是基于FreeMarker模板语言的一个功能很强大的Android开发模板,可以这样说,代码片段模板和文件模板是一种提高编码效率的工具,而文件组模板可以算是一种模板引擎。

网站设计、成都网站设计中从网站色彩、结构布局、栏目设置、关键词群组等细微处着手,突出企业的产品/服务/品牌,帮助企业锁定精准用户,提高在线咨询和转化,使成都网站营销成为有效果、有回报的无锡营销推广。成都创新互联专业成都网站建设10多年了,客户满意度97.8%,欢迎成都创新互联客户联系。

效果图展示  

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

创建工程时使用模板 

示例场景

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

当我们进行模板引入时,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.  
  2.  
  3.  
  4.     format="5" 
  5.  
  6.     revision="5" 
  7.  
  8.     name="Debug Activity" 
  9.  
  10.     minApi="7" 
  11.  
  12.     minBuildApi="14" 
  13.  
  14.     description="创建一个Debug的Activity"> 
  15.  
  16.      
  17.  
  18.      
  19.  
  20.     
  21.  
  22.         id="activityClass" 
  23.  
  24.         name="Activity名称" 
  25.  
  26.         type="string" 
  27.  
  28.         constraints="class|unique|nonempty" 
  29.  
  30.         default="SetupActivity" 
  31.  
  32.         help="创建Activity的名称" /> 
  33.  
  34.     
  35.  
  36.         id="addExample" 
  37.  
  38.         name="是否添加按钮使用示例" 
  39.  
  40.         type="boolean" 
  41.  
  42.         default="false" 
  43.  
  44.         help="选择时会自动生成测试按钮;否则不生成" /> 
  45.  
  46.      
  47.  
  48.     
  49.  
  50.         id="addJumpActivity" 
  51.  
  52.         name="是否添加跳转Activity示例" 
  53.  
  54.         type="boolean" 
  55.  
  56.         default="false" 
  57.  
  58.         help="选择时会自动生成跳转Activity相关逻辑;否则不生成" /> 
  59.  
  60.     
  61.  
  62.         id="isLauncher" 
  63.  
  64.         name="设为启动页面" 
  65.  
  66.         type="boolean" 
  67.  
  68.         default="true" 
  69.  
  70.         help="选择时设置该页面为启动页面;否则不设" /> 
  71.  
  72.      
  73.  
  74.     
  75.  
  76.         id="packageName" 
  77.  
  78.         name="包名" 
  79.  
  80.         type="string" 
  81.  
  82.         constraints="package" 
  83.  
  84.         default="com.mycompany.myapp" 
  85.  
  86.         help="输入Application包名" /> 
  87.  
  88.      
  89.  
  90.      
  91.  
  92.          
  93.  
  94.         template_debug_activity.png 
  95.  
  96.      
  97.  
  98.      
  99.  
  100.      
  101.  
  102.   

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工程 
  40.  
  41.  * 使用姿势: 
  42.  
  43.  * 1. 新建一个子类继承该类 
  44.  
  45.  * 2. 跳转Activity: 在子类配置{@link Jump}注解, 然后在注解中配置跳转Activity的类型 
  46.  
  47.  * 3. 点击按钮触发方法: 在子类声明一个名称以"_"开头的方法(支持任意修饰符),最终生成按钮的文字便是改方法截去"_" 
  48.  
  49.  * 4. 方法参数支持缺省参数和单个参数 
  50.  
  51.  * 5. 如果是单个参数,参数类型必须是Button或Button的父类类型,当方法执行时,该参数会被赋值为该Buttom对象 
  52.  
  53.  * https://github.com/puke3615/DebugActivity 
  54.  
  55.  * 

     

  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 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[] 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 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 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 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 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.  
  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.  
  58.  
  59. }  

ok,到此对于该模板的编写过程就结束了,接下来重启下Android Studio,然后New Project一路next下去,直到这个界面,这里就是我们自定义的DebugActivity模板了

网页名称:AndroidStudio模板之文件组
转载源于:http://www.csdahua.cn/qtweb/news43/124343.html

网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网