Android深入

AndroidStudio Fox 编写自定义模板

2021-12-08  本文已影响0人  JiaYang627

写在前面 此文仅为记录AndroidStudio fox 编写自定义模板,网上关于此类文章少之又少,踩了不少坑,遂写文记录希望能帮到有需要的人。

原创文章 禁止未授权转载

开始


Use this template

我们需要在官方的template模板上进行编写,官方模板地址为:intellij-platform-plugin-template,打开模板仓库后,点击Use this template 会提示你 Create a new repository from intellij-platform-plugin-template 类似fork,创建好仓库后,git clone 到本地,并使用AS 打开

添加依赖

dependencies {
    compileOnly(files("lib/wizard-template.jar"))
}
    plugins {
    ...
    // detekt linter - read more: https://detekt.github.io/detekt/gradle.html
    id("io.gitlab.arturbosch.detekt") version "1.16.0"
    // ktlint linter - read more: https://github.com/JLLeitschuh/ktlint-gradle
    id("org.jlleitschuh.gradle.ktlint") version "10.0.0"
}


dependencies {
    detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:1.18.1")
}
plugins {
    // Java support
    id("java")
    // Kotlin support
    id("org.jetbrains.kotlin.jvm") version "1.6.0"
    // Gradle Changelog Plugin
    id("org.jetbrains.changelog") version "1.3.1"
    // Gradle Qodana Plugin
    id("org.jetbrains.qodana") version "0.1.13"
    // Gradle IntelliJ Plugin
    id("org.jetbrains.intellij") version "1.3.0"
    // detekt linter - read more: https://detekt.github.io/detekt/gradle.html
    id("io.gitlab.arturbosch.detekt") version "1.16.0"
    // ktlint linter - read more: https://github.com/JLLeitschuh/ktlint-gradle
    id("org.jlleitschuh.gradle.ktlint") version "10.0.0"
}


dependencies {
    detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:1.18.1")
    compileOnly(files("lib/wizard-template.jar"))
}

修改gradle.properties

AndroidStudioVersion
# IntelliJ Platform Artifacts Repositories
# -> https://plugins.jetbrains.com/docs/intellij/intellij-artifacts.html

pluginGroup = me.jiayang
pluginName = tin-mvvm
# SemVer format -> https://semver.org
pluginVersion = 1.0.72

# See https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
# for insight into build numbers and IntelliJ Platform versions.
pluginSinceBuild = 201
pluginUntilBuild = 213.*
pluginVerifierIdeVersions = 2020.2.4, 2020.3.4, 2021.1
# IntelliJ Platform Properties -> https://github.com/JetBrains/gradle-intellij-plugin#intellij-platform-properties
platformType = IC
platformVersion = 2020.2.4

# Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html
# Example: platformPlugins = com.intellij.java, com.jetbrains.php:203.4449.22
platformPlugins = java, com.intellij.java, org.jetbrains.android, android, org.jetbrains.kotlin

# Java language level used to compile sources and to generate the files for - Java 11 is required since 2020.3
javaVersion = 11

# Gradle Releases -> https://github.com/gradle/gradle/releases
gradleVersion = 7.3

# Opt-out flag for bundling Kotlin standard library.
# See https://plugins.jetbrains.com/docs/intellij/kotlin.html#kotlin-standard-library for details.
# suppress inspection "UnusedProperty"
kotlin.stdlib.default.dependency = false

修改包名以及创建Template生成类

Project.png
internal class MyProjectManagerListener : ProjectManagerListener {

    override fun projectOpened(project: Project) {
        projectInstance = project
        project.getService(MyProjectService::class.java)
    }

    override fun projectClosing(project: Project) {
        projectInstance = null
        super.projectClosing(project)
    }

    companion object {
        var projectInstance: Project? = null
    }
}
class SamplePluginTemplateProviderImpl: WizardTemplateProvider() {
    override fun getTemplates(): List<Template> = listOf(
        mvvmActivityTemplate
    )
}
<idea-plugin>
    <id>me.jiayang.tinmvvm</id>
    <name>TinMVVM</name>
    <vendor>jiayang</vendor>

    <depends>org.jetbrains.android</depends>
    <depends>org.jetbrains.kotlin</depends>
    <depends>com.intellij.modules.java</depends>
    <depends>com.intellij.modules.platform</depends>

    <extensions defaultExtensionNs="com.intellij">
        <applicationService serviceImplementation="me.jiayang.template.services.MyApplicationService"/>
        <projectService serviceImplementation="me.jiayang.template.services.MyProjectService"/>
    </extensions>

    <applicationListeners>
        <listener class="me.jiayang.template.listeners.MyProjectManagerListener"
                  topic="com.intellij.openapi.project.ProjectManagerListener"/>
    </applicationListeners>
    <extensions defaultExtensionNs="com.android.tools.idea.wizard.template">
        <wizardTemplateProvider implementation="me.jiayang.tinmvvm.SamplePluginTemplateProviderImpl" />
    </extensions>
</idea-plugin>

val mvvmActivityTemplate
    get() = template {
//        revision = 1
        name = "Quick Template"
        description = "一键创建 QuickAndroid 单个页面所需要的全部组件"
        minApi = MIN_API
        minApi = MIN_API
        category = Category.Other
        formFactor = FormFactor.Mobile
        screens = listOf(
            WizardUiContext.ActivityGallery,
            WizardUiContext.MenuEntry,
            WizardUiContext.NewProject,
            WizardUiContext.NewModule
        )


        val mRootPackageName = stringParameter {
            name = "Root Package Name"
            constraints = listOf(Constraint.PACKAGE)
            default = "com.jiayang.quickandroid"
            visible = { !isNewModule }
            help = "此 PluginTemplate 是针对 `QuickAndroid` 项目编写,默认包名为项目的包名,可根据自己需要填写"
        }

        val mPageName = stringParameter {
            name = "Create Page Name"
            constraints = listOf(Constraint.UNIQUE, Constraint.NONEMPTY)
            default = "Main"
            help = "需要生成页面的名字,不需要再写 名字后缀:如Activity、Fragment,会自动生成,以及对应文件名后缀"
        }

        val mIsFragment = booleanParameter {
            name = "Generate Fragment"
            default = false
            help = "是否需要创建 Fragment ? 不勾选则不生成"
        }

        val mIsLazyFragment = booleanParameter {
            name = "Fragment is Lazy ?"
            default = false
            visible = { mIsFragment.value}
            help = "是否 使创建的Fragment 为Lazy Fragment"
        }

        val mIsActivity = booleanParameter {
            name = "Generate Activity"
            default = true
            visible = { !mIsFragment.value }
            help = "是否需要创建 Activity ? 不勾选则不生成"
        }


        val mActivityLayoutName = stringParameter {
            name = "Activity Layout Name"
            default = "activity_main"
            constraints = listOf(Constraint.LAYOUT, Constraint.NONEMPTY)
            suggest = { activityToLayout(mPageName.value.toLowerCase()) }
            visible = { mIsActivity.value }
        }

        val mIsGenerateActivityLayout = booleanParameter {
            name = "Generate Activity Layout"
            default = true
            visible = { mIsActivity.value }
            help = "默认勾选,如果使用已存在布局 则无需勾选,若不勾选,创建后记得修改Act或 Fragment 绑定的视图文件!!!"
        }

        val mActivityPackageName = stringParameter {
            name = "Activity Package Name"
            constraints = listOf(Constraint.NONEMPTY)
            default = "ui"
            visible = { mIsActivity.value }
            help = "Activity 将被输出到此包下,请认真核实此包名是否是你需要输出的目标包名 (基于 Root Package Name )"
        }

        val mFragmentLayoutName = stringParameter {
            name = "Fragment Layout Name"
            default = "fragment_main"
            constraints = listOf(Constraint.LAYOUT, Constraint.NONEMPTY)
            suggest = { fragmentToLayout(mPageName.value.toLowerCase()) }
            visible = { mIsFragment.value }
        }
        val mIsGenerateFragmentLayout = booleanParameter {
            name = "Generate Fragment Layout"
            default = true
            visible = { mIsFragment.value }
            help = "默认勾选,如果使用已存在布局 则无需勾选,若不勾选,创建后记得修改Act或 Fragment 绑定的视图文件!!!"
        }

        val mFragmentPackageName = stringParameter {
            name = "Fragment Package Name"
            constraints = listOf(Constraint.NONEMPTY)
            default = "ui"
            visible = { mIsFragment.value }
            help = "Fragment 将被输出到此包下,请认真核实此包名是否是你需要输出的目标包名 (基于 Root Package Name )"
        }


        widgets(
            PackageNameWidget(mRootPackageName),
            TextFieldWidget(mPageName),
            CheckBoxWidget(mIsFragment),
            CheckBoxWidget(mIsActivity),
            TextFieldWidget(mActivityLayoutName),
            CheckBoxWidget(mIsGenerateActivityLayout),
            TextFieldWidget(mActivityPackageName),
            TextFieldWidget(mFragmentLayoutName),
            CheckBoxWidget(mIsGenerateFragmentLayout),
            TextFieldWidget(mFragmentPackageName),
            CheckBoxWidget(mIsLazyFragment)
        )

        recipe = { data: TemplateData ->
            mvvmActivityRecipe(
                data as ModuleTemplateData,
                mRootPackageName.value,
                mPageName.value,
                mIsActivity.value,
                mActivityLayoutName.value,
                mIsGenerateActivityLayout.value,
                mActivityPackageName.value,
                false,
                mIsFragment.value,
                mIsLazyFragment.value,
                mFragmentLayoutName.value,
                mIsGenerateFragmentLayout.value,
                mFragmentPackageName.value
            )
        }

    }

fun createLayoutName(className: String): String {
    val array = className.toCharArray()
    val string = StringBuilder()
    array.forEach {
        if (it.isUpperCase()) {
            //第一个首字母大写的话 不加下划线
            if (string.isNotEmpty()) {
                string.append("_")
            }
            string.append(it.toLowerCase())
        } else {
            string.append(it)
        }
    }
    return string.toString()
}



mvvmActivityRecipe 代码:

fun RecipeExecutor.mvvmActivityRecipe(
    moduleTemplateData: ModuleTemplateData,
    mRootPackageName: String,
    mPageName: String,
    mIsActivity: Boolean,
    mActivityLayoutName: String,
    mIsGenerateActivityLayout: Boolean,
    mActivityPackageName: String,
    mIsUseHilt: Boolean,
    mIsFragment :Boolean,
    mIsLazyFragment : Boolean,
    mFragmentLayoutName : String,
    mIsGenerateFragmentLayout : Boolean,
    mFragmentPackageName : String
) {
    val (projectData, srcOut, resOut) = moduleTemplateData
    val ktOrJavaExt = projectData.language.extension



    if (mIsActivity) {

        generateManifest(
            moduleData = moduleTemplateData,
            activityClass = "${mPageName}Activity",
            packageName = ".${mActivityPackageName.replace("/",".")}",
            isLauncher = false,
            hasNoActionBar = false,
            generateActivityTitle = false
        )

        val mvvmActivity = mvvmActivityKt(mRootPackageName, mActivityPackageName.replace("/","."), mPageName)
        // 保存Activity
        save(
            mvvmActivity,
            srcOut.resolve("${mActivityPackageName}/${mPageName}Activity.${ktOrJavaExt}")
        )
        if (mIsGenerateActivityLayout) {
            // 保存xml
            save(mvvmXml(), resOut.resolve("layout/${mActivityLayoutName}.xml"))
        }
        // 保存viewmodel
        save(
            mvvmViewModelKt(mRootPackageName, mActivityPackageName.replace("/","."), mPageName),
            srcOut.resolve("${mActivityPackageName}/${mPageName}ViewModel.${ktOrJavaExt}")
        )
        // 保存repository
        save(
            mvvmRepositoryKt(mRootPackageName, mActivityPackageName.replace("/","."), mPageName),
            srcOut.resolve("${mActivityPackageName}/${mPageName}Repository.${ktOrJavaExt}")
        )
    } else if (mIsFragment){
        val mvvmFragment: String = if (mIsLazyFragment) {

            mvvmLazyFragmentKt(mRootPackageName, mFragmentPackageName.replace("/","."), mPageName)
        } else {
            mvvmFragmentKt(mRootPackageName, mFragmentPackageName.replace("/","."), mPageName)
        }

        // 保存Fragment
        save(
            mvvmFragment,
            srcOut.resolve("${mFragmentPackageName}/${mPageName}Fragment.${ktOrJavaExt}")
        )
        if (mIsGenerateFragmentLayout) {
            // 保存xml
            save(mvvmXml(), resOut.resolve("layout/${mFragmentLayoutName}.xml"))
        }
        // 保存viewmodel
        save(
            mvvmViewModelKt(mRootPackageName, mFragmentPackageName.replace("/","."), mPageName),
            srcOut.resolve("${mFragmentPackageName}/${mPageName}ViewModel.${ktOrJavaExt}")
        )
        // 保存repository
        save(
            mvvmRepositoryKt(mRootPackageName, mFragmentPackageName.replace("/","."), mPageName),
            srcOut.resolve("${mFragmentPackageName}/${mPageName}Repository.${ktOrJavaExt}")
        )
    }
}

mvvmActivityKt.kt 代码:


fun mvvmActivityKt(
    mRootPackageName:String?,
    mActivityPackageName:String,
    mPageName:String
)="""
package ${mRootPackageName}.${mActivityPackageName}

import android.content.Intent
import android.os.Build
import android.os.Bundle
import androidx.activity.viewModels
import dagger.hilt.android.AndroidEntryPoint
import $mRootPackageName.databinding.Activity${mPageName}Binding
import $mRootPackageName.R
import $mRootPackageName.base.BaseActivity
import ${mRootPackageName}.${mActivityPackageName}.${mPageName}ViewModel


@AndroidEntryPoint
class ${mPageName}Activity : BaseActivity<Activity${mPageName}Binding>() {
    // use hilt
    val mViewModel : ${mPageName}ViewModel by viewModels()

    override fun initActivity(savedInstanceState: Bundle?) {
    }
}
"""

编译

编写好后 点击 Gradle - Task - Build - jar,编译好后 会在 项目根目录-build-libs文件夹下生成jar包,名字为 项目名-版本号.jar

可能你的Gradle 页面是这样的:

Gradle-No-Task

点击Task list not built..

去掉Gradle 下第一个 Do not build Gradle task list ..的勾选

Gradle-Setting

拿着编译好的jar包,打开AS Settings/Preferences > Plugins > ⚙️ > Install plugin from disk... > RESTART IDE

点击app 或者包名 右键使用模板

TemplateUse.png TemplateDetails.png

最后

编写匆忙 恐有疏漏 如有不足尽情谅解

鸣谢

鸿洋 : Android Studio自定义模板 写页面竟然可以如此轻松

敲代码的鱼哇 : Android Studio自定义页面模板

亿特博客 : AndroidStudio4.1 自定义模板

上一篇 下一篇

猜你喜欢

热点阅读