美文网首页Android
IntelliJ IDEA编写插件入门(1):自动创建代码

IntelliJ IDEA编写插件入门(1):自动创建代码

作者: zhongjh | 来源:发表于2017-12-21 20:37 被阅读145次

    当项目引入mvp框架的话,虽然代码结构逻辑简单了,但是创建类的过程太繁琐了并且都是千篇一律的,所以我们有没有这样的工具代替呢,答案是有的!

    在写这份文章之前,我是通过http://lib.csdn.net/article/android/63052该文章学习的,然后下面的内容是我在创建的过程记录的。如果下面文章依然看不懂的话,可以在这个链接下载源码更仔细能看到。但是不知道这个源码是不是旧版的原因,反正我这边是运行不了的。

    1. 开发工具下载

    下载IntelliJ IDEA,用过studio都知道它是在IntelliJ IDEA基础上开发的。下载地址:https://www.jetbrains.com/idea/
    我下载的是2017.3.1的版本。

    2. 创建项目

    image.png
    (1)点击Next,创建名称起个叫:MvpAutomaticCreation
    项目结构: image.png
    (2)点击src,在这里选择你的sdk地址(如果没有配置sdk的话,是不能使用一些别的功能的) image.png
    (3)一切配置完后,然后就可以开始创建类了,点击src,在这里创建Action image.png
    (4)填写Action信息
    image.png
    属性 描述
    Action ID 这个Action的唯一标示
    Class Name 类名称
    Name 这个插件在菜单上的名称
    Description 关于这个插件的描述信息
    Groups 代表这个插件会出现的位置。比如想让这个插件出现在Code菜单下的第一次选项,就如图中一样选择CodeMenu(Code),右边Anchor选择First
    Keyboard Shortcuts 快捷键设置。图中设置Alt+T。
    点击ok后,就创建完毕了,然后我们想要编辑的话,如图中的配置里面的actions,比如修改快捷键 image.png
    (5)编写代码
    在编写代码之前,我们肯定已经知道我们想要生成什么样的代码了,每个人写的框架都不一样,那么假设我们现在写的一个mvp是这么一个架构,图中的Base可以忽略,因为这是两个项目同时引用的这一个类。 image.png

    注意:我这边编写的生成类,因为考虑到实际使用,我是不写基类的生成的

    (5.1)首先是基于Activity的mvp有3个类,让我们先创建3个: image.png
    后缀名当然是txt了,创建如下: image.png 打开其中一个文件: image.png

    代码里面的$packagename、$basepackagename、$author、$description、$date、$name这些字符都是可以动态替换的。

    (5.2)开始创建插件ui了 image.png
    可视化编辑: image.png 最终效果: image.png

    (5.3)接下来解析对应view的控制类,请看注释

    import javax.swing.*;
    import java.awt.event.*;
    
    public class MvpAutomaticCreation extends JDialog {
        private JPanel contentPane;
        private JButton buttonOK;
        private JButton buttonCancel;
        private JTextField textField1;
        private JTextField textField2;
    
        private DialogCallBack mCallBack;
    
        /**
         * 在自动创建该类的时候,添加一个回调函数DialogCallBack,并且改变了onOK这个方法
         * @param callBack 回调函数
         */
        public MvpAutomaticCreation(DialogCallBack callBack) {
            this.mCallBack = callBack;
            setTitle("MvpAutomaticCreation");
            setContentPane(contentPane);
            setModal(true);
            getRootPane().setDefaultButton(buttonOK);
            buttonOK.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    onOK();
                }
            });
            buttonCancel.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    onCancel();
                }
            });
            // call onCancel() when cross is clicked
            setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
            addWindowListener(new WindowAdapter() {
                public void windowClosing(WindowEvent e) {
                    onCancel();
                }
            });
            // call onCancel() on ESCAPE
            contentPane.registerKeyboardAction(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    onCancel();
                }
            }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
        }
    
        private void onOK() {
            // add your code here
            if (null != mCallBack){
                mCallBack.ok(textField1.getText().trim(), textField2.getText().trim());
            }
            dispose();
        }
    
        private void onCancel() {
            // add your code here if necessary
            dispose();
        }
    
        // 这个作废,去掉,无用
    //    public static void main(String[] args) {
    //        MvpAutomaticCreation dialog = new MvpAutomaticCreation();
    //        dialog.pack();
    //        dialog.setVisible(true);
    //        System.exit(0);
    //    }
    
        public interface DialogCallBack{
            void ok(String author, String moduleName);
        }
    
    }
    

    (5.4)然后接着看执行类

    import com.intellij.openapi.actionSystem.AnAction;
    import com.intellij.openapi.actionSystem.AnActionEvent;
    import com.intellij.openapi.actionSystem.PlatformDataKeys;
    import com.intellij.openapi.project.Project;
    import com.intellij.openapi.ui.Messages;
    import org.w3c.dom.Document;
    import org.w3c.dom.Element;
    import org.w3c.dom.Node;
    import org.w3c.dom.NodeList;
    
    import javax.xml.parsers.DocumentBuilder;
    import javax.xml.parsers.DocumentBuilderFactory;
    import java.io.*;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    /**
     * 执行类
     * (1)会获取包名,然后读取模板文件,替换模板文件中动态字符,在Dialog输入的作者和模块名称也会替换模板中字符,
     * (2)最后通过包名路径生成类文件
     *
     *  后面我会根据实际工作需求,会想办法改进选择生成fragment还是activity
     *  而作者名称也应该能设置是默认的
     *
     */
    public class MvpAutomaticCreationAction extends AnAction {
    
        private Project project;
        private String packageName = "";//包名
        private String mAuthor;//作者
        private String mModuleName;//模块名称
    
        /**
         * 创建类型枚举
         */
        private enum  CodeType {
            Activity, Fragment, Contract, Presenter, BaseView, BasePresenter
        }
    
        @Override
        public void actionPerformed(AnActionEvent e) {
            // TODO: insert action logic here
            project = e.getData(PlatformDataKeys.PROJECT);
            packageName = getPackageName();
            refreshProject(e);
            init();
        }
    
        /**
         * 刷新项目
         * @param e
         */
        private void refreshProject(AnActionEvent e) {
            e.getProject().getBaseDir().refresh(false, true);
        }
    
        /**
         * 初始化Dialog
         */
        private void init(){
            MvpAutomaticCreation myDialog = new MvpAutomaticCreation(new MvpAutomaticCreation.DialogCallBack() {
                @Override
                public void ok(String author, String moduleName) {
                    // 实例化ok事件
                    mAuthor = author;
                    mModuleName = moduleName;
                    createClassFiles();
                    Messages.showInfoMessage(project,"create mvp code success","title");
                }
            });
            myDialog.setVisible(true);
    
        }
    
        /**
         * 生成类文件
         */
        private void createClassFiles() {
            createClassFile(CodeType.Activity);
            createClassFile(CodeType.Fragment);
            createClassFile(CodeType.Contract);
            createClassFile(CodeType.Presenter);
    //        createBaseClassFile(CodeType.BaseView); // 暂时作废
    //        createBaseClassFile(CodeType.BasePresenter); // 暂时作废
        }
    
        /**
         * 生成mvp框架代码
         * @param codeType 类型
         */
        private void createClassFile(CodeType codeType) {
            String fileName = "";
            String content = "";
            String appPath = getAppPath();
            switch (codeType){
                case Activity:
                    fileName = "TemplateActivity.txt";
                    content = ReadTemplateFile(fileName);
                    content = dealTemplateContent(content);
                    writeToFile(content, appPath + mModuleName.toLowerCase(), mModuleName + "Activity.java");
                    break;
                case Fragment:
                    fileName = "TemplateFragment.txt";
                    content = ReadTemplateFile(fileName);
                    content = dealTemplateContent(content);
                    writeToFile(content, appPath + mModuleName.toLowerCase(), mModuleName + "Fragment.java");
                    break;
                case Contract:
                    fileName = "TemplateContract.txt";
                    content = ReadTemplateFile(fileName);
                    content = dealTemplateContent(content);
                    writeToFile(content, appPath + mModuleName.toLowerCase(), mModuleName + "Contract.java");
                    break;
                case Presenter:
                    fileName = "TemplatePresenter.txt";
                    content = ReadTemplateFile(fileName);
                    content = dealTemplateContent(content);
                    writeToFile(content, appPath + mModuleName.toLowerCase(), mModuleName + "Presenter.java");
                    break;
            }
        }
    
        /**
         * 生成
         * @param content 类中的内容
         * @param classPath 类文件路径
         * @param className 类文件名称
         */
        private void writeToFile(String content, String classPath, String className) {
            try {
                File floder = new File(classPath);
                if (!floder.exists()){
                    floder.mkdirs();
                }
    
                File file = new File(classPath + "/" + className);
                if (!file.exists()) {
                    file.createNewFile();
                }
    
                FileWriter fw = new FileWriter(file.getAbsoluteFile());
                BufferedWriter bw = new BufferedWriter(fw);
                bw.write(content);
                bw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
    
    
        /**
         * 替换模板中字符
         * @param content
         * @return
         */
        private String dealTemplateContent(String content) {
            content = content.replace("$name", mModuleName);
            if (content.contains("$packagename")){
                content = content.replace("$packagename", packageName + "." + mModuleName.toLowerCase());
            }
            if (content.contains("$basepackagename")){
                content = content.replace("$basepackagename", packageName + ".base");
            }
            content = content.replace("$author", mAuthor);
            content = content.replace("$date", getDate());
            return content;
        }
    
        /**
         * 获取包名文件路径
         * @return
         */
        private String getAppPath(){
            String packagePath = packageName.replace(".", "/");
            String appPath = project.getBasePath() + "/App/src/main/java/" + packagePath + "/";
            return appPath;
        }
    
        /**
         * 获取当前时间
         * @return
         */
        public String getDate() {
            Date currentTime = new Date();
            SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd");
            String dateString = formatter.format(currentTime);
            return dateString;
        }
    
        /**
         * 从AndroidManifest.xml文件中获取当前app的包名
         * @return 当前app的包名
         */
        private String getPackageName() {
            String package_name = "";
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            try {
                DocumentBuilder db = dbf.newDocumentBuilder();
                Document doc = db.parse(project.getBasePath() + "/App/src/main/AndroidManifest.xml");
    
                NodeList nodeList = doc.getElementsByTagName("manifest");
                for (int i = 0; i < nodeList.getLength(); i++){
                    Node node = nodeList.item(i);
                    Element element = (Element) node;
                    package_name = element.getAttribute("package");
                }
    
            } catch (Exception e) {
                e.printStackTrace();
            }
            return package_name;
        }
    
        /**
         * 读取模板文件中的字符内容
         * @param fileName 模板文件名
         */
        private String ReadTemplateFile(String fileName) {
            InputStream in = null;
            in = this.getClass().getResourceAsStream("/Template/" + fileName);
            String content = "";
            try {
                content = new String(readStream(in));
            } catch (IOException e) {
                e.printStackTrace();
            }
            return content;
        }
    
        /**
         * 读取数据
         * @param inputStream
         */
        private byte[] readStream(InputStream inputStream) throws IOException {
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len;
            try {
                while ((len = inputStream.read(buffer)) != -1){
                    outputStream.write(buffer, 0, len);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                outputStream.close();
                inputStream.close();
            }
    
            return outputStream.toByteArray();
        }
    
    }
    
    
    
    (6)部署插件
    (6.1)找到该文件,填一些资料 image.png image.png 记住这个版本号要改成145,否则Android Studio导入会报不兼容问题 image.png

    (6.2)然后生成


    image.png

    会创建一个jar包,拿到这个jar包就可以安装到Android Studio了。


    image.png
    (7)部署插件

    点击Install plugin from disk...,选择自己生成的jar,就能导入成功了。


    image.png 然后重启Android studio,在菜单这里就能看到了 image.png
    (7) 错误汇总

    当点击发现没任何反应的时候,我们查询bug发现这么一个提示:

    null
    java.lang.NullPointerException
    at com.intellij.ide.SystemHealthMonitor.getActionName
    

    注意:在创建自定义的XXAction类时,需要保证自己的XXAction类在某个package中,否则会出现如下之类的报错:

    示例如我google中查询:


    image.png

    最近找到一个很不错的插件源码合集
    https://github.com/balsikandar/Android-Studio-Plugins
    http://wiki.jikexueyuan.com/project/intellij-idea-tutorial/plugins-develop.html

    相关文章

      网友评论

        本文标题:IntelliJ IDEA编写插件入门(1):自动创建代码

        本文链接:https://www.haomeiwen.com/subject/opgwwxtx.html