IDEA之MVP插件开发思路

426 阅读2分钟

背景

写一个IDE插件的目的永远都是提高工作效率。当有以下几种场景的时候,那么你就可以尝试去写一个插件了。

  • 一个页面或功能有比较多的模板代码。如GsonFormat能快捷生成数据类型。
  • 在一个团队中遇到协作性的问题,可以用插件来解决的。如 git 的可视化操作。

环境准备

这是一篇环境搭建和基础知识的教程

MVP插件

环境搭建按照教程走一遍就好,要开发一个插件,还是要自己亲手撸一个之后才有感觉。

首先创建一个名为 MvpPluginDemo 的项目, 第一步就是在plugin.xml把信息填写好:

<idea-plugin>
  // 填写唯一id
  <id>com.test.mvp</id>
  // 插件名
  <name>MvpPluginDemo</name>
  // 版本号
  <version>1.0</version>
  // 链接信息
  <vendor email="my@gmail.com" url="http://www.yoursite.com">我/公司</vendor>
  //描述
  <description><![CDATA[
      这是一个插件的描述,用户搜索下载时会显示描述。这个描述尽量满足50字,巴拉巴拉巴拉。。
    ]]></description>
  //迭代信息
  <change-notes><![CDATA[
      这是版本更新时,更新内容的描述,也是尽量满足50字,巴拉拉拉拉拉拉拉拉拉拉拉。。
    ]]>
  </change-notes>

  <!-- please see https://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/build_number_ranges.html for description -->
  <idea-version since-build="173.0"/>

  <!-- please see https://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/plugin_compatibility.html
       on how to target different products -->
  <depends>com.intellij.modules.platform</depends>

  <extensions defaultExtensionNs="com.intellij">
    <!-- Add your extensions here -->
  </extensions>

  <actions>
    <!-- Add your actions here -->
  </actions>

</idea-plugin>

一般情况下填写注释的那些信息即可。

创建Action

接着创建一个 Action,我们不需要手动填写 actions 信息,可以直接可视化构造:

Groups是指定创建的入口,这比较难以选择,根据描述很难判断具体的入口是哪个,只能靠自己去google或一点点试吧。如创建Mvp模板类代码与创建一个文件的操作类似,所以我们把它放在创建Kotlin文件的下方也算合理,after表示放在某个action后面。

开始编码

一个Action是长这样的:

public class MvpAction extends AnAction {

    @Override
    public void actionPerformed(AnActionEvent e) {
    
    }
}

编码阶段和写Java代码没太大差异,重点需要了解如何获取project的一些基本信息,即我们能从AnActionEvent里能获取什么,这也只能根据自己需要什么来研究调用对应的api。在这个例子我需要知道创建模板文件时的路径:

String dirPath = event.getData(PlatformDataKeys.VIRTUAL_FILE).getPath();

以上能获取当前创建mvp模板时的文件夹路径,然后执行生成文件的逻辑:

// 无论做什么,WriteCommandAction.runWriteCommandAction 是固定代码
WriteCommandAction.runWriteCommandAction(event.getProject(), () -> {
    String dirPath = event.getData(PlatformDataKeys.VIRTUAL_FILE).getPath();
    List<IGenerator> generatorList = new ArrayList<>();
    // 分别生成model、presenter、component、view、vo
    generatorList.add(new ModelGenerator());
    generatorList.add(new PresenterGenerator());
    generatorList.add(new ComponentGenerator());
    generatorList.add(new ViewGenerator());
    generatorList.add(new VoGenerator());
    for(IGenerator generator : generatorList) {
        generator.generateFile(dirPath);
    }
    // 以下代码就是为了刷新下项目,否则可能创建的文件可能不会时时生效
    event.getProject().getBaseDir().refresh(false, true);
    Timer timer = new Timer();
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            event.getProject().getBaseDir().refresh(false, true);
        }
    }, 1000);
});

因为本例中的mvp是定制的架构,所以就不细节到每一个文件了,随便选一个文件来看下具体生成代码的逻辑。

public class PresenterGenerator implements IGenerator{

    private static final String SUFFIX = "Presenter.kt";
    private static final String SYMBOL = "/";

    @Override
    public void generateFile(String dirPath) {
    		// moduleName是当前要创建模板文件的空目录
        String moduleName = GeneratorUtil.getModule(dirPath);
        String fileName = dirPath + SYMBOL + moduleName + SUFFIX;
        // 创建文件
        File file = new File(fileName);
        try {
        	  // 文件输出流写入
            FileOutputStream fileOutputStream = new FileOutputStream(file);
            OutputStreamWriter writer = new OutputStreamWriter(fileOutputStream);
            // 把模板代码写入,需要动态替换的地方用 replace 替换字符串。
            writer.append("package " + GeneratorUtil.getPackage(dirPath) + "\n\n");
            writer.append("import android.app.Activity\n" +
                    "import android.os.Bundle\n" +
                    "import com.alibaba.dinggov.ui.components.BaseComponentPresenter\n");
            writer.append(GeneratorUtil.getComment());
            writer.append("class $MODULEPresenter(activity: Activity) : BaseComponentPresenter<$MODULEView>() {\n\n".replace("$MODULE", moduleName));
            writer.append("    private var model: $MODULEModel = $MODULEModel()\n\n".replace("$MODULE", moduleName));
            writer.append("    override fun onStart(params: Bundle?) {\n");
            writer.append("        loadInitData()\n");
            writer.append("    }\n");
            writer.append("    private fun loadInitData() {\n");
            writer.append("        // TODO use model to load Data\n");
            writer.append("    }\n");
            writer.append("}\n");
            writer.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

不复杂的模板代码只需要用字符串一行行的拼接即可,逻辑也是相当简单的。最后需要生成jar:

如果是本地,直接添加jar就可以,需要放到仓库的看下其他上传的教程。

效果演示

最后看下效果演示: