AndroidStudio插件开发—MVP生成代码插件

简介

之前一篇介绍了如何去开发一个简单的AnroidStudio插件,这一篇详细讲解如何去做一个生成MVP中我们经常重复用到的通用代码插件。

要求

1.根据定义好的Contract.java文件生成对应的Model.java文件,Presenter.java文件,以及构造Contract内部通用代码
2.生成的代码可以被git作为新生成的文件

一:实现Contract内部代码

首先完成Contract内部代码的编写,这里需要了解插件开发的相关API,参考:API
我的mvp是基于T-MVP利用泛型去实现,所以需要用到Model,View,Presenter,另外还有一个用于存放这几个接口Contract契约类,结构如下:

图片.png

至于MVP架构如何设计,网上框架多多,这里主要分析如何去编写插件,老规矩,先新建项目MVPDataBind,t填好常用的几个参数,如下:
图片.png

接着新建一个Action动作,然后在actionPerformed方法中编写,主要实现思路是我们要通过当前获取到的文件得到文件类名,判断文件名是否符合规则,然后通过Document改变当前文件内容:
网上对于Document介绍:Document其实就是Virtual File的内容的字符序列,所以对Document的各种操作都是基于普通文本的。Document的可操作的方法并不多,主要是Document是基于字符序列的,操作起来难度有点大。事实上我们对Document的使用也比较少,通常都是一些信息的简单获取。
代码如下:

 private void createContractContent() {
        int lastIndex = this.content.lastIndexOf("}");
        this.content = this.content.substring(0, lastIndex);
        MessagesCenter.showDebugMessage(this.content, "debug");
        final String content = this.getContractContent();
        WriteCommandAction.runWriteCommandAction(this.editor.getProject(), new Runnable() {
            public void run() {
                MVPDataBind.this.editor.getDocument().setText(content);
            }
        });
    }

    private String getContractContent() {
        return this.content.trim() + "\n\n    interface View extends BaseView {\n      \n    }\n\n    interface Model extends BaseModel {\n        \n    }\n\n    abstract class Presenter extends BasePresenter<Model, View> {\n        \n    }\n\n}";
    }

然后是我们要先判断操作的当前文件是否合法,命名必须要xxxContract.java:

 private boolean checkCanUse() {
        this.content = this.editor.getDocument().getText();
        String[] words = this.content.split(" ");
        String[] var2 = words;
        int var3 = words.length;

        for (int var4 = 0; var4 < var3; ++var4) {
            String word = var2[var4];
            if (word.contains("Contract")) {
                String className = word.substring(0, word.indexOf("Contract"));
                this.classModel.setFunctionName(className);
                this.classModel.setClassName(word);
                MessagesCenter.showDebugMessage(className, "class name");
            }
        }

        if (TextUtils.isEmpty(this.classModel.getFunctionName())) {
            Messages.showErrorDialog(
                    "文件名定义不规范",
                    "Error");
//            MessagesCenter.showErrorMessage("Create failed ,Can't found 'Contract'  in your class name,your class name must contain 'Contract'", "error");
            return false;
        } else {
            return true;
        }
    }

如果只是Contract,则会弹框报错:


图片.png

到此,我们就实现了编写Contract文件里面的内容,这里要介绍一下

AnActionEvent

AnActionEvent 函数和update函数的形参都包含AnActionEvent对象。AnActionEvent对象是我们与IntelliJ IDEA交互的桥梁,我们可以通过AnActionEvent对象获取当前IntelliJ IDEA的各个模块对象,如编辑框窗口对象、项目窗口对象等,获取到这些对象我们就可以做一些定制的效果。

通过AnActionEvent对象的getData函数可以得到IDEA界面各个窗口对象以及各个窗口为实现某些特定功能的对象。getData函数需要传入DataKey<T>对象,用于指明想要获取的IDEA中的哪个对象。在CommonDataKeys已经定义好各个IDEA对象对应的DataKey<T>对象,不仅仅CommonDataKeys中定义了DataKey<T>对象,为了添加更多的DataKey<T>对象并且兼容等,又提供了PlatformDataKeys类,PlatformDataKeys类是CommonDataKeys子类,也就是说,只要是CommonDataKeys有的,PlatformDataKeys类都有。这里我们通过getData获取到当前的编辑框用于操作编辑框的内容:

 this.editor = (Editor) e.getData(PlatformDataKeys.EDITOR);

二.实现新增文件

Contract文件内容编辑好了,接下来就是要通过插件,新建文件了,插件开发中, 使用PSI Files来表示具体操作的文件.参考:https://www.jianshu.com/p/0117d4b1eb00
对于Java来说, 可以通过JavaDirectoryService#createClass(dir, fileName)来创建类文件, 同时会返回一个PsiClass实例表示该类.然后我们定义接口回调,执行后续的类实现和继承等操作,如下:

 public void generateFile(final PsiDirectory directory, final String fileName, final String type, final onFileGeneratedListener listener) {
        WriteCommandAction.runWriteCommandAction(myProject, new Runnable() {
            @Override
            public void run() {
                PsiClass[] psiClasses = myShortNamesCache.getClassesByName(fileName, myProjectScope);//NotNull
                PsiClass psiClass;
                PsiJavaFile javaFile;
                if (psiClasses.length != 0) {//if the class already exist.
                    psiClass = psiClasses[0];
                    javaFile = (PsiJavaFile) psiClass.getContainingFile();
                    javaFile.delete();//then delete the old one
                }//and re-generate one
                psiClass = myDirectoryService.createClass(directory, fileName, type);
                javaFile = (PsiJavaFile) psiClass.getContainingFile();
                PsiPackage psiPackage = myDirectoryService.getPackage(directory);
                javaFile.setPackageName(psiPackage.getQualifiedName());
                listener.onJavaFileGenerated(javaFile, psiClass);
            }
        });

这里是生成xxxModel文件的操作,先通过myDirectoryService.createClass(directory, fileName, type)创建类文件,然后通过查询之前定义得xxxContract得到需要实现的Model接口名,最后用add方法加入到新建的类里面,最后通过setModifierProperty设置类的修饰符为public,生成Presenter代码类似,把实现接口变为继承Presenter子类,代码如下:

    fileGenerator.generateFile(myCurrentDir, className + "Model", JavaTemplateUtil.INTERNAL_CLASS_TEMPLATE_NAME, new FileGenerator.onFileGeneratedListener() {
                @Override
                public void onJavaFileGenerated(PsiJavaFile javaFile, PsiClass psiClass) {
                    PsiClass contractClass = fileGenerator.myShortNamesCache.getClassesByName(fileGenerator.myPrefix + "Contract", fileGenerator.myProjectScope)[0];
                    PsiClass model = contractClass.findInnerClassByName("Model", false);//don't need to search base
                    psiClass.getImplementsList().add(fileGenerator.myFactory.createClassReferenceElement(model));
                    psiClass.getModifierList().setModifierProperty("public", true);//force 'public interface myPrefixContract'
                }
            });
 fileGenerator.generateFile(myCurrentDir, className + "Presenter", JavaTemplateUtil.INTERNAL_CLASS_TEMPLATE_NAME, new FileGenerator.onFileGeneratedListener() {
                @Override
                public void onJavaFileGenerated(PsiJavaFile javaFile, PsiClass psiClass) {
                    PsiClass contractClass = fileGenerator.myShortNamesCache.getClassesByName(fileGenerator.myPrefix + "Contract", fileGenerator.myProjectScope)[0];
                    PsiClass model = contractClass.findInnerClassByName("Presenter", false);//don't need to search base
                    psiClass.getExtendsList().add(fileGenerator.myFactory.createClassReferenceElement(model));
                    psiClass.getModifierList().setModifierProperty("public", true);//force 'public interface myPrefixContract'
                }
            });

文件创建完成之后编译打成jar包,导入AndroidStudio测试效果如下:


GIF.gif

基本达到了我们的需求

总结:

对于MVP架构,插件生成我们的常用契约类,对开发效率还是有一定的提升,免去了我们重复建类,命名的问题,不过文件的读写,创建只是插件开发的一点点,要想熟练插件开发,还需要阅读官方文档Api,多参考别人的插件开发实现原理,最后附上插件源码github

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,175评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,674评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,151评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,597评论 1 269
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,505评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,969评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,455评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,118评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,227评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,213评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,214评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,928评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,512评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,616评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,848评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,228评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,772评论 2 339

推荐阅读更多精彩内容