IntelliJ IDE 插件开发 | (二)UI 界面与数据持久化
系列文章
- IntelliJ IDE 插件开发 |(一)快速入门
前言
在上一篇文章中介绍了在IDEA
下开发、运行和安装插件的基本步骤,因此创建项目等基础步骤不再赘述,本文则开始介绍如何进行 UI 界面的开发以及相关数据的持久化存储,本文涉及到的的完整代码已上传到Github。
UI 界面开发
在开发插件的过程中,我们或多或少都需要进行 UI 界面的开发,但是IntelliJ IDE
插件需要使用Swing
进行 UI 的开发,相信大部分人都不太了解,因此本文及后续文章都选择使用UI Designer
这款插件(IDEA 默认安装,可自行检查一下)通过可视化工具拖拽的方式来实现基本的界面设计。
UI Designer 使用步骤
在上一篇文章中提到过,后续 UI 相关的开发使用 Java,其他则使用 Kolin,由于默认创建的工程目录如下,没有 Java 模块:
所有我们首先需要创建 Java 模块:
然后按照引导创建 UI 文件:
创建完成后,会出现类似下图的界面:
可以看到,默认会创建一个 Java 类和一个 form 布局文件,其中 Java 文件主要用于后续控制字段的初始化及获取等操作,form 文件则用于界面布局,点击 form 文件会出现上图所示的三个栏目:组件属性栏、UI 效果栏和组件栏,其中先选中右侧组件然后就可以拖拽到中间的 UI 效果栏展示。
下面我们用一个简单的登录表单来讲解使用方式,首先以一个 GIF 演示拖拽功能开始:
修改 Label 的内容可以通过左侧工具栏或者直接在 UI 效果栏双击标签进行:
输入框对应的字段名则通过左侧组件属性栏进行修改:
这时候我们查看 UIDemo.java 文件会发现以下内容(注释随自己的配置变化):
import javax.swing.*;/*** UI 界面** @author butterfly* @date 2023-12-06*/
public class UIDemo {private JTextField username;private JTextField password;
}
然后通过预览功能,我们也可以先查看表单效果:
简单的使用步骤就到这里,这里就不挨个讲解组件的效果和使用方式了,后续会在使用的过程和实战应用中再进行讲解,大家也可以先自行探索。
使用平台自带组件
在上一小节中我们讲解了如何通过UI Designer
插件来创建我们的 UI 界面,可以发现默认提供的 Swing UI 组件并没有办法满足我们的日常使用,比如文件下拉树选择组件就不存在。而我们对 Swing 的开发又不熟悉,那该怎么办呢?还好,我们还可以使用 IntelliJ 平台自带的组件,下面就来讲解使用方法:
首先在空白处右键创建一个分组:
然后右键分组选择第一项添加组件:
选择类名的方式,然后点击...
:
这里输入TextFieldWithBrowseButton
即可找到带有文件下拉树选择输入框:
然后经过两次确认就可以发现组件已经添加到了右侧组件栏中:
由于 UI Designer 的预览功能不支持原生组件的预览,需要运行插件才可以,这里先知道是类似下图选择文件夹的效果即可:
平台自带的组件大多都可以在com.intellij.openapi.ui
包下找到,根据组件名TextFieldWithBrowseButton
我们也可以发现相关组件的命名也很规范,因此当需要某个组件时就可以先在该包下或者通过关键字进行搜索,除此之外,我们还可以参考开源插件来找到和学习原生组件的使用方式。
以 Git 插件为例,布局如下:
可以在官方仓库找到源码(这里选择的是 192 版本这个分支,之后的版本布局开始使用 Kotlin 进行了重构):
根据插件界面可以发现第一行就是使用带有文件树选择的输入框,我们在代码中也可以找到对应实现:
由于 192 版本之后使用 Kotlin 进行 UI 的编写,因此初学建议可以下载一个 192 的 IDEA 社区版进行界面的参考,这样通过参考布局及相应源码也是快速学习 UI 界面开发的一种方法。
至于如何快速找到插件的某个组件实现,这里建议在下载源码后,就可以根据界面上的提示文字进行代码的全局搜索即可。
在配置界面和侧边栏中展示 UI 界面
通过上述两步,我们可以了解到如何实现开发一个简单的 UI 界面,下面就开始讲解如何将设计好的界面展示在配置页面或者侧边栏中。
准备工作
在正式开始之前我们需要对上文中创建的界面进行一些修改,布局外层的 Panel 需要先设置一下字段名:
然后增加相应的 Get 方法:
import javax.swing.*;/*** UI 界面** @author butterfly* @date 2023-12-06*/
public class UIDemo {private JTextField username;private JTextField password;private JPanel mainPanel;public JPanel getMainPanel() {return mainPanel;}}
准备工作到此结束。
配置界面 UI
接下来就是将 UI 展示在配置界面,我们先创建一个UISettingsConfig.kt
文件:
import cn.butterfly.ui.UIDemo
import com.intellij.openapi.options.Configurable
import javax.swing.JComponent/*** UI 配置界面配置类** @author butterfly* @date 2023-12-06*/
class UISettingsConfig: Configurable {private val form = UIDemo()private val component: JComponentinit {component = form.mainPanel}override fun createComponent() = componentoverride fun isModified() = trueoverride fun apply() {}override fun getDisplayName() = "UISettingsConfig"}
然后在plugin.xml
文件中进行如下的配置,这里的applicationConfigurable
代表使用应用级别的配置,相应地,还有projectConfigurable
代表项目级别的配置,具体区别在下文的数据持久化中进行介绍:
<extensions defaultExtensionNs="com.intellij"><applicationConfigurableinstance="cn.butterfly.ui.config.UISettingsConfig"id="cn.butterfly.ui.config.UISettingsConfig"displayName="UISettingsConfig"/>
</extensions>
这里我们运行插件,打开配置界面就可以发现我们的界面效果了:
如果想要修改其显示位置,比如显示在Tools
菜单下:
只需要在配置中增加parentId="tools"
即可:
<applicationConfigurableinstance="cn.butterfly.ui.config.UISettingsConfig"id="cn.butterfly.ui.config.UISettingsConfig"parentId="tools"displayName="UISettingsConfig"/>
applicationConfigurable
中完整的配置项可查看官网文档。
侧边栏界面 UI
最后再来说明如何在侧边栏中显示界面,首先创建一个UISidebarConfig.kt
:
import cn.butterfly.ui.UIDemo
import com.intellij.openapi.project.Project
import com.intellij.openapi.wm.ToolWindow
import com.intellij.openapi.wm.ToolWindowFactory
import com.intellij.ui.content.ContentFactory
import javax.swing.JComponent/*** UI 侧边栏界面配置类** @author butterfly* @date 2023-12-06*/
class UISidebarConfig: ToolWindowFactory {private val form = UIDemo()private val component: JComponentinit {component = form.mainPanel}override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) {toolWindow.contentManager.addContent(ContentFactory.getInstance().createContent(component, "", false))}}
然后我们可以准备一个 svg 图标文件用于后续在侧边栏展示:
同时需要创建对应的文件加载接口:
import com.intellij.openapi.util.IconLoader;
import javax.swing.*;/*** 插件图标** @author butterfly* @date 2023-12-06*/
public interface PluginIcons {Icon BUTTERFLY = IconLoader.getIcon("/icons/butterfly.svg", PluginIcons.class);}
然后在plugin.xml
配置文件中进行如下设置即可,anchor
可设置默认位置,icon
用于设置上文的图标:
<extensions defaultExtensionNs="com.intellij"><toolWindow id="UISettingsConfig" anchor="right"factoryClass="cn.butterfly.ui.config.UISidebarConfig"icon="cn.butterfly.ui.icons.PluginIcons.BUTTERFLY"/>
</extensions>
运行插件,即可在右侧的侧边栏看到我们设置的界面:
数据持久化
讲完了界面开发的相关内容,下面就开始介绍如何进行配置数据的持久化,首先是创建一个存储数据信息的UIDemoState.kt
文件:
import com.intellij.openapi.components.PersistentStateComponent
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.State
import com.intellij.openapi.components.Storage
import com.intellij.util.xmlb.XmlSerializerUtil/*** 数据持久化存储** @author butterfly* @date 2023-12-06*/
@Service
@State(name = "UIDemoState", storages = [Storage("ui-demo-state.xml")])
class UIDemoState: PersistentStateComponent<UIDemoState> {var username = ""var password = ""override fun getState(): UIDemoState {return this}override fun loadState(state: UIDemoState) {XmlSerializerUtil.copyBean(state, this)}}
其中username
和password
对应表单中我们设置的两个字段,这里设置了默认值为空字符串,ui-demo-state.xml
用于设置数据持久化存储的文件名。
然后我们需要对前文中创建的布局文件进行些许的修改,增加字段的 Get 方法:
import javax.swing.*;/*** UI 界面** @author butterfly* @date 2023-12-06*/
public class UIDemo {/*** 用户名*/private JTextField username;/*** 密码*/private JTextField password;private JPanel mainPanel;public JTextField getUsername() {return username;}public JTextField getPassword() {return password;}public JPanel getMainPanel() {return mainPanel;}}
最后我们就需要对前文中创建的UISettingsConfig.kt
文件进行修改,用于处理界面上保存数据的操作:
import cn.butterfly.ui.UIDemo
import cn.butterfly.ui.state.UIDemoState
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.options.Configurable
import javax.swing.JComponent/*** UI 配置界面配置类** @author butterfly* @date 2023-12-06*/
class UISettingsConfig: Configurable {private val form = UIDemo()private val component: JComponentprivate val state = ApplicationManager.getApplication().getService(UIDemoState::class.java)init {component = form.mainPanelreset()}override fun createComponent() = componentoverride fun isModified(): Boolean {return state.username != form.username.text || state.password != form.password.text}override fun apply() {state.username = form.username.textstate.password = form.password.text}override fun reset() {form.username.text = state.usernameform.password.text = state.password}override fun getDisplayName() = "UISettingsConfig"}
其中reset
用于重置表单内容为修改前设置的值,apply
用于保存当前修改,isModified
用于判断当前数据和上次数据之间是否存在不同,用于Apply
按钮的禁用/激活状态的切换,这三个操作对应界面上的效果如下:
那我们的配置文件存储位置是在哪里呢?
由于在前文中我们使用applicationConfigurable
选择了应用级别的存储,因此文件就存储在 IDEA 默认的配置文件存储地址:C:\Users\用户名\AppData\Roaming\JetBrains\IntelliJIdea2023.2\options
,其中IntelliJIdea2023.2
对应自己使用的 IDEA 名称。不过,由于我们当前是通过沙盒环境运行,所以位置并不是全局设置的位置,而是在build/idea-sandbox/config/options
下:
当我们将插件安装到我们正式的 IDEA 中,也就可以在上述的位置下发现配置文件了:
如果我们将配置设为projectConfigurable
选择项目级别,那么首先需要对配置文件进行如下修改:
<extensions defaultExtensionNs="com.intellij"><projectConfigurableinstance="cn.butterfly.ui.config.UISettingsConfig"id="cn.butterfly.ui.config.UISettingsConfig"parentId="tools"displayName="UISettingsConfig"/>
</extensions>
同时修改UIDemoState.kt
文件上的注解配置:
import com.intellij.openapi.components.PersistentStateComponent
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.State
import com.intellij.openapi.components.Storage
import com.intellij.util.xmlb.XmlSerializerUtil/*** 数据持久化存储** @author butterfly* @date 2023-12-06*/
@Service(Service.Level.PROJECT) // 只修改该行
@State(name = "UIDemoState", storages = [Storage("ui-demo-state.xml")])
class UIDemoState: PersistentStateComponent<UIDemoState> {var username = ""var password = ""override fun getState(): UIDemoState {return this}override fun loadState(state: UIDemoState) {XmlSerializerUtil.copyBean(state, this)}}
最后还需要修改UISettingsConfig.kt
文件,增加一个 project 的构造参数并修改 state 的初始化:
import cn.butterfly.ui.UIDemo
import cn.butterfly.ui.state.UIDemoState
import com.intellij.openapi.options.Configurable
import com.intellij.openapi.project.Project
import javax.swing.JComponent/*** UI 配置界面配置类** @author butterfly* @date 2023-12-06*/
// 增加 project: Project 构造参数
class UISettingsConfig(project: Project): Configurable {private val form = UIDemo()private val component: JComponent// 修改该行private val state = project.getService(UIDemoState::class.java)init {component = form.mainPanelreset()}override fun createComponent() = componentoverride fun isModified(): Boolean {return state.username != form.username.text || state.password != form.password.text}override fun apply() {state.username = form.username.textstate.password = form.password.text}override fun reset() {form.username.text = state.usernameform.password.text = state.password}override fun getDisplayName() = "UISettingsConfig"}
然后我们就可以在项目下的.idea
文件夹中找到我们针对项目级别的配置了:
至于读取配置文件,则通过private val state = project.getService(UIDemoState::class.java)
或者private val state = ApplicationManager.getApplication().getService(UIDemoState::class.java)
分别换取项目或应用级别的 state 对象,然后读取其中的字段即可。
总结
本文讲解了关于 UI 界面开发和数据持久化相关的内容,如果有错误或不足之处,欢迎一起交流讨论。
相关文章:

IntelliJ IDE 插件开发 | (二)UI 界面与数据持久化
系列文章 IntelliJ IDE 插件开发 |(一)快速入门 前言 在上一篇文章中介绍了在IDEA下开发、运行和安装插件的基本步骤,因此创建项目等基础步骤不再赘述,本文则开始介绍如何进行 UI 界面的开发以及相关数据的持久化存储ÿ…...

使用vue UI安装路由插件
1.使用vue创建项目 vue create vue-appvue ui 2.使用vue ui界面创建管理项目 终端页面输入:vue ui 创建项目 安装完成。可以直接在ui界面运行,也可以在编辑器中使用命令运行 安装路由,安装状态 选择插件 - 添加vue-router、添加vuex 安装…...

RPG项目01_脚本代码
基于“RPG项目01_场景及人物动画管理器”,我们创建一个XML文档 在资源文件夹下创建一个文件夹, 命名为Xml 将Xnl文档拖拽至文件夹中, 再在文件夹的Manager下新建脚本LoadManager 写代码: using System.Collections; using System…...
目标检测YOLO实战应用案例100讲-交通目标数据集构建及高性能检测算法研究与应用
目录 前言 国内外研究现状 目标检测研究现状 目标检测数据集研究现状...
浅谈Vue.js的计算属性computed
什么是computed属性 computed 属性用于声明计算属性,这些属性的值是基于其他响应式属性计算而来的,当依赖的响应式属性发生变化时,计算属性会自动重新计算。 与Vue.js 2相比,Vue.js 3的 computed 属性语法稍有变化,不…...

Linux常用指令详解
目录 前言: Linux的目录结构 Linux常用指令简介 whoami指令 ls指令 pwd指令 cd指令 tree指令 touch指令 mkdir指令 rmdir指令与rm指令 man指令 cp(copy)指令 mv(move)指令 cat指令 重定向及重定向的类型…...
Nginx(性能优化)
到这里文章的篇幅较长了,最后再来聊一下关于Nginx的性能优化,主要就简单说说收益最高的几个优化项,在这块就不再展开叙述了,毕竟影响性能都有多方面原因导致的,比如网络、服务器硬件、操作系统、后端服务、程序自身、数…...
机器学习笔记 - 如何在Python中对网格和点云进行体素化?
一、简述 本文主要是为了了解如何生成体素表示,体素之于3D就像像素之于2D。体素本质上是 3D 像素,但它们不是正方形,而是完美的立方体。 理论上,体素是复制现实的完美建模技术。 这里我们要了解四个广泛流行的 Python 库(Open3D、Trimesh、PyVista、pyntcloud )生成点云…...

冒个泡!OceanBase亮相 2023 新加坡金融科技节
近日,OceanBase 亮相 Singapore Fintech Festival 2023(2023 新加坡金融科技节)!本届新加坡金融科技节于 2023 年 11 月 15 日至 17 日在新加坡博览展览中心举行,展会期间,OceanBase 得到了众多金融科技机构…...

正则表达式(5):常用符号
正则表达式(5):常用符号 小结 本博文转载自 在本博客中,”正则表达式”为一系列文章,如果你想要从头学习怎样在Linux中使用正则,可以参考此系列文章,直达链接如下: 在Linux中使用正…...

Web安全漏洞分析-XSS(下)
随着互联网的迅猛发展,Web应用的普及程度也愈发广泛。然而,随之而来的是各种安全威胁的不断涌现,其中最为常见而危险的之一就是跨站脚本攻击(Cross-Site Scripting,简称XSS)。XSS攻击一直以来都是Web安全领…...

金南瓜SECS/GEM C# SDK 快速使用指南
本文对如何使用金南瓜SECS/GEM C# SDK 快速创建一个满足SECS/GEM通信要求的应用程序,只需简单3步完成。 第一步:创建C# .NET程序 示例使用Visual Studio 2010,使用者可以选择更高级版本 Visual Studio 第二步:添加DLL库引用&am…...
在一个没有超级用户的mongodb 生产库上如何添加超级用户
说来这个问题,都觉得不可思议,一个数据库怎么没有超级用户呢,我们知道,MYSQL,PG,ORACLE等,创建好后,都有一个默认的超级用户,MONGODB也有超级用户,但需要自己…...
排序算法之二:冒泡排序
冒泡排序的思路 冒泡排序是交换排序 基本思想:所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动…...

一键搭建你的hnust请假条
hnust请假条 湖南科技大学请假条生成器 https://hnust.rick.icu/new (直接使用) Hnust Leave Note 去github https://github.com/rickhqh/hnust_leave_note 效果展示 界面展示效果图 v2.0 更新 vant和vue重构了整个源码同步学校新版请假条样式修复了…...

C练习题13
单项选择题(本大题共20小题,每小题2分,共40分。在每小题给出的四个备选项中,选出一个正确的答案,并将所选项前的字母填写在答题纸的相应位置上。) 1.结构化程序由三种基本结构组成、三种基本结构组成的算法是() A.可以完成任何复杂的任务 B. 只能完成部分复杂的任务 C. 只能完…...

交易历史记录20231206 记录
昨日回顾: select top 10000 * from dbo.CODEINFO A left join dbo.全部A股20231206010101 B ON A.CODE B.代码 left join dbo.全部A股20231206CONF D on A.CODED.代码left join dbo.全部A股20231206 G on A.CODEG.代码 left…...

1-5总体分布的推断
...

深信服技术认证“SCSA-S”划重点:XSS漏洞
为帮助大家更加系统化地学习网络安全知识,以及更高效地通过深信服安全服务认证工程师考核,深信服特别推出“SCSA-S认证备考秘笈”共十期内容,“考试重点”内容框架,帮助大家快速get重点知识~ 划重点来啦 *点击图片放大展示 深信服…...

MIT6S081-Lab2总结
大家好,我叫徐锦桐,个人博客地址为www.xujintong.com,github地址为https://github.com/xjintong。平时记录一下学习计算机过程中获取的知识,还有日常折腾的经验,欢迎大家访问。 Lab2就是了解一下xv6的系统调用流程&…...

stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...

DAY 47
三、通道注意力 3.1 通道注意力的定义 # 新增:通道注意力模块(SE模块) class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...

剑指offer20_链表中环的入口节点
链表中环的入口节点 给定一个链表,若其中包含环,则输出环的入口节点。 若其中不包含环,则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...
解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错
出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上,所以报错,到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本,cu、torch、cp 的版本一定要对…...

蓝桥杯3498 01串的熵
问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798, 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
以下是一个完整的 Angular 微前端示例,其中使用的是 Module Federation 和 npx-build-plus 实现了主应用(Shell)与子应用(Remote)的集成。 🛠️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...

云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...

FFmpeg:Windows系统小白安装及其使用
一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】,注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录(即exe所在文件夹)加入系统变量…...
LangFlow技术架构分析
🔧 LangFlow 的可视化技术栈 前端节点编辑器 底层框架:基于 (一个现代化的 React 节点绘图库) 功能: 拖拽式构建 LangGraph 状态机 实时连线定义节点依赖关系 可视化调试循环和分支逻辑 与 LangGraph 的深…...