资讯动态

使用 Kotlin UI DSL(v2)构建 IntelliJ Platform 界面:DialogPanel、数据绑定与校验完整指南

发布时间:2026/9/17 20:11:50 来源:尧图企业网站定制
使用 Kotlin UI DSLv2构建 IntelliJ Platform 界面DialogPanel、数据绑定与校验完整指南【免费下载链接】intellij-communityIntelliJ IDEA IntelliJ Platform项目地址: https://gitcode.com/GitHub_Trending/in/intellij-community本指南以 intellij-community 仓库中的 Kotlin UI DSL 技能文档 为核心骨架系统讲解 IntelliJ Platform 新一代 UI 构建方式——Kotlin UI DSLcom.intellij.ui.dsl.builder即 version 2。文中覆盖从panel { }网格模型、Row 布局、组件工厂到数据绑定、表单校验、动态显隐与设置页集成的完整链路并给出可直接复制的代码片段。读完你将能够用 Kotlin UI DSL 编写符合平台规范、自带校验与数据回写的对话框、设置页和工具窗口表单并避开旧版 v1 DSL 的坑。为什么新表单一律使用 Kotlin UI DSL v2Kotlin UI DSL 是 IntelliJ Platform 官方推荐的表单构建方式适用于对话框Dialog、设置页Settings以及形如表单的工具窗口内容。它的核心入口是panel { }一个函数调用即返回一个DialogPanel布局、数据绑定与校验在构建时被统一接线完毕val panel: DialogPanel panel { row(MyBundle.message(label.host)) { // Host: — 设置 labelFor 助记键 可访问性上下文 textField() .align(AlignX.FILL) .bindText(model::host) } row(MyBundle.message(label.port)) { intTextField(range 0..65535) .bindIntText(model::port) } row { checkBox(MyBundle.message(checkbox.use.auth)) .bindSelected(model::useAuth) } }需要特别强调的是v1 版本com.intellij.ui.layout包下的LayoutBuilder、CellBuilder、PropertyBinding、noteRow、titledRow等已于 2026 年 7 月被移除。编写新代码时绝不要依赖 v1也不要从旧代码片段中复制其导入。注意一个容易混淆的细节ComponentPredicate与ValidationInfoBuilder虽然仍位于com.intellij.ui.layout包中但它们是当前 v2 的支撑代码并非 v1 遗留物可以放心使用。在动手之前建议先熟悉两篇配套技能文档Swing 组件架构技能组件状态流、EDT 线程与生命周期相关架构知识UI 可访问性技能可访问性审查相关规范。本技能文档本身由 .ai/render-guides.mjs 从 .agents/skills/kotlin-ui-dsl/SKILL.md 渲染生成。源码位置与现成示例先查再写按照 SKILL.md 的说明Kotlin UI DSL 的代码与示例集中在以下位置路径以技能文档描述为准API 定义community/platform/platform-api/src/com/intellij/ui/dsl/builder/包含Panel.kt、Row.kt、Cell.kt以及按组件划分的扩展文件textField.kt、button.kt、comboBox.kt、spinner.kt等实现代码community/platform/platform-impl/src/com/intellij/ui/dsl/builder/impl/UI DSL Showcase文档级演示community/plugins/devkit/intellij.devkit.uiDsl/src/showcase/Demo*.kt覆盖 Basics、RowLayout、ComponentLabels、Comments、Components、Gaps、Groups、Availability、Validation、Binding、Examples 等主题可通过Tools | Internal Actions | UI | Kotlin UI DSL | UI DSL Showcase运行UI Sandbox压力测试面板community/plugins/devkit/intellij.devkit.uiDsl/src/sandbox/包含约 80 个覆盖边界场景的面板可通过Tools | Internal Actions | UI | UI Sandbox运行。实践原则是在发明新模式之前先检查某个Demo*.kt或 sandbox 面板是否已经演示过该模式优先复用平台已验证的写法。核心模型逐行构建的网格一个 panel 本质上是逐行构建的网格行自上而下排列行内的单元格从左到右排列每次调用工厂函数textField()、checkBox(...)等产生一个单元格每行的最后一个单元格占据剩余宽度行尾的空单元格会被合并进它一个单元格既可以容纳一个组件也可以容纳一个自带独立网格的子面板panel { }。这个模型意味着你不需要像传统GridBagLayout那样手动指定行列坐标布局顺序即代码书写顺序天然易读。Row 布局与标签规范标签行的正确姿势row(Label:) { ... }是带标签的行。可修改组件的标签必须通过row(label)或Cell.label(...)挂接绝不使用裸露的label(...)单元格放在组件旁边。这两种方法会正确处理间距、助记键mnemonics、labelFor以及可访问性上下文。规范细节标签文本以冒号:结尾如Host:需要与带标签的行对齐、但自身无标签的行使用row()Cell.label(text, LabelPosition.TOP)将标签放在组件上方。RowLayout控制行的网格参与方式Row.layout(RowLayout)决定该行如何参与父网格布局行为典型场景INDEPENDENT行拥有自己的独立网格无标签行的默认值LABEL_ALIGNED标签位于父网格其余部分独立row(Label:)的默认值注意无标签时第一个单元格通常是复选框会被当作标签PARENT_GRID每个单元格都参与父网格需要整列对齐的表单当某行有一个特别长的标签、拉伸了其它行时给该行设置.layout(RowLayout.INDEPENDENT)即可解除对齐。纵向空间与注释Row.resizableRow()让该行占据剩余纵向空间多个可伸缩行会共享通常与单元格上的align(Align.FILL)配合使用文本域、树、表格等场景Row.rowComment(...)整行下方的灰色注释会跟随行的可见/启用状态Row.topGap(TopGap.SMALL | MEDIUM)/bottomGap(...)纵向间距。间距要挂在相关联的行上这样该行被隐藏时不会留下多余的间隙。默认无纵向间距group会自带间距。组件工厂Row 工厂函数永远优先使用工厂函数而不是cell(JBTextField())——工厂会应用平台默认值宽度、间距、组归属等。常用工厂清单开关类checkBox(text)、threeStateCheckBox(text)、radioButton(text, value null)按钮类button(text) { }、button(text, anAction)、actionButton(anAction)/actionsButton(...)platform-impl 中extensions.kt的扩展、segmentedButton(items) { text ... }文本输入textField()、passwordField()、expandableTextField()、extendableTextField()、intTextField(range null, keyboardStep null)、textArea()、textFieldWithBrowseButton(fileChooserDescriptor, project)选择类comboBox(items 或 model, renderer null)、spinner(IntRange, step)、spinner(ClosedRangeDouble, step)、slider(min, max, minorTick, majorTick)静态展示label(text)、text(text)支持换行 HTML 文本与链接、comment(text)、icon(icon)、contextHelp(description, title null)即(?)图标、link(text) { }、browserLink(text, url)、dropDownLink(item, items)原始单元格cell(component)自定义组件、scrollCell(component)包装进JBScrollPane、cell()预留空白网格单元、placeholder()内容稍后赋值、panel { }子面板单元格组件微调用.applyToComponent { ... }。文本域宽度用.columns(COLUMNS_TINY | COLUMNS_SHORT | COLUMNS_MEDIUM | COLUMNS_LARGE)控制对应 6 / 18 / 25 / 36 列。结构化布局分组与多列组与区块panel { group(MyBundle.message(group.server)) { ... } // 带标题的块独立网格周围有纵向间距 groupRowsRange(title) { ... } // 带标题的块共享父网格 collapsibleGroup(title) { ... } // 可折叠标题可聚焦支持助记键 rowsRange { ... } // 不可见范围用于批量 visibleIf/enabledIf属父网格 indent { ... } // 标准左缩进 separator() // 纯分割线带标题的分割线用 group/groupRowsRange panel { ... } // 子面板整行宽度独立网格 twoColumnsRow({ checkBox(...) }, { checkBox(...) }) // 以及 threeColumnsRow(...) }单选按钮组buttonsGroup(title null) { ... }是包裹radioButton的必需结构绝不使用原始的javax.swing.ButtonGroup也用于给带标题分组的复选框分组。每个单选按钮绑定一个值buttonsGroup(MyBundle.message(group.color)) { for (value in Color.entries) { row { radioButton(value.displayName, value) } } }.bind(model::color) // radioButton 的值参数必须与属性类型匹配注意radioButton的 value 参数类型必须与绑定属性的类型严格一致。多列区块在同一个row中放置多个panel { }单元格并用.align(AlignY.TOP)对齐各自顶部Row.panel { }可以内联构建嵌套网格。尺寸、对齐与间距对齐Cell.align(AlignX.LEFT | CENTER | RIGHT | FILL)、AlignY.TOP | CENTER | BOTTOM | FILL可组合如align(AlignX.RIGHT AlignY.TOP)、Align.FILL、Align.CENTER。默认值是AlignX.LEFT AlignY.CENTER。伸缩列Cell.resizableColumn()让该列占据额外水平空间多个列共享。关键区分AlignX.FILL是组件在列内拉伸resizableColumn()是列本身变宽——整行宽的字段通常两者都需要。等宽分组Cell.widthGroup(name)让组内元素等宽如按钮不要与AlignX.FILL同时使用。水平间距单元格间默认间距无需写代码。.gap(RightGap.SMALL)用于把紧密相关的相邻元素绑在一起如作为标签的复选框与后面的字段、字段与其单位标签textField().gap(RightGap.SMALL); label(pixels)。RightGap.COLUMNS用于分隔独立的列。禁止手拼布局绝不要用自制的JPanel或Border包裹 DSL 内容来修间距。非常规场景使用Cell.customize(UnscaledGaps(...))/Row.customize(UnscaledGapsY(...))需要整体改变间距配置时使用customizeSpacingConfiguration(...)。数据绑定属性只在 apply 时写入绑定把组件与属性连接起来属性仅在DialogPanel.apply()时被写入而reset()与isModified()自动免费获得组件绑定方式checkBox及任意AbstractButtonbindSelected(model::flag)textField及其它文本组件bindText(model::text)/bindText(getter, setter)intTextFieldbindIntText(model::count)comboBoxbindItem(model::item.toNullableProperty())spinnerbindIntValue(model::value)/bindValue(model::double)sliderbindValue(model::value)textFieldWithBrowseButtonbindText(model::path)buttonsGroup.bind(model::choice)— 单选按钮携带值segmentedButton.bind(property)自定义cell(c).bind(componentGet, componentSet, prop)属性形态KMutableProperty0model::field、getter/setter Lambda、MutableProperty(getter, setter)转换器有toMutableProperty()、toNullableProperty()、toNonNullableProperty(default)。绑定ObservableMutableProperty时会额外立即更新 UI 并自动注册校验请求方。两个要点没有bindItems下拉框的条目必须在构造时传入comboBox(items, renderer)额外逻辑Panel.onApply/onReset/onIsModified { }与Cell.onApply/onReset/onIsModified { }可追加 apply/reset/modified 逻辑。生命周期由宿主代为管理DialogWrapper在点击 OK 时自动 apply 面板BoundConfigurable将apply/reset/isModified委托给面板。只有在自定义宿主中才需要手动调用panel.apply()/reset()/isModified()。校验输入即校验与提交校验Cell上支持两种规则风格// 稳定 APIValidationInfoBuilder 接收者 —— error(...) / warning(...) textField() .validationOnInput { if (it.text.toIntOrNull() null) error(MyBundle.message(error.not.a.number)) else null } .validationOnApply { if (it.text.isBlank()) error(MyBundle.message(error.specify.value)) else null } // 较新 API实验性平台内广泛使用最终将改名为 validation textField().cellValidation { addInputRule(MyBundle.message(error.contains.digits), level Level.WARNING) { it.text.any(Char::isDigit) } addApplyRule(MyBundle.message(error.must.not.be.empty)) { it.text.isNullOrEmpty() } // 条件为 true 存在问题 enabledIf(someCheckBox.selected) }使用原则validationOnInput每次变更都会执行——保持轻量只拒绝不可能合法的输入非法字符、超范围值validationOnApply及addApplyRule在点击 OK 时执行——空值与昂贵检查放在这里绝不在输入时或焦点丢失时标记必填为空即空必填字段的提示只在提交时出现warning(...)/Level.WARNING不阻止 OKerror(...)默认禁用 OK复杂表单中可用.withOKEnabled()保持 OK 可用。接线方式DialogWrapper从createCenterPanel()返回DialogPanel校验器自动注册到对话框的 disposable 上doValidateAll()会包含panel.validateAll()OK 时自动 applyBoundConfigurable/DslConfigurableBase同样全自动独立宿主自行调用panel.registerValidators(parentDisposable)并在 apply 前运行panel.validateAll()。自定义组件同样可以参与校验cell(custom).validationRequestor { ... }或com.intellij.openapi.ui.validation中预置的WHEN_TEXT_CHANGED等告诉面板何时需要重新校验。跨字段校验没有专属 DSL API为每个字段安装ComponentValidator(disposable).withValidator { ... }.installOn(field)并在每个字段的onChanged中统一revalidate()所有相关字段参考 sandbox 中的CrossValidationPanel.kt。动态 UI条件显隐与占位visibleIf(predicate)/enabledIf(predicate)存在于Cell、Row、Panel、RowsRange上接受ComponentPredicate或ObservablePropertyBoolean。内建谓词cellOrButton.selected、comboBox.selectedValueIs(v)/selectedValueMatches { }、textComponent.enteredTextSatisfies { }并用and/or/not组合lateinit var enableAll: CellJBCheckBox row { enableAll checkBox(MyBundle.message(checkbox.enable)) } indent { row { checkBox(MyBundle.message(checkbox.option1)) } }.enabledIf(enableAll.selected)placeholder()预留一个单元格稍后对其component赋值或清空放在其中的嵌套panel { }会保留自己的绑定与校验变更监听Cell.onChanged { component - }/onChangedContext { component, context - }支持按钮、文本组件、下拉框、滑块——JSpinner不支持调用会抛出UiDslException实验性类型化助手需要传入 disposablewhenTextChangedFromUi、whenStateChangedFromUi、whenItemSelectedFromUi。列表与下拉框渲染器绝不要手写ListCellRenderer用于下拉框和JBList——使用com.intellij.ui.dsl.listCellRenderer包中的 DSL它统一处理新旧 UI 的选中形状、禁用颜色、缩放、可访问性与快速搜索comboBox(items, textListCellRenderer { it?.displayName }) // 简单文本 comboBox(items, listCellRenderer { // 富文本行 icon(value.icon) text(value.name) text(value.detail) { foreground greyForeground } separator { text Group } // 区块分隔线 })设置页集成继承BoundConfigurable(displayName)或BoundSearchableConfigurable(displayName, helpTopic)在createPanel(): DialogPanel中构建 UI需要 disposable 的 UI 使用继承来的disposableapply/reset/isModified/校验全部由基类处理嵌入到复合 configurable 中的片段实现UiDslUnnamedConfigurable.Simple并重写Panel.createContent()将内容放在group { }或panel { }中避免干扰父网格。硬性规则清单所有用户可见字符串必须来自消息包MyBundle.message(key)工厂参数上的NlsContexts注解会强制这一点句子首字母大写复选框/单选按钮标签结尾不加句号普通标签以:结尾不使用javax.swing.ButtonGroup一律用buttonsGroup { }DSL 表单内不使用手写JPanel/GridBagLayout包裹改用嵌套panel { }/rowsRange/customize(UnscaledGaps)自带边框的自定义组件会扰乱布局——设置putClientProperty(DslComponentProperty.VISUAL_PADDINGS, UnscaledGaps.EMPTY)组合式组件还需设置DslComponentProperty.INTERACTIVE_COMPONENT让labelFor、校验和onChanged指向正确的子组件DialogPanel的构建属于 EDT 工作与其他 Swing 一致不要在panel { }块内运行服务或 I/O。上手路线小结新表单开发的推荐流程是先运行Tools | Internal Actions | UI | Kotlin UI DSL | UI DSL Showcase与UI | UI Sandbox浏览官方演示与边界用例确定模式后按核心模型 → Row 布局 → 组件工厂 → 分组结构 → 尺寸对齐 → 数据绑定 → 校验 → 动态 UI → 渲染器 → 设置页集成的顺序搭建最后对照硬性规则清单消息包、buttonsGroup、禁止手写布局、DslComponentProperty、EDT逐项自检。遵循以上规范即可写出与 IntelliJ IDEA 原生设置界面风格一致、行为可靠、可无障碍访问的 Kotlin UI DSL 表单。【免费下载链接】intellij-communityIntelliJ IDEA IntelliJ Platform项目地址: https://gitcode.com/GitHub_Trending/in/intellij-community创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价