Android:基于intellij-platform-plugin-template实现自定义页面模板插件
开启生长之旅!这是我参加「日新方案 2 月更文应战」的第 21 天,点击检查活动概况
我们平常开发运用的结构是自己建立的吗?或许关于大公司而言,都有专业负责办理结构的大神,底子轮不到自己进场。但关于一些中小型公司来说,往往都是自己孤身奋战,独立开发,这时都是运用自己建立的结构,时刻充裕的时分还会制作一些有意思的插件,以提高平常的开发效率。今天我们就来说说关于Android端页面级模板插件的自定义。
时刻回退三四年,那时还主流MVP模式,其时项目运用的便是组长封装的 MVPArms 结构,自开始运用便依据文档装置了与之配套的页面级(MVPArmsTemplate)和模板级(MVPArms-Module-Template)插件,文档写的非常仔细,一看就会,平常开发起来适当节省时刻,一个页面、一个模块只需一键即可生成。怎么办AndroidStudio几乎每次更新,内部的模板就会更改,所以自定义的模板也就面对更改,因为之前结构一向再保护,所以模块也会跟着保护。
如今,MVP已不再是主流,MVPArms内运用的技能也已过时,所以这个结构注定被淘汰。信任我们都现已跟从谷歌爸爸的脚步基于Jetpack自己建立了MVVM或许最新的MVI模式结构吧。不得不说,现在的结构和以前的结构比较,确实优化了许多不足,运用起来也变得简略,首要仍是看自己的代码风格和喜爱。
有了自己的结构,不自定义一个模板总感觉少了点什么,所以我们就来讲讲AndroidStudio如何自定义模板吧:
大概是在AS 4.1版别开始,以往的自定义模板办法现已不行用了,就连对应的templates文件夹都不存在。最后才发现,只能运用官方提供的intellij-platform-plugin-template 库进行插件自定义,生成Jar包供AS装置。但其实内部的样板代码是差不多的,首要便是装备环境和生成Jar包容易出现问题。
创立完成后,运用AS翻开,注意这儿还需求依靠一个Jar包,能够理解为AS的基础模板Jar包,位置在AS装置目录下/Applications/Android/Studio.app/Contents/plugins/android/lib/目录下。
在build.gradle.kts中增加依靠
dependencies {
compileOnly(files("lib/wizard-template.jar"))
}
在gradle.properties中修正对应信息
pluginGroup = com.enample.mvvm.mvvmtemplatenew //插件途径
pluginName_ = MVVMTemplateNew //插件名
pluginVersion = 1.0.4 //插件版别
platformVersion = 2021.1.3 //ide版别
platformPlugins = com.intellij.java, org.jetbrains.kotlin //编译语言
listeners目录下新建类
internal class MyProjectManagerListener : ProjectManagerListener {
override fun projectOpened(project: Project) {
project.service<MyProjectService>()
}
}
在settings.properties中修正模板名
rootProject.name = "MVVMTemplateNew"
基础目录便是这样:
内部修正的代码其实都是照猫画虎,我们就看看mvvmRecipe和mvvmTemplate两个工具类的部分办法:
val MVVMTemplate
get() = template {
name = "MVVM Template"
description = "一键创立 MVVM 单个页面所需求的悉数组件"
minApi = 19
category = Category.Other
formFactor = FormFactor.Mobile
screens = listOf(WizardUiContext.ActivityGallery, WizardUiContext.MenuEntry, WizardUiContext.NewProject, WizardUiContext.NewModule)
val pageName = stringParameter {
name = "Page Name"
default = "Main"
help = "请填写页面名,如填写 Main,会主动生成 MainActivity, MainViewModel 等文件"
constraints = listOf(Constraint.NONEMPTY, Constraint.UNIQUE)
}
val packageName = stringParameter {
name = "Root Package Name"
default = "com.mycompany.myapp"
constraints = listOf(Constraint.PACKAGE)
help = "请填写你的项目包名,请仔细核实此包名是否是正确的项目包名,不能包含子包,正确的格式如:com.exanmple.app"
}
//是否需求生成Activity
val needActivity = booleanParameter {
name = "Generate Activity"
default = true
help = "是否需求生成 Activity ? 不勾选则不生成"
}
//布局名
val activityLayoutName = stringParameter {
name = "Activity Layout Name"
default = "activity_main"
visible = { needActivity.value }
help = "Activity 创立之前需求填写 Activity 的布局名,若布局已创立就直接填写此布局名,若还没创立此布局,请勾选下面的单选框"
constraints = listOf(Constraint.LAYOUT, Constraint.NONEMPTY)
suggest = { "${activityToLayout(pageName.value.upperCase())}" }
}
//是否需求Activity的布局
val generateActivityLayout = booleanParameter {
name = "Generate Activity Layout"
default = true
visible = { needActivity.value }
help = "是否需求给 Activity 生成布局? 若勾选,则运用上面的布局名给此 Activity 创立默许的布局"
}
val activityPackageName = stringParameter {
name = "Activity Package Name"
default = "Activity Package Name"
visible = { needActivity.value }
help = "Activity 将被输出到此包下,请仔细核实此包名是否是你需求输出的方针包名"
constraints = listOf(Constraint.PACKAGE)
suggest = { "${packageName.value}.${pageName.value.toLowerCase()}" }
}
//Fragment
//是否需求生成Fragment
val needFragment = booleanParameter {
name = "Generate Fragment"
default = false
help = "是否需求生成 Fragment ? 不勾选则不生成"
}
//布局名
val fragmentLayoutName = stringParameter {
name = "Fragment Layout Name"
default = "fragment_main"
visible = { needFragment.value }
help = "Fragment 创立之前需求填写 Fragment 的布局名,若布局已创立就直接填写此布局名,若还没创立此布局,请勾选下面的单选框"
constraints = listOf(Constraint.LAYOUT, Constraint.UNIQUE, Constraint.NONEMPTY)
suggest = { "${fragmentToLayout(pageName.value.upperCase())}" }
}
//是否需求Fragment的布局
val generateFragmentLayout = booleanParameter {
name = "Generate Fragment Layout"
default = true
visible = { needFragment.value }
help = "是否需求给 Fragment 生成布局? 若勾选,则运用上面的布局名给此 Fragment 创立默许的布局"
}
val fragmentPackageName = stringParameter {
name = "Fragment Package Name"
default = "function Package Name"
constraints = listOf(Constraint.PACKAGE)
visible = { needFragment.value }
help = "Fragment 将被输出到此包下,请仔细核实此包名是否是你需求输出的方针包名"
suggest = {"${packageName.value}.${pageName.value.toLowerCase()}"}
}
val needRepository = booleanParameter {
name = "Generate Repository"
default = true
help = "是否需求生成 Repository ? 不勾选则不生成"
}
val repositoryPackageName = stringParameter {
name = "Repository Package Name"
default = "Repository Package Name"
constraints = listOf(Constraint.PACKAGE)
visible = { needRepository.value }
help = "Repository 将被输出到此包下,请仔细核实此包名是否是你需求输出的方针包名"
suggest = {"${packageName.value}.${pageName.value.toLowerCase()}"}
}
val needViewModel = booleanParameter {
name = "Generate ViewModel"
default = true
help = "是否需求生成 ViewModel ? 不勾选则不生成"
}
val viewModelPackageName = stringParameter {
name = "ViewModel Package Name"
default = "ViewModel Package Name"
constraints = listOf(Constraint.PACKAGE)
visible = { needViewModel.value }
help = "ViewModel 将被输出到此包下,请仔细核实此包名是否是你需求输出的方针包名"
suggest = {"${packageName.value}.${pageName.value.toLowerCase()}"}
}
widgets(
TextFieldWidget(pageName),
PackageNameWidget(packageName),
CheckBoxWidget(needActivity),
TextFieldWidget(activityLayoutName),
CheckBoxWidget(generateActivityLayout),
TextFieldWidget(activityPackageName),
CheckBoxWidget(needFragment),
TextFieldWidget(fragmentLayoutName),
CheckBoxWidget(generateFragmentLayout),
TextFieldWidget(fragmentPackageName),
CheckBoxWidget(needRepository),
TextFieldWidget(repositoryPackageName),
CheckBoxWidget(needViewModel),
TextFieldWidget(viewModelPackageName),
LanguageWidget()
)
thumb { File("template_blank_activity.png") }
recipe = { data: TemplateData ->
mvvmRecipe(
data as ModuleTemplateData,
pageName.value,
packageName.value,
needActivity.value,
activityLayoutName.value,
generateActivityLayout.value,
activityPackageName.value,
needFragment.value,
fragmentLayoutName.value,
generateFragmentLayout.value,
fragmentPackageName.value,
needRepository.value,
needViewModel.value,
repositoryPackageName.value,
viewModelPackageName.value
)
}
}
也便是创立时需求填写的一些页面信息,需求的就勾上,不需求的则不勾选
在mvvmRecipe中依据勾选的文件履行相应操作
if (needActivity) {
mergeXml(
manifestTemplateXml(packageRealName, activityPackageName, "${pageName}Activity"),
manifestOut.resolve("AndroidManifest.xml")
)
}
if (needActivity) {
save(
mvvmActivityKt(
packageRealName,
pageName,
activityPackageName,
activityLayoutName,
needViewModel
), srcOut.resolve("${pageName.toLowerCase()}/${pageName}Activity.${ktOrJavaExt}")
)
}
这儿还差一步,把刚刚mvvmTemplate中的MVVMTemplate装备到SamplePluginTemplateProviderImpl中
class SamplePluginTemplateProviderImpl : WizardTemplateProvider(){
override fun getTemplates(): List<Template> = listOf(
// activity的模板
MVVMTemplate
)
}
到此就能够点击AS中的Run Plugin进行编译,这儿有个古怪的现象是AS会下载装备中对应的IntelLiJ IDEA,下载完后会主动启动。此刻在IntelLiJ IDEA编译成功无误后,两个编译器都可点击Run Plugin生成Jar包,运行成功后会在其根目录下/bulid/libs/中检查到对应Jar包。
这个时分复制出这个Jar包,接下来便是Android开发了解的环节了,按下图装置完成重启AS
此刻的你就能够原地起飞,创立一个页面只需一键式操作,大大节省开发时刻。模板内容能够依据自己喜爱增加,AS更新需求慎重,有或许更新后插件无法运用,但依据报错信息和AS自带模板比照修正问题应该不大,所以如非必要尽量不要更新AS。
总结
整体完成下来也不是很难,但很容易出现编译失利等问题,还有便是插件装置后无法一键创立,或许创立出来的文件和预期不符的状况。很正常,这都是一个必经之路,仔细检查报错原因和代码,再依据官方文档(英文哦)提示,根本都能克服。无非便是一些版别原因导致,但插件成功了,在今后的开发中,尤其是初期开发,效率是大大提高,何乐而不为呢?
好了,这篇文章就讲解到这儿,希望对我们有所帮助。
开启生长之旅!这是我参加「日新方案 2 月更文应战」的第 21 天,点击检查活动概况