别再羡慕别人了!教你如何轻松掌握IDEA插件开发,成为编程高手!(二)UI设计与开发

1,681 阅读6分钟

✨这里是第七人格的博客✨小七,欢迎您的到来~✨

🍅系列专栏:实战🍅

✈️本篇内容: UI设计与开发✈️

🍱本篇收录完整代码地址:gitee.com/diqirenge/i…🍱

楔子

前篇 《别再羡慕别人了!教你如何轻松掌握IDEA插件开发,成为编程高手!(一)》 我们讲了开发环境的搭建 ,接下来,为了更好的掌握IDEA插件开发,我们给自己定一个要求,完成一个代码生成器的开发。

UI设计

基本构思

我们来思考一下,一个简单的代码生成器需要些什么东西?

首先,因为我们是根据数据库表来生成的代码,所以肯定需要有录入数据库配置的地方;

其次,我们不可能一次性生成所有的表,所以需要有查询、展示以及选择表的地方;

最后,最好可以选择输入输出的位置,也就是说可以自己选择模版(输入),自己选择代码生成的位置(输出)。

构思图

结合以上思考,我们将我们的插件分为如下4个部分 001 UI设计.png

UI图

构思图添加细节后如下,并且将该插件放在IDEA右边侧比较方便

构思图2.jpg

分支名称

231027-52javaee.com-BuildUI

仓库地址

gitee.com/diqirenge/i…

分支描述

构造UI

代码实现

005构建UI.jpg

通过拖拽生成UI相关文件.jpg

增加右侧窗口创建方法

/**
 * 代码生成工具窗口工厂
 * 关注公众号【奔跑的码畜】,一起进步不迷路
 *
 * @author 第七人格
 * @date 2023/10/27
 */
public class CodeGenerateWindowFactory implements ToolWindowFactory {

    /**
     * 创建右侧窗口
     *
     * @param project    项目
     * @param toolWindow 工具窗口
     */
    @Override
    public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) {
        // 获取内容工厂的实例
        ContentFactory contentFactory = ContentFactory.getInstance();
        CodeGenerateUi codeGenerateUi = new CodeGenerateUi(project);
        // 获取 ToolWindow 显示的内容
        Content content = contentFactory.createContent(codeGenerateUi.getMain(), "", false);
        // 设置 ToolWindow 显示的内容
        toolWindow.getContentManager().addContent(content);
    }

}

修改plugin.xml插件配置

<idea-plugin>
  <id>com.run2code.idea.plugin.codegenerate</id>
  <name>Code-Generate</name>
  <vendor email="760470497@qq.com" url="https://52javaee.com">第七人格</vendor>

  <description><![CDATA[
    代码生成器,通过数据库以及表结构,生成PO、DAO、VO、Mapper XML、Service、Controller等。
    ]]></description>

  <!-- please see http://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 -->
    <!-- 窗体 (IDEA 界面右侧) -->
    <toolWindow id="Generate-Code" secondary="false" anchor="right" icon="/icons/logo.png"
                factoryClass="com.run2code.idea.plugin.codegenerate.factory.CodeGenerateWindowFactory"/>

  </extensions>
  
</idea-plugin>

修改CodeGenerateUi

初始化数据

在构造方法里面初始化我们的UI

public CodeGenerateUi(Project project) {
    // 初始化数据
    this.project = project;
    this.projectName.setText(project.getName());
    this.classpath.setText(project.getBasePath());
    // 增加监听
    addListener(project);
}

给对应事件增加监听

监听文件选择按钮

实现

/**
 * 增加监听
 *
 * @param project 工程
 */
private void addListener(Project project) {
    // 选择自定义模版目录
    listenFileChooserButton(this.templateUrlButton, project, "选择自定义模版目录", CodeGenerateUi.this.templateUrl);

    // 选择entity生成目录
    listenFileChooserButton(this.entityButton, project, "选择entity生成目录", CodeGenerateUi.this.entityUrl);

    // 选择mapper生成目录
    listenFileChooserButton(this.mapperUrlButton, project, "选择mapper生成目录", CodeGenerateUi.this.mapperUrl);

    // 选择xml生成目录
    listenFileChooserButton(this.xmlUrlButton, project, "选择xml生成目录", CodeGenerateUi.this.xmlUrl);

    // 测试数据库链接


    // 查询数据库表列表


    // 生成代码


    // 监听鼠标点击表格

}
/**
 * 监听文件选择按钮
 *
 * @param templateUrlButton
 * @param project
 * @param message
 */
private void listenFileChooserButton(JButton templateUrlButton, Project project, String message, JTextField templateUrl) {
    templateUrlButton.addActionListener(e -> {
        templateUrl.setText("默认输出地址");
        // todo 调用统一方法选择文件目录
        Messages.showInfoMessage(project, "打开文件选择窗口:" + message, message);
    });
}

测试

运行插件,点击选择按钮,弹出提示框,并且对应文本框被赋值为默认输出地址

监听文件选择按钮.jpg

监听测试按钮

实现

/**
 * 监听测试按钮
 */
private void listenTestButton() {
    this.testButton.addActionListener(e -> {
        // 测试数据库连接
        try {
            if (checkRequireDbParam(project)) {
                return;
            }
            // todo 连接数据库
            Messages.showInfoMessage(project, "数据库连接成功!", "连接成功");
        } catch (Exception exception) {
            Messages.showWarningDialog(project, "数据库连接错误,请检查配置.", "Error");
        }
    });
}
private boolean checkRequireDbParam(Project project) {
    if (this.username.getText() == null || "".equals(this.username.getText())) {
        Messages.showWarningDialog(project, "请填写用户名", "Warring");
        return true;
    }
    if (this.password.getPassword() == null || this.password.getPassword().length == 0) {
        Messages.showWarningDialog(project, "请填写密码", "Warring");
        return true;
    }
    if (this.port.getText() == null || "".equals(this.port.getText())) {
        Messages.showWarningDialog(project, "请填写端口号", "Warring");
        return true;
    }
    if (this.localhost.getText() == null || "".equals(this.localhost.getText())) {
        Messages.showWarningDialog(project, "请填写数据库连接地址", "Warring");
        return true;
    }
    return false;
}

测试

运行插件

(1)当填写了host、port、username、password等必填参数后,点击 [测试连接] 按钮,提示

测试连接成功.jpg

(2)当某个必填项未填写时,提示对应警告

测试连接警告.jpg

监听查询数据库表列表按钮

实现

/**
 * 监听查询数据库表按钮
 */
private void listenQueryButton() {
    try {
        this.queryButton.addActionListener(e -> {
            if (checkRequireDbParam(project)) {
                return;
            }
            if (this.database.getText() == null || "".equals(this.database.getText())) {
                Messages.showWarningDialog(project, "请填写database", "Warring");
                return;
            }

            // 每次查询都先清空缓存
            this.tableNames = new HashSet<>();

            // todo 查询数据库表,后面调用接口查询数据库
            List<String> tableList = Arrays.asList("user", "role", "menu");

            String[] title = {"序号", "表名"};
            Object[][] data = new Object[tableList.size()][2];
            for (int i = 0; i < tableList.size(); i++) {
                data[i][1] = tableList.get(i);
            }

            table1.setModel(new DefaultTableModel(data, title));
            TableColumn tc = table1.getColumnModel().getColumn(0);
            tc.setCellRenderer(table1.getDefaultRenderer(Boolean.class));
            tc.setCellEditor(new DefaultCellEditor(new JCheckBox()));
            tc.setCellEditor(table1.getDefaultEditor(Boolean.class));
            tc.setMaxWidth(200);
        });
    } catch (Exception exception) {
        Messages.showWarningDialog(project, "数据库查询错误,请检查配置.", "Error");
    }
}

测试

运行插件

(1)点击 [查询数据库表名],下方生成列表数据

下方生成列表数据.jpg

(2)必填校验,省略

监听鼠标点击表格

实现

![鼠标点击表格1](C:\work\blog\book\别再羡慕别人了!教你如何轻松掌握IDEA插件开发,成为编程高手!(二)\鼠标点击表格1.jpg)/**
 * 监听鼠标点击表格
 */
private void listenTableMouseClicked() {
    this.table1.addMouseListener(new MouseAdapter() {
        // 选择表
        @Override
        public void mouseClicked(MouseEvent e) {
            if (1 == e.getClickCount()) {
                int rowIdx = table1.rowAtPoint(e.getPoint());
                Boolean flag = (Boolean) table1.getValueAt(rowIdx, 0);
                if (null != flag && flag) {
                    tableNames.add(table1.getValueAt(rowIdx, 1).toString());
                } else {
                    tableNames.remove(table1.getValueAt(rowIdx, 1).toString());
                }
                Messages.showWarningDialog(project, "table:" + table1.getValueAt(rowIdx, 1).toString() + ",rowIdx:" + rowIdx + ",是否被选择:" + flag
                        + ",当前所有被选中的表:" + tableNames, "测试:选择表");
            }
        }
    });
}

测试

运行插件

(1)选中提示

这里打出了tableNames的变化,因为后面需要将这个集合传到后面去生成代码

鼠标点击表格1.jpg

鼠标点击表格2.jpg

(2)必填校验,省略

监听代码生成按钮

实现

/**
 * 监听代码生成按钮
 */
private void listenGenerateButton() {
    this.generateButton.addActionListener(e -> {
        try {
            if (checkRequireGenerateParam(project)) {
                return;
            }
            Messages.showWarningDialog(project, "代码已生成", "Success");
        } catch (Exception ex) {
            Messages.showWarningDialog(project, "代码生成失败:" + ex.getMessage(), "Error");
        }
    });
}

测试

运行插件

(1)未选中表名时,弹出生成代码失败提示框 未选中表名时,生成代码失败.jpg

(2)有选择表名时,弹出生成成功提示框

选中表名时,生成代码成功.jpg

(3)必填校验,省略

添加文件选择组件

实现

/**
 * 文件选择组件
 * 关注公众号【奔跑的码畜】,一起进步不迷路
 *
 * @author 第七人格
 * @date 2023/10/27
 */
public class FileChooserComponent {

    /**
     * 项目
     */
    private final Project project;

    public FileChooserComponent(Project project) {
        this.project = project;
    }

    /**
     * 获取实例
     *
     * @param project 项目
     * @return {@link FileChooserComponent}  文件选择组件
     */
    public static FileChooserComponent getInstance(@NotNull Project project) {
        return new FileChooserComponent(project);
    }

    /**
     * “显示文件夹选择”对话框
     *
     * @param title    标题
     * @param toSelect 默认选中文件夹
     * @param roots    根目录
     * @return {@link VirtualFile} 虚拟文件
     */
    public VirtualFile showFolderSelectionDialog(@NotNull String title, @Nullable VirtualFile toSelect, @Nullable VirtualFile... roots) {
        final FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor();
        descriptor.setTitle(title);
        if (null != roots) {
            descriptor.setRoots(roots);
        }
        return FileChooser.chooseFile(descriptor, project, toSelect);
    }

}

修改listenFileChooserButton方法

实现

/**
 * 监听文件选择按钮
 *
 * @param templateUrlButton
 * @param project
 * @param message
 */
private void listenFileChooserButton(JButton templateUrlButton, Project project, String message, JTextField templateUrl) {
    templateUrlButton.addActionListener(e -> {
        FileChooserComponent component = FileChooserComponent.getInstance(project);
        VirtualFile baseDir = ProjectUtil.guessProjectDir(project);
        VirtualFile virtualFile = component.showFolderSelectionDialog(message, baseDir, baseDir);
        if (null != virtualFile) {
            templateUrl.setText(virtualFile.getPath());
        }
    });
}

测试

(1)点击选择按钮,出现选择框

点击选择按钮,出现选择框.jpg

(2)选择文件夹,返显文件夹路径

选择文件夹,返显文件夹路径.jpg

删除action下多余代码

传送门

传送门

别再羡慕别人了!教你如何轻松掌握IDEA插件开发,成为编程高手!(一)-环境搭建篇

别再羡慕别人了!教你如何轻松掌握IDEA插件开发,成为编程高手!(二)UI设计与开发

别再羡慕别人了!教你如何轻松掌握IDEA插件开发,成为编程高手!(三)-核心代码篇

别再羡慕别人了!教你如何轻松掌握IDEA插件开发,成为编程高手!(四)- 拓展收尾篇