持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情
前面我们讲过了IDEA开发的基础篇和进阶篇,现在我们来看看高级篇。
进度条的使用
ProgressManager.getInstance().run(object : Task.Backgroundable(project, title) {
override fun run(indicator: ProgressIndicator) {
// 这里用indicator显示任务完成的进度
// indicator.text = "进度显示的文本"
// indicator.fraction = 0.5 进度的百分比
}
}
有了进度条,我们就再也不用担心进度完成情况了。
action图标配置
<action icon="AllIcons.Actions.Compile"></action>
图标方便我们找到这个action。sdk内置了很多图标,我们可以就地取材。
action快捷键配置
<action>
<keyboard-shortcut
keymap="$default"
first-keystroke="control alt D"
second-keystroke="E"/>
</action>
用来配置第一快捷键和第二快捷键。
action按钮位置配置
<action>
<add-to-group group-id="BuildMenu" anchor="last"/>
</action>
用来配置action按钮的位置。group-id表示我们要添加到哪个大的分组,而anchor代表是添加到这个分组的最前面还是最后面。
使用Android Studio的模板生成sdk
好戏现在开始了。我们不仅可以完成从零开发一个action,还可以基于Android Studio的组件模板sdk更方便地开发Activity和Fragment等组件的生成器。我们要先在Android Studio的安装目录找到wizard-template.jar。巧妇难为无米之炊,没有sdk怎么行?导入sdk,开始写代码。
首先,你需要定义一个WizardTemplateProvider,来作为模块生成的入口。这个类相当于一个模板清单,表示你开发了哪些模板生成功能。
class DoraTemplateWizardProvider: WizardTemplateProvider() {
override fun getTemplates() = listOf(MVVMActivityTemplate, MVVMFragmentTemplate)
}
找到resources/META-INF/plugins.xml加上以下代码。
<extensions defaultExtensionNs="com.android">
<tools.idea.wizard.template.wizardTemplateProvider
implementation="com.yourdomain.templates.recipes.DoraTemplateWizardProvider"/>
</extensions>
然后,也看模板相关信息的类。
object MVVMActivityTemplate : Template {
override val category: Category
get() = Category.Activity
override val constraints: Collection<TemplateConstraint>
get() = emptyList() //AndroidX, kotlin
override val description: String
get() = "创建一个dora.MVVMActivity,来自https://github.com/dora4/dora"
override val documentationUrl: String?
get() = null
override val formFactor: FormFactor
get() = FormFactor.Mobile
override val minCompileSdk: Int
get() = MIN_API
override val minSdk: Int
get() = MIN_API
override val name: String
get() = "MVVM Activity"
override val recipe: Recipe
get() = {
mvvmActivityRecipe(
it as ModuleTemplateData,
activityClassInputParameter.value,
activityTitleInputParameter.value,
layoutNameInputParameter.value,
packageName.value
)
}
override val revision: Int
get() = 1
override val uiContexts: Collection<WizardUiContext>
get() = listOf(WizardUiContext.ActivityGallery, WizardUiContext.MenuEntry, WizardUiContext.NewProject, WizardUiContext.NewModule)
override val widgets: Collection<Widget<*>>
get() = listOf(
TextFieldWidget(activityTitleInputParameter),
TextFieldWidget(activityClassInputParameter),
TextFieldWidget(layoutNameInputParameter),
PackageNameWidget(packageName),
LanguageWidget()
)
override fun thumb(): Thumb {
return Thumb { findResource(this.javaClass, File("template_mvvm_activity.png")) }
}
val activityClassInputParameter = stringParameter {
name = "Activity Name"
default = "MainActivity"
help = "The name of the activity class to create"
constraints = listOf(Constraint.CLASS, Constraint.UNIQUE, Constraint.NONEMPTY)
suggest = { layoutToActivity(layoutNameInputParameter.value) }
}
var layoutNameInputParameter: StringParameter = stringParameter {
name = "Layout Name"
default = "activity_main"
help = "The name of the layout to create for the activity"
constraints = listOf(Constraint.LAYOUT, Constraint.UNIQUE, Constraint.NONEMPTY)
suggest = { activityToLayout(activityClassInputParameter.value) }
}
val activityTitleInputParameter = stringParameter {
name = "Title"
default = "Main"
help = "The name of the activity. For launcher activities, the application title"
visible = { false }
constraints = listOf(Constraint.NONEMPTY)
suggest = { buildClassNameWithoutSuffix(activityClassInputParameter.value, "Activity") }
}
val packageName = defaultPackageNameParameter
}
category:该模板放在哪个分类下面,比如activity则放在创建activity的菜单里面 constraint:需要依赖的Android特性 description:描述信息 documentationUrl:文档url formFactor:表单因子,mobile代表手机 minCompileSdk:最低编译sdk minSdk:最低sdk name:action中显示的名称 recipe:秘诀,就是我们要生成模板的过程
接下来,我们可以定义模板生成的过程,主要定义要生成哪些文件。
fun RecipeExecutor.mvvmActivityRecipe(
moduleData: ModuleTemplateData,
activityClass: String,
activityTitle: String,
layoutName: String,
packageName: String
) {
val (projectData, srcOut, resOut) = moduleData
generateManifest(
moduleData = moduleData,
activityClass = activityClass,
activityTitle = activityTitle,
packageName = packageName,
isLauncher = false,
hasNoActionBar = false,
generateActivityTitle = false,
)
if (projectData.language.equals(Language.Kotlin)) {
save(mvvmActivityKt(projectData.applicationPackage ?: packageName, packageName, activityClass,
buildBindingName(layoutName), layoutName), srcOut.resolve("${activityClass}.${projectData.language.extension}"))
}
if (projectData.language.equals(Language.Java)) {
save(mvvmActivity(projectData.applicationPackage ?: packageName, packageName, activityClass,
buildBindingName(layoutName), layoutName), srcOut.resolve("${activityClass}.${projectData.language.extension}"))
}
save(mvvmActivityXml(packageName, activityClass), resOut.resolve("layout/${layoutName}.xml"))
open(resOut.resolve("layout/${layoutName}.xml"))
}
fun RecipeExecutor.mvvmFragmentRecipe(
moduleData: ModuleTemplateData,
fragmentClass: String,
layoutName: String,
packageName: String
) {
val (projectData, srcOut, resOut) = moduleData
if (projectData.language.equals(Language.Kotlin)) {
save(mvvmFragmentKt(projectData.applicationPackage ?: packageName, packageName, fragmentClass,
buildBindingName(layoutName), layoutName), srcOut.resolve("${fragmentClass}.${projectData.language.extension}"))
}
if (projectData.language.equals(Language.Java)) {
save(mvvmFragment(projectData.applicationPackage ?: packageName, packageName, fragmentClass,
buildBindingName(layoutName), layoutName), srcOut.resolve("${fragmentClass}.${projectData.language.extension}"))
}
save(mvvmFragmentXml(packageName, fragmentClass), resOut.resolve("layout/${layoutName}.xml"))
open(resOut.resolve("layout/${layoutName}.xml"))
}
val defaultPackageNameParameter
get() = stringParameter {
name = "Package Name"
default = "com.mycompany.myapp"
constraints = listOf(Constraint.UNIQUE, Constraint.NONEMPTY)
suggest = { packageName }
}
/**
* 将类名去掉最后一个类型的单词。
*/
fun buildClassNameWithoutSuffix(className: String, classNameSuffix: String): String {
if (className.endsWith(classNameSuffix)) {
return className.subSequence(0, className.length - classNameSuffix.length).toString()
}
return className
}
fun buildBindingName(layoutName: String) : String {
val builder = StringBuilder()
if (layoutName.contains("_")) {
for (section in layoutName.split("_")) {
builder.append(section[0].toUpperCase()).append(section.subSequence(1, section.length))
}
}
return builder.append("Binding").toString()
}
save:保存文件,将文件写入要生成的目录 open:打开文件,生成后是否打开预览
最后,写我们要生成的模板内容的代码,以Activity为例。这个就要用到kotlin的字符串模板特性了,对""" """不熟的同学可以复习下kotlin基础知识哦。
activity类生成
fun mvvmActivityKt(
applicationPackage: String,
packageName: String,
activityClass: String,
bindingName: String,
layoutName: String,
) = """
package ${packageName}
import android.os.Bundle
import dora.BaseActivity
import ${applicationPackage}.R
import ${applicationPackage}.databinding.${bindingName}
class ${activityClass} : BaseActivity<${bindingName}>() {
override fun getLayoutId(): Int {
return R.layout.${layoutName}
}
override fun initData(savedInstanceState: Bundle?) {
TODO("Not yet implemented")
}
}
"""
fun mvvmActivity(
applicationPackage: String,
packageName: String,
activityClass: String,
bindingName: String,
layoutName: String,
) = """
package ${packageName};
import android.os.Bundle;
import dora.BaseActivity;
import ${applicationPackage}.R;
import ${applicationPackage}.databinding.${bindingName};
public class ${activityClass} extends BaseActivity<${bindingName}> {
@Override
protected int getLayoutId() {
return R.layout.${layoutName};
}
@Override
public void initData(Bundle savedInstanceState) {
// TODO: Not yet implemented
}
}
"""
res文件生成
fun mvvmActivityXml(
packageName: String,
activityClass: String
) = """
<?xml version="1.0" encoding="utf-8"?>
<layout 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"
tools:context="${packageName}.${activityClass}">
<data>
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>
</layout>
"""