1. 简介
- Jetbrains 提供了3种插件创建的方式
- Using GitHub Template
- Using Gradle
- Using DevKit
本文选择第一种方案实现
代码地址:https://github.com/sxfxwlh/hw_as_plugins
AS插件开发文档:https://plugins.jetbrains.com/docs/intellij/android-studio.html
2. 模板项目搭建和环境配置
2.1 获取模板项目
首先来到JetBrains的GitHub仓库,根据readme.md
引导进行操作,获取到官方提供的template项目。
2.2 在本地运行起来获取到的项目
2.2.1 用AS在本地获取并打开自己fork到的项目。
2.2.2 修改配置文件,添加依赖
- 修改文件
settings.gradle.kts
中的变量rootProject.name = "自定义插件名字"
- 修改文件
gradle.properties
中的变量
pluginGroup = com.hw
pluginName = hw_child
pluginVersion = 1.0.0
platformPlugins = android
- 修改src/main/resources/META_INF/plugin.xml文件,增加以下依赖
<depends>org.jetbrains.android</depends>
<depends>com.intellij.modules.androidstudio</depends>
- 拷贝AndroidStudio安装目录下模板jar包到项目根目录下的lib下,jar包文件路径:
/Applications/Android Studio.app/Contents/plugins/android/lib/wizard-template.jar
, 同时添加依赖到build.gradle.kts文件最底部,Sync Now
dependencies {
compileOnly(files("lib/wizard-template.jar"))
}
2.2.3 注册插件入口
在src/main/kotlin
下新建other包,新建kotlin-class WizardTemplateProviderImpl
继承自WizardTemplateProvider
,实现抽象方法如下,
override fun getTemplates(): List<Template> = listOf(
//自定义模板就添加在此处
)
注册插件到plugin.xml中,如下
<!-- android-plugins.xml文档地址如下 -->
<!-- https://plugins.jetbrains.com/docs/intellij/extension-point-list.html#android-plugin-->
<!-- <extensions defaultExtensionNs="com.android.tools.idea.wizard.template">-->
<!-- <wizardTemplateProvider implementation="com.android.tools.idea.wizard.template.impl.WizardTemplateProviderImpl" />-->
<!-- </extensions>-->
<extensions defaultExtensionNs="com.android.tools.idea.wizard.template">
<wizardTemplateProvider implementation="WizardTemplateProviderImpl" />
</extensions>
3. 模板基础文件编写
3.1 在src/main/kotlin
目录下新建包层级结构如下
3.2 activity插件主要文件编写
3.2.1 在activity/res/layout
目录下新建createActivityXml
文件
package other.activity.res.layout
import java.lang.StringBuilder
/**
* @Author hubert
* @Date 2022/5/1 12:51 下午
*/
fun createActivityXml(
packageName: String,
activityClass: String
):String{
val sb = StringBuilder()
sb.append( """
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="${packageName}.${activityClass}Activity">
""")
if(hasNavigation){
sb.append("""
<com.necer.basic2.view.Navigation
android:id="@+id/navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
""")
}
sb.append("""
</LinearLayout>
""")
return sb.toString().trim()
}
3.2.2 在activity/src/app_package
目录下新建createActivityKt
文件
package other.activity.src.app_package
import java.lang.StringBuilder
/**
* @Author hubert
* @Date 2022/5/1 12:57 下午
*/
fun createActivityKt(
applicationPackage: String?,
packageName: String,
activityClass: String,
layoutName: String,
hasNavigation: Boolean
):String{
val sb = StringBuilder()
sb.append("""
package $packageName
import android.os.Bundle
import ${applicationPackage}.R
import com.necer.basic2.ui.BaseActivity
import kotlinx.android.synthetic.main.${layoutName}.*
class ${activityClass}Activity : BaseActivity() {
override fun getLayoutId()=R.layout.${layoutName}
override fun onCreatee(savedInstanceState: Bundle?) {
""")
if(hasNavigation){
sb.append("""
navigation.title("").left(true)
""")
}
sb.append("""
}
override fun getNetData() {
}
}
""")
return sb.toString()
}
3.2.3 在activity/src
目录下新建createActivityRecipe
文件
package other.activity.src
import com.android.tools.idea.wizard.template.ModuleTemplateData
import com.android.tools.idea.wizard.template.RecipeExecutor
import com.android.tools.idea.wizard.template.impl.activities.common.generateManifest
import other.activity.res.layout.createActivityXml
import other.activity.src.app_package.createActivityKt
/**
* @Author hubert
* @Date 2022/5/1 1:02 下午
*/
fun RecipeExecutor.createActivityRecipe(
moduleData: ModuleTemplateData,
packageName: String,
activityClass: String,
layoutName: String,
hasNavigation:Boolean
) {
val (projectData, srcOut, resOut) = moduleData
val ktOrJavaExt = projectData.language.extension
// generateManifest(moduleData: com.android.tools.idea.wizard.template.ModuleTemplateData, activityClass: kotlin.String, packageName: kotlin.String,
// isLauncher: kotlin.Boolean,
// hasNoActionBar: kotlin.Boolean,
// activityThemeName: kotlin.String /* = compiled code */,
// isNewModule: kotlin.Boolean /* = compiled code */,
// isLibrary: kotlin.Boolean /* = compiled code */,
// manifestOut: java.io.File /* = compiled code */,
// baseFeatureResOut: java.io.File /* = compiled code */,
// generateActivityTitle: kotlin.Boolean,
// isResizeable: kotlin.Boolean /* = compiled code */): kotlin.Unit { /* compiled code */
// }
generateManifest(
moduleData = moduleData,
activityClass = "${activityClass}Activity",
packageName = packageName,
isLauncher = false,
hasNoActionBar = false,
isNewModule = false,
isLibrary = false,
// manifestOut = ,
// baseFeatureResOut = ,
generateActivityTitle = false,
isResizeable = false,
)
val createActivity = createActivityKt(projectData.applicationPackage,packageName, activityClass, layoutName, hasNavigation)
// 保存Activity
save(createActivity, srcOut.resolve("${activityClass}Activity.${ktOrJavaExt}"))
// 保存xml
save(createActivityXml(packageName, activityClass,hasNavigation), resOut.resolve("layout/${layoutName}.xml"))
}
3.2.4 在activity/src
目录下新建createActivityTemplate
文件
package other.activity.src
import com.android.tools.idea.wizard.template.*
import com.android.tools.idea.wizard.template.impl.activities.common.MIN_API
import java.io.File
/**
* @Author hubert
* @Date 2022/5/1 1:42 下午
*/
val createActivityTemplate
get() = template {
name = "Child Activity"
description = "继承自BaseActivity的Activity"
minApi = MIN_API
category = Category.Other
formFactor = FormFactor.Mobile
screens = listOf(WizardUiContext.ActivityGallery, WizardUiContext.MenuEntry, WizardUiContext.NewProject, WizardUiContext.NewModule)
val activityClass = stringParameter {
name = "Activity Name"
default = "Main"
help = "只输入名字,不要包含Activity"
constraints = listOf(Constraint.NONEMPTY)
}
val layoutName = stringParameter {
name = "Layout Name"
default = "activity_main"
help = "请输入布局的名字"
constraints = listOf(Constraint.LAYOUT,Constraint.UNIQUE,Constraint.NONEMPTY)
suggest = { activityToLayout(activityClass.value.toLowerCase()) }
}
val hasNavigation = booleanParameter {
name = "Has a Navigation"
default = true
help = "若勾选,自动添加导航栏"
}
val packageName = stringParameter {
name = "Package name"
visible = { !isNewModule }
default = "com.hw.lzjr"
constraints = listOf(Constraint.PACKAGE)
suggest = { packageName }
}
widgets(
TextFieldWidget(activityClass),
TextFieldWidget(layoutName),
CheckBoxWidget(hasNavigation),
PackageNameWidget(packageName)
)
recipe = { data:TemplateData ->
createActivityRecipe(
data as ModuleTemplateData,
packageName.value,
activityClass.value,
layoutName.value,
hasNavigation.value
)
}
thumb { File("images/template_child_activity.png") }
}
3.3 添加Activity模板
到WizardTemplateProviderImpl
class WizardTemplateProviderImpl: WizardTemplateProvider() {
override fun getTemplates(): List<Template> = listOf(
//自定义模板就添加在此处
createActivityTemplate
)
}
4 生成插件,安装使用
4.1 执行gradle任务获取插件
执行Run Plugin
后,在build/libs
目录下即可获取生成的pluginhw-child-1.0.0.jar
,如下图
4.2 安装插件
4.3 使用插件新建Activity
【注】 插件缩略图展示为no_activity.png 未按照配置展示,原因不明。
- 联系方式:微信账号:sxfxwlh