Android Studio 自定义模板

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目录下新建包层级结构如下

plugin-4.png

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任务获取插件

plugin-5.png

执行Run Plugin 后,在build/libs目录下即可获取生成的pluginhw-child-1.0.0.jar,如下图

plugin-6.png

4.2 安装插件

plugin-7.png

plugin-8.png

4.3 使用插件新建Activity

plugin-9.png

plugin-10.png

【注】 插件缩略图展示为no_activity.png 未按照配置展示,原因不明。

  • 联系方式:微信账号:sxfxwlh
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容