AndroidStudio插件開發(fā)—MVP生成代碼插件

簡介

之前一篇介紹了如何去開發(fā)一個簡單的AnroidStudio插件,這一篇詳細講解如何去做一個生成MVP中我們經(jīng)常重復(fù)用到的通用代碼插件艰争。

要求

1.根據(jù)定義好的Contract.java文件生成對應(yīng)的Model.java文件,Presenter.java文件灌具,以及構(gòu)造Contract內(nèi)部通用代碼
2.生成的代碼可以被git作為新生成的文件

一:實現(xiàn)Contract內(nèi)部代碼

首先完成Contract內(nèi)部代碼的編寫卧波,這里需要了解插件開發(fā)的相關(guān)API,參考:API
我的mvp是基于T-MVP利用泛型去實現(xiàn)淆游,所以需要用到Model,View,Presenter,另外還有一個用于存放這幾個接口Contract契約類,結(jié)構(gòu)如下:

圖片.png

至于MVP架構(gòu)如何設(shè)計隔盛,網(wǎng)上框架多多犹菱,這里主要分析如何去編寫插件,老規(guī)矩吮炕,先新建項目MVPDataBind,t填好常用的幾個參數(shù)腊脱,如下:
圖片.png

接著新建一個Action動作,然后在actionPerformed方法中編寫龙亲,主要實現(xiàn)思路是我們要通過當(dāng)前獲取到的文件得到文件類名陕凹,判斷文件名是否符合規(guī)則,然后通過Document改變當(dāng)前文件內(nèi)容:
網(wǎng)上對于Document介紹:Document其實就是Virtual File的內(nèi)容的字符序列鳄炉,所以對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}";
    }

然后是我們要先判斷操作的當(dāng)前文件是否合法,命名必須要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(
                    "文件名定義不規(guī)范",
                    "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

到此,我們就實現(xiàn)了編寫Contract文件里面的內(nèi)容空凸,這里要介紹一下

AnActionEvent

AnActionEvent 函數(shù)和update函數(shù)的形參都包含AnActionEvent對象嚎花。AnActionEvent對象是我們與IntelliJ IDEA交互的橋梁,我們可以通過AnActionEvent對象獲取當(dāng)前IntelliJ IDEA的各個模塊對象呀洲,如編輯框窗口對象紊选、項目窗口對象等啼止,獲取到這些對象我們就可以做一些定制的效果。

通過AnActionEvent對象的getData函數(shù)可以得到IDEA界面各個窗口對象以及各個窗口為實現(xiàn)某些特定功能的對象丛楚。getData函數(shù)需要傳入DataKey<T>對象族壳,用于指明想要獲取的IDEA中的哪個對象。在CommonDataKeys已經(jīng)定義好各個IDEA對象對應(yīng)的DataKey<T>對象趣些,不僅僅CommonDataKeys中定義了DataKey<T>對象,為了添加更多的DataKey<T>對象并且兼容等贰您,又提供了PlatformDataKeys類坏平,PlatformDataKeys類是CommonDataKeys子類,也就是說锦亦,只要是CommonDataKeys有的舶替,PlatformDataKeys類都有。這里我們通過getData獲取到當(dāng)前的編輯框用于操作編輯框的內(nèi)容:

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

二.實現(xiàn)新增文件

Contract文件內(nèi)容編輯好了杠园,接下來就是要通過插件顾瞪,新建文件了,插件開發(fā)中, 使用PSI Files來表示具體操作的文件.參考:http://www.reibang.com/p/0117d4b1eb00
對于Java來說, 可以通過JavaDirectoryService#createClass(dir, fileName)來創(chuàng)建類文件, 同時會返回一個PsiClass實例表示該類.然后我們定義接口回調(diào)抛蚁,執(zhí)行后續(xù)的類實現(xiàn)和繼承等操作陈醒,如下:

 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)創(chuàng)建類文件瞧甩,然后通過查詢之前定義得xxxContract得到需要實現(xiàn)的Model接口名钉跷,最后用add方法加入到新建的類里面,最后通過setModifierProperty設(shè)置類的修飾符為public肚逸,生成Presenter代碼類似爷辙,把實現(xiàn)接口變?yōu)槔^承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'
                }
            });

文件創(chuàng)建完成之后編譯打成jar包朦促,導(dǎo)入AndroidStudio測試效果如下:


GIF.gif

基本達到了我們的需求

總結(jié):

對于MVP架構(gòu)膝晾,插件生成我們的常用契約類,對開發(fā)效率還是有一定的提升务冕,免去了我們重復(fù)建類血当,命名的問題,不過文件的讀寫,創(chuàng)建只是插件開發(fā)的一點點洒疚,要想熟練插件開發(fā)歹颓,還需要閱讀官方文檔Api,多參考別人的插件開發(fā)實現(xiàn)原理油湖,最后附上插件源碼github

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末巍扛,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子乏德,更是在濱河造成了極大的恐慌撤奸,老刑警劉巖吠昭,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異胧瓜,居然都是意外死亡矢棚,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門府喳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蒲肋,“玉大人,你說我怎么就攤上這事钝满《嫡常” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵弯蚜,是天一觀的道長孔轴。 經(jīng)常有香客問我,道長碎捺,這世上最難降的妖魔是什么路鹰? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮收厨,結(jié)果婚禮上晋柱,老公的妹妹穿的比我還像新娘。我一直安慰自己帽氓,他們只是感情好趣斤,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著黎休,像睡著了一般浓领。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上势腮,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天联贩,我揣著相機與錄音,去河邊找鬼捎拯。 笑死泪幌,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的署照。 我是一名探鬼主播祸泪,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼建芙!你這毒婦竟也來了没隘?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤禁荸,失蹤者是張志新(化名)和其女友劉穎右蒲,沒想到半個月后阀湿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡瑰妄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年陷嘴,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片间坐。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡灾挨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出竹宋,到底是詐尸還是另有隱情涨醋,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布逝撬,位于F島的核電站,受9級特大地震影響乓土,放射性物質(zhì)發(fā)生泄漏宪潮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一趣苏、第九天 我趴在偏房一處隱蔽的房頂上張望狡相。 院中可真熱鬧,春花似錦食磕、人聲如沸尽棕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽滔悉。三九已至,卻和暖如春单绑,著一層夾襖步出監(jiān)牢的瞬間回官,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工搂橙, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留歉提,地道東北人。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓区转,卻偏偏與公主長得像苔巨,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子废离,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理侄泽,服務(wù)發(fā)現(xiàn),斷路器厅缺,智...
    卡卡羅2017閱讀 134,659評論 18 139
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,163評論 25 707
  • 最近有一個想法窄刘,想把自己深入思考的一些問題,用本子記錄下來舷胜,寫成文章娩践。比如說,如何看待閱讀烹骨,如何自律翻伺,如何學(xué)習(xí)新的...
    奇跡先生李憨憨閱讀 275評論 1 0
  • 一位看起來極其苗條的女同事在某個午餐時間宣稱:我要減肥吨岭,不和你們?nèi)コ燥埩恕1娙艘粫r憤懣:如果你都要減肥峦树,那我們該怎...
    流蘇月白閱讀 226評論 0 0