✨这里是第七人格的博客✨小七,欢迎您的到来~✨
🍅系列专栏:实战🍅
✈️本篇内容: UI设计与开发✈️
🍱本篇收录完整代码地址:gitee.com/diqirenge/i…🍱
楔子
前篇 《别再羡慕别人了!教你如何轻松掌握IDEA插件开发,成为编程高手!(一)》 我们讲了开发环境的搭建 ,接下来,为了更好的掌握IDEA插件开发,我们给自己定一个要求,完成一个代码生成器的开发。
UI设计
基本构思
我们来思考一下,一个简单的代码生成器需要些什么东西?
首先,因为我们是根据数据库表来生成的代码,所以肯定需要有录入数据库配置的地方;
其次,我们不可能一次性生成所有的表,所以需要有查询、展示以及选择表的地方;
最后,最好可以选择输入输出的位置,也就是说可以自己选择模版(输入),自己选择代码生成的位置(输出)。
构思图
结合以上思考,我们将我们的插件分为如下4个部分
UI图
构思图添加细节后如下,并且将该插件放在IDEA右边侧比较方便
分支名称
231027-52javaee.com-BuildUI
仓库地址
分支描述
构造UI
代码实现
增加右侧窗口创建方法
/**
* 代码生成工具窗口工厂
* 关注公众号【奔跑的码畜】,一起进步不迷路
*
* @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);
});
}
测试
运行插件,点击选择按钮,弹出提示框,并且对应文本框被赋值为默认输出地址
监听测试按钮
实现
/**
* 监听测试按钮
*/
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等必填参数后,点击 [测试连接] 按钮,提示
(2)当某个必填项未填写时,提示对应警告
监听查询数据库表列表按钮
实现
/**
* 监听查询数据库表按钮
*/
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)点击 [查询数据库表名],下方生成列表数据
(2)必填校验,省略
监听鼠标点击表格
实现
\鼠标点击表格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的变化,因为后面需要将这个集合传到后面去生成代码
(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)未选中表名时,弹出生成代码失败提示框
(2)有选择表名时,弹出生成成功提示框
(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)点击选择按钮,出现选择框
(2)选择文件夹,返显文件夹路径
删除action下多余代码
传送门
传送门
别再羡慕别人了!教你如何轻松掌握IDEA插件开发,成为编程高手!(一)-环境搭建篇
别再羡慕别人了!教你如何轻松掌握IDEA插件开发,成为编程高手!(二)UI设计与开发