javafx
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseJavaFX Desktop Development Standards — Renamer App
JavaFX桌面开发规范 — Renamer应用
Critical Rules
核心规则
- MUST execute all scene graph reads and writes exclusively on the FX Application Thread — marshal from background threads via or
Platform.runLater()callbacks (Task,setOnSucceeded)setOnFailed - MUST set to the fully qualified controller class name —
fx:controllerroutes it throughViewLoaderServiceso Guice handles instantiationinjector::getInstance - MUST use the prefix for every CSS property in JavaFX stylesheets — W3C property names are silently ignored
-fx- - MUST use for all operations exceeding 100 ms — always on daemon threads
javafx.concurrent.Task<V> - MUST install a global during startup
Thread.setDefaultUncaughtExceptionHandler - MUST NOT use Swing, SWT, or AWT for UI — MUST NOT import or
javax.swing.*org.eclipse.swt.*
- 必须仅在FX应用线程上执行所有场景图的读取和写入操作——通过或
Platform.runLater()回调(Task、setOnSucceeded)将后台线程的操作调度到FX线程setOnFailed - 必须将设置为控制器类的全限定名——
fx:controller会通过ViewLoaderService路由,由Guice处理实例化injector::getInstance - JavaFX样式表中的每个CSS属性必须使用前缀——W3C属性名将被静默忽略
-fx- - 所有耗时超过100毫秒的操作必须使用——始终在守护线程上执行
javafx.concurrent.Task<V> - 启动期间必须安装全局
Thread.setDefaultUncaughtExceptionHandler - 不得使用Swing、SWT或AWT开发UI——不得导入或
javax.swing.*org.eclipse.swt.*
Bootstrap & DI Integration
启动与依赖注入集成
This project uses Guice 7 (not Spring). The DI startup chain:
Guice.createInjector(DIAppModule, DICoreModule, DIUIModule)The JavaFX subclass is . Launcher is .
Applicationua.renamer.app.RenamerApplicationua.renamer.app.LauncherFXML loading with Guice — sets , so
Guice resolves the controller named in . The service is used internally by :
ViewLoaderServiceloader.setControllerFactory(injector::getInstance)fx:controllerDIUIModule.loadAndRegister()java
// FXML declares the controller class:
// fx:controller="ua.renamer.app.ui.controller.mode.impl.ModeAddTextController"
//
// ViewLoaderService creates the loader and wires Guice:
// loader.setControllerFactory(injector::getInstance)
// loader.setResources(resourceBundle)
//
// DIUIModule.provideModeViewRegistry() loads each mode view at startup:
// loadAndRegister(registry, viewLoaderApi, ViewNames.MODE_ADD_TEXT, addText);Adding a new UI mode requires:
- Controller class implementing with
ModeControllerV2Api<MyModeParams>@RequiredArgsConstructor(onConstructor_ = {@Inject}) - FXML file with
fx:controller="...ModeMyModeController" - in
bind(ModeMyModeController.class).in(Singleton.class)DIUIModule.bindViewControllers() - Add controller parameter + call in
loadAndRegister()DIUIModule.provideModeViewRegistry() - Entry in enum
ViewNames
本项目使用Guice 7(而非Spring)。依赖注入启动链:
Guice.createInjector(DIAppModule, DICoreModule, DIUIModule)JavaFX的子类为,启动器为。
Applicationua.renamer.app.RenamerApplicationua.renamer.app.Launcher结合Guice加载FXML——会设置,因此Guice会解析中指定的控制器。该服务由内部调用:
ViewLoaderServiceloader.setControllerFactory(injector::getInstance)fx:controllerDIUIModule.loadAndRegister()java
// FXML声明控制器类:
// fx:controller="ua.renamer.app.ui.controller.mode.impl.ModeAddTextController"
//
// ViewLoaderService创建加载器并关联Guice:
// loader.setControllerFactory(injector::getInstance)
// loader.setResources(resourceBundle)
//
// DIUIModule.provideModeViewRegistry()在启动时加载每个模式视图:
// loadAndRegister(registry, viewLoaderApi, ViewNames.MODE_ADD_TEXT, addText);添加新UI模式需执行以下步骤:
- 实现的控制器类,添加
ModeControllerV2Api<MyModeParams>注解@RequiredArgsConstructor(onConstructor_ = {@Inject}) - FXML文件中设置
fx:controller="...ModeMyModeController" - 在中添加
DIUIModule.bindViewControllers()bind(ModeMyModeController.class).in(Singleton.class) - 在中添加控制器参数及
DIUIModule.provideModeViewRegistry()调用loadAndRegister() - 在枚举中添加对应条目
ViewNames
FXML Rules
FXML规则
- MUST place all FXML files under
app/ui/src/main/resources/fxml/ - MUST set to the fully qualified controller class name — Guice resolves it via
fx:controllerloader.setControllerFactory(injector::getInstance) - fields/methods are injected after
@FXMLreturnsloader.load() - Centralize FXML paths in enum — MUST NOT hardcode
ViewNamesstrings elsewhere/fxml/...
- 所有FXML文件必须放置在目录下
app/ui/src/main/resources/fxml/ - 必须将设置为控制器类的全限定名——Guice通过
fx:controller解析loader.setControllerFactory(injector::getInstance) - 字段/方法在
@FXML返回后注入loader.load() - FXML路径必须集中在枚举中——不得在其他地方硬编码
ViewNames字符串/fxml/...
Threading & Concurrency
线程与并发
- FX Application Thread: all scene graph reads/writes, mutations,
ObservableList/TableViewupdatesListView - Background thread: single daemon (injected via
ExecutorService) runsDIAppModuleinstancesjavafx.concurrent.Task<V> - V2 parallel phase: for metadata extraction and physical rename phases
Executors.newVirtualThreadPerTaskExecutor()
java
// Correct: update UI from background thread
Platform.runLater(() -> tableView.getItems().setAll(results));
// Correct: bind progress bar
progressBar.progressProperty().bind(task.progressProperty());
// WRONG: scene graph mutation from non-FX thread
new Thread(() -> tableView.getItems().add(item)).start(); // crashesTask with error handling:
java
Task<List<RenameResult>> task = new Task<>() {
@Override
protected List<RenameResult> call() {
return orchestrator.orchestrate(files, config);
}
};
task.setOnSucceeded(e -> updateTable(task.getValue()));
task.setOnFailed(e -> showError(task.getException()));
executor.execute(task);- FX应用线程:所有场景图的读取/写入、修改、
ObservableList/TableView更新ListView - 后台线程:单个守护(通过
ExecutorService注入)运行DIAppModule实例javafx.concurrent.Task<V> - V2并行阶段:使用处理元数据提取和重命名执行阶段
Executors.newVirtualThreadPerTaskExecutor()
java
// 正确示例:从后台线程更新UI
Platform.runLater(() -> tableView.getItems().setAll(results));
// 正确示例:绑定进度条
progressBar.progressProperty().bind(task.progressProperty());
// 错误示例:在非FX线程修改场景图
new Thread(() -> tableView.getItems().add(item)).start(); // 会崩溃带错误处理的Task示例:
java
Task<List<RenameResult>> task = new Task<>() {
@Override
protected List<RenameResult> call() {
return orchestrator.orchestrate(files, config);
}
};
task.setOnSucceeded(e -> updateTable(task.getValue()));
task.setOnFailed(e -> showError(task.getException()));
executor.execute(task);Data Binding
数据绑定
- Use ,
SimpleStringProperty,SimpleBooleanPropertyfor model fields bound to UISimpleObjectProperty - Use for
FXCollections.observableArrayList()/TableViewdata sourcesListView - Mutate only on the FX Application Thread
ObservableList
- 绑定到UI的模型字段需使用、
SimpleStringProperty、SimpleBooleanPropertySimpleObjectProperty - /
TableView的数据源需使用ListViewFXCollections.observableArrayList() - 仅能在FX应用线程上修改
ObservableList
CSS Styling
CSS样式
- MUST use prefix for all CSS properties
-fx-
| Standard CSS | JavaFX CSS |
|---|---|
| |
| |
| |
| |
| |
| |
| |
| |
- Place CSS files under
app/ui/src/main/resources/styles/ - Apply stylesheets at level, not on individual nodes
Scene - MUST NOT set
-fx-focus-color: transparent
- 所有CSS属性必须使用前缀
-fx-
| 标准CSS | JavaFX CSS |
|---|---|
| |
| |
| |
| |
| |
| |
| |
| |
- CSS文件需放置在目录下
app/ui/src/main/resources/styles/ - 在级别应用样式表,而非单个节点
Scene - 不得设置
-fx-focus-color: transparent
Accessibility
无障碍访问
- Every interactive control must be reachable via Tab / Shift+Tab
- Set on image-only buttons and
accessibleTextelementsImageView - WCAG 2.1 AA contrast: 4.5:1 for normal text, 3:1 for large text
- 所有交互控件必须可通过Tab / Shift+Tab键访问
- 纯图片按钮和元素需设置
ImageViewaccessibleText - 遵循WCAG 2.1 AA对比度标准:普通文本4.5:1,大文本3:1
Crash Handling
崩溃处理
java
// In Application.start():
Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
log.error("Uncaught exception on thread {}", thread.getName(), throwable);
Platform.runLater(() -> showErrorDialog(throwable));
});java
// 在Application.start()中:
Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
log.error("Uncaught exception on thread {}", thread.getName(), throwable);
Platform.runLater(() -> showErrorDialog(throwable));
});Enforcement
规范执行要求
- MUST flag any FXML file missing (all mode views must declare their controller class)
fx:controller - MUST flag any without
new Thread()setDaemon(true) - MUST flag any import from or
javax.swing.*org.eclipse.swt.* - MUST flag any CSS property without prefix
-fx- - MUST flag blocking operations called directly on the FX Application Thread
- 必须标记任何缺少的FXML文件(所有模式视图必须声明其控制器类)
fx:controller - 必须标记任何未设置的
setDaemon(true)new Thread() - 必须标记任何来自或
javax.swing.*的导入org.eclipse.swt.* - 必须标记任何未使用前缀的CSS属性
-fx- - 必须标记任何直接在FX应用线程上调用的阻塞操作