当前位置: 首页 > 科技观察

AndroidStudio模板的文件组

时间:2023-03-12 05:54:57 科技观察

文件组模板是一个基于FreeMarker模板语言的强大的Android开发模板。可以说,代码片段模板和文件模板是提高编码效率的利器,而文件组模板则可以看作是一个模板引擎。效果图显示了现有项目中模板的使用。创建项目时使用模板的效果图。模板示例场景。在开发Android时,我们通常会创建一个演示项目。可能有很多目的。可能是为了验证一个问题,也可能是为了学习一个框架的使用,可能是为了测试自己写的一个lib库等,这时候我们可能会创建一个Activity,然后在xml中写一些按钮,然后写Activity中按钮的事件监听逻辑,也就是说,我们为了执行一段代码,必须要做那么多的操作。为了简化这种重复的操作,我这里写了一个DebugActivity类,然后我们只需要写一个子类来继承它,然后像下面这样写几个方法。运行时会根据方法动态创建按钮,并在按钮被点击时执行该方法的代码逻辑。publicvoid_test(){T("PopupToast");}由于本文主要介绍模板相关,所以与该场景相关的具体代码技术细节不再赘述。有兴趣的可以看看DebugActivity的代码,这里提出来只是为了模板开发做铺垫。模板位置AndroidStudioTemplate中有一些系统预设的模板,我们可以直接修改,也可以单独添加新的模板。打开AndroidStudio安装目录/Contents/plugins/android/lib/templates文件夹,我们可以看到如下目录结构,这是存放AS中模板的地方。我们的下一个工作就在这里。为了保险起见,我们在这里新建一个目录。我们写的模板都放在新建的目录下。例如,我在这里创建了一个名为pk的目录。在上面模板规范的基础上,我们可以直接打开/activies/EmptyActivity目录。如下图所示,我们可以在上图红色区域看到Template的文件结构。大致说一下每个文件(文件夹)的含义globals.xml.ftlTemplaterecipe.xml.ftl中配置参数的地方(可选)recipe.xml.ftl模板行为执行的地方,引入这个模板后,whattonextdoiswhattheysaid(可选,但不选是没有意义的,因为template导入是行为驱动的)root存放模板文件和导入资源目录,模板文件可以是任意文本格式文件如.xml,.java,.gradle等资源一般为我们导入的.png资源文件(可以选择,不要选择同上)template_blank_activity.png导入模板时的导图(可选)template.xml模板引擎的配置文件(必须)我们可以看到真正核心的部分是root、recipe.xml.ftl和template.xml,那么本文将重点介绍这三个部分。我们打开根目录,可以看到里面的文件除了图片资源文件外,都是以.ftl结尾的,而.ftl是一个标准的FreeMarker文件。FreeMarker是一个类似于Velocity的模板框架。据说对于多文件处理有更好的性能,这大概也是AndroidStudio选择Velocity作为单文件模板,FreeMarker作为文件组模板的原因。有兴趣的可以去FreeMarker官网学习。它的自定义标签功能还是很强大的,个人感觉比Velocity更接地气。接下来我们看一下recipe.xml.ftl的内容,打开如下,这里所有<#开头的语法都是FreeMarker的语法,基本上看葫芦画瓢就懂了,就不说了很多。其实这个文件最重要的部分就是下面四个标签:copy就是简单的复制,将模板根目录下的一个文件复制到目标项目中的一个目录instantiate和copy很像,但是多了一点功能不是简单的通过IO流进行copy,而是根据FreeMarker在模板中可以识别的逻辑判断和数据导入,通过FreeMarker框架生成最终的目标文件merge。目标项目中有某个文件,我们还想将这个文件合并到我们模板的某些部分时,我们选择merge。比如我们添加一个Activity,需要mergeAndroidManifest.xml的配置。目前支持的merge格式有.xml和.gradle,但是对.gradle的支持不是很好,但是不影响模板的开发。对于这个模板引擎的开发者来说,这可能是最头疼的部分。不过对于我们用户来说,就不用测试那么多了,直接用就可以open这个很简单,就是指定模板导入后IDE要打开的文件,然后看模板的内容.xmltemplate_blank_activity.png当我们在导入模板的时候,AS会弹出一个如下图所示的UI界面,让我们填写或者选择一些数据,比如输入Activity的名称,选择SDK的版本等,这个界面是基于在这个文件上。.内容很多,为了减少篇幅,我挑一些比较重要的,模板标签名,导入模板时的模板名,根据模板的描述弹出Dialog的标题他选择,对应的区域1类别表示该模板属于哪个类别,导入的时候会有一个类别选择参数。每个label对应一个Dialog界面的inputitemid。这个参数的唯一标识也是我们在.ftl中引入的值。比如定义的id为username时引用,就是Dialog上输入项名称对应的$username名称,type对应参数的类型,Dialog就是根据这个来判断是否对应的输入是选择框、输入框或下拉框等,constraints对应参数的约束条件,如果有则使用|将多个值的建议值分开。这个输入部分是由级联效应引起的。也许你改变了A参数,B参数也会随之改变。是根据该参数确定的默认参数的默认值visibility。配置一个boolean类型的参数,一般指向另一个输入源help当焦点在某个输入源上时,上图中的区域3会限制这里的内容。了解了模板规范后,我们就可以编写模板了不再那么被动了,我们自己来编写文章开头所示的模板吧。首先在刚才说的自定义模板下创建如下图所示的目录结构,然后把下面的代码粘贴进去(图片部分找个替换就好了。。。)globals.xml.ftlrecipe.xml.ftltemplate。xmltemplate_debug_activity.pngAndroidManifest.xml.ftlDebugActivity.java.ftlpackage${packageName};importandroid.app.Activity;importandroid.content.Context;importandroid.content.Intent;importandroid.os.Bundle;importandroid.util.Log;importandroid.view.View;importandroid.widget.Button;importandroid.widget.LinearLayout;importandroid.widget.ScrollView;importandroid.widget.Toast;importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;importjava.lang.reflect.Method;importjava.util.ArrayList;importjava.util.List;/***调试测试类,快速调试Demo工程


*使用姿势:
*1.创建一个新的子类来继承这个类
*2。跳转活动:在subclass{@linkJump}注解中配置,然后在注解中配置跳转活动的类型
*3。点击按钮触发方法:在子类中声明一个名称以“_”开头的方法(支持任意修饰符),最后生成按钮的文字就是把方法改掉“_”
*4。方法参数支持默认参数和单个参数
*5。如果是单个参数,参数类型必须是Button或者Button的父类型,当方法执行的时候,参数会被赋值给Button对象
*https://github.com/puke3615/DebugActivity
*

**@authorzijiao*@version16/10/16*/publicabstractclassDebugActivityextendsActivity{protectedstaticfinalStringFIXED_PREFIX="_";privatefinalStringTAG=getClass().getName();privatefinalListbuttonItems=newArrayList<>();protectedLinearLayoutlinearLayout;protectedContextcontext;@Target(PElementType.TY(licy.RUNTIME)public@interfaceJump{Class[]value()default{};}@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);this.context=this;ScrollViewscrollView=newScrollView(this);setContentView(scrollView);this.linearLayout=newLinearLayout(this);this.linearLayout.setOrientation(LinearLayout.VERTICAL);scrollView.addView(linearLayout);try{resolveConfig();createButton();}catch(Throwablee){error(e...);button.setOnClickListener(newView.OnClickListener(){@OverridepublicvoidonClick(Viewv){if(buttonItem.target!=null){to(buttonItem.target);}else{Methodmethod=buttonItem.method;method.setAccessible(true);Class[]parameterTypes=method.getParameterTypes();intparamSize=parameterTypes.length;switch(paramSize){case0:try{method.invoke(DebugActivity.this);}catch(Throwablee){e.printStackTrace();error(e.getMessage());}break;case1:if(parameterTypes[0].isAssignableFrom(Button.class)){try{method.invoke(DebugActivity.this,button);}catch(Throwablee){e.printStackTrace();error(e.getMessage());}break;}default:error(method.getName()+"方法参数配置错误。");break;}}}});returnbutton;}privatevoidresolveConfig(){Classcls=getClass();//读取消跳转配置if(cls.isAnnotationPresent(Jump.class)){Jumpannotation=cls.getAnnotation(Jump.class);for(ClassactivityClass:annotation.value()){buttonItems.add(buildJumpActivityItem(activityClass)));}}//读取方法for(Methodmethod:cls.getDeclaredMethods()){handleMethod(method);}}protectedvoidhandleMethod(Methodmethod){StringmethodName=method.getName();if(methodName.startsWith(FIXED_PREFIX)){methodName=methodName.replaceFirst(FIXED_PREFIX,"");ButtonItembuttonItem=newButtonItem();buttonItem.method=method;buttonItem.name=methodName;buttonItems.add(buttonItem);}}protectedButtonItembuildJumpActivityItem(ClassactivityClass){ButtonItembuttonItem=newButtonItem();buttonItem.name="跳转到"+activityClass.getSimpleName();buttonItem.target=activityClass;returnbuttonItem;}publicvoidL(Objects){Log.i(TAG,s+"");}publicvoiderror(StringerrorMessage){T("[错误信息]\n"+errorMessage);}publicvoidT(Objectmessage){Toast.makeText(context,String.valueOf(message),Toast.LENGTH_SHORT).show();}publicvoidto(Classtarget){try{startActivity(newIntent(this,target)));}catch(Exceptione){e.printStackTrace();error(e.getMessage());}}publicvoidT(Stringformat,Object...values){T(String.format(format,values));}protectedstaticclassButtonItem{publicStringname;publicMethodmethod;publicClasstarget;}}JumpActivity.java.ftlSimpleActivity.java.ftlpackage${packageName};@DebugActivity.Jump({<#ifaddJumpActivity>JumpActivity.class,<#else>})publicclass${activityClass}extendsDebugActivity{<#ifaddExample>privateintnumber=0;publicvoid_noparametermethodcall(){T("methodcallwithoutparameter");}publicvoid_methodcallwithparameter(Buttonbutton){button.setText("numberis"+number++);}//代码无法执行,并直接弹出toast提示报错publicvoid_wrongparametercall(Stringmsg){T("test");}//方法名不以“_”开头,按钮无法创建成功publicvoidinvalidcall(){T("test");}//会抓到crash,以toast方式弹出publicvoid_Crashtest(){inta=1/0;}}ok,过程写模板就结束了。接下来,重启AndroidStudio,然后NewProject一路往下,直到这个界面,这里是我们自定义的DebugActivity模板