javafx

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

JavaFX 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
    Platform.runLater()
    or
    Task
    callbacks (
    setOnSucceeded
    ,
    setOnFailed
    )
  • MUST set
    fx:controller
    to the fully qualified controller class name —
    ViewLoaderService
    routes it through
    injector::getInstance
    so Guice handles instantiation
  • MUST use the
    -fx-
    prefix for every CSS property in JavaFX stylesheets — W3C property names are silently ignored
  • MUST use
    javafx.concurrent.Task<V>
    for all operations exceeding 100 ms — always on daemon threads
  • MUST install a global
    Thread.setDefaultUncaughtExceptionHandler
    during startup
  • MUST NOT use Swing, SWT, or AWT for UI — MUST NOT import
    javax.swing.*
    or
    org.eclipse.swt.*

  • 必须仅在FX应用线程上执行所有场景图的读取和写入操作——通过
    Platform.runLater()
    Task
    回调(
    setOnSucceeded
    setOnFailed
    )将后台线程的操作调度到FX线程
  • 必须将
    fx:controller
    设置为控制器类的全限定名——
    ViewLoaderService
    会通过
    injector::getInstance
    路由,由Guice处理实例化
  • JavaFX样式表中的每个CSS属性必须使用
    -fx-
    前缀——W3C属性名将被静默忽略
  • 所有耗时超过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
Application
subclass is
ua.renamer.app.RenamerApplication
. Launcher is
ua.renamer.app.Launcher
.
FXML loading with Guice
ViewLoaderService
sets
loader.setControllerFactory(injector::getInstance)
, so Guice resolves the controller named in
fx:controller
. The service is used internally by
DIUIModule.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:
  1. Controller class implementing
    ModeControllerV2Api<MyModeParams>
    with
    @RequiredArgsConstructor(onConstructor_ = {@Inject})
  2. FXML file with
    fx:controller="...ModeMyModeController"
  3. bind(ModeMyModeController.class).in(Singleton.class)
    in
    DIUIModule.bindViewControllers()
  4. Add controller parameter +
    loadAndRegister()
    call in
    DIUIModule.provideModeViewRegistry()
  5. Entry in
    ViewNames
    enum

本项目使用Guice 7(而非Spring)。依赖注入启动链:
Guice.createInjector(DIAppModule, DICoreModule, DIUIModule)
JavaFX的
Application
子类为
ua.renamer.app.RenamerApplication
,启动器为
ua.renamer.app.Launcher
结合Guice加载FXML——
ViewLoaderService
会设置
loader.setControllerFactory(injector::getInstance)
,因此Guice会解析
fx:controller
中指定的控制器。该服务由
DIUIModule.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模式需执行以下步骤:
  1. 实现
    ModeControllerV2Api<MyModeParams>
    的控制器类,添加
    @RequiredArgsConstructor(onConstructor_ = {@Inject})
    注解
  2. FXML文件中设置
    fx:controller="...ModeMyModeController"
  3. DIUIModule.bindViewControllers()
    中添加
    bind(ModeMyModeController.class).in(Singleton.class)
  4. DIUIModule.provideModeViewRegistry()
    中添加控制器参数及
    loadAndRegister()
    调用
  5. ViewNames
    枚举中添加对应条目

FXML Rules

FXML规则

  • MUST place all FXML files under
    app/ui/src/main/resources/fxml/
  • MUST set
    fx:controller
    to the fully qualified controller class name — Guice resolves it via
    loader.setControllerFactory(injector::getInstance)
  • @FXML
    fields/methods are injected after
    loader.load()
    returns
  • Centralize FXML paths in
    ViewNames
    enum — MUST NOT hardcode
    /fxml/...
    strings elsewhere

  • 所有FXML文件必须放置在
    app/ui/src/main/resources/fxml/
    目录下
  • 必须将
    fx:controller
    设置为控制器类的全限定名——Guice通过
    loader.setControllerFactory(injector::getInstance)
    解析
  • @FXML
    字段/方法在
    loader.load()
    返回后注入
  • FXML路径必须集中在
    ViewNames
    枚举中——不得在其他地方硬编码
    /fxml/...
    字符串

Threading & Concurrency

线程与并发

  • FX Application Thread: all scene graph reads/writes,
    ObservableList
    mutations,
    TableView
    /
    ListView
    updates
  • Background thread: single daemon
    ExecutorService
    (injected via
    DIAppModule
    ) runs
    javafx.concurrent.Task<V>
    instances
  • V2 parallel phase:
    Executors.newVirtualThreadPerTaskExecutor()
    for metadata extraction and physical rename phases
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(); // crashes
Task 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
    ,
    SimpleBooleanProperty
    ,
    SimpleObjectProperty
    for model fields bound to UI
  • Use
    FXCollections.observableArrayList()
    for
    TableView
    /
    ListView
    data sources
  • Mutate
    ObservableList
    only on the FX Application Thread

  • 绑定到UI的模型字段需使用
    SimpleStringProperty
    SimpleBooleanProperty
    SimpleObjectProperty
  • TableView
    /
    ListView
    的数据源需使用
    FXCollections.observableArrayList()
  • 仅能在FX应用线程上修改
    ObservableList

CSS Styling

CSS样式

  • MUST use
    -fx-
    prefix for all CSS properties
Standard CSSJavaFX CSS
background-color
-fx-background-color
font-family
-fx-font-family
font-size
-fx-font-size
padding
-fx-padding
color
(text)
-fx-text-fill
border-radius
-fx-background-radius
/
-fx-border-radius
cursor
-fx-cursor
opacity
-fx-opacity
  • Place CSS files under
    app/ui/src/main/resources/styles/
  • Apply stylesheets at
    Scene
    level, not on individual nodes
  • MUST NOT set
    -fx-focus-color: transparent

  • 所有CSS属性必须使用
    -fx-
    前缀
标准CSSJavaFX CSS
background-color
-fx-background-color
font-family
-fx-font-family
font-size
-fx-font-size
padding
-fx-padding
color
(文本)
-fx-text-fill
border-radius
-fx-background-radius
/
-fx-border-radius
cursor
-fx-cursor
opacity
-fx-opacity
  • CSS文件需放置在
    app/ui/src/main/resources/styles/
    目录下
  • Scene
    级别应用样式表,而非单个节点
  • 不得设置
    -fx-focus-color: transparent

Accessibility

无障碍访问

  • Every interactive control must be reachable via Tab / Shift+Tab
  • Set
    accessibleText
    on image-only buttons and
    ImageView
    elements
  • WCAG 2.1 AA contrast: 4.5:1 for normal text, 3:1 for large text

  • 所有交互控件必须可通过Tab / Shift+Tab键访问
  • 纯图片按钮和
    ImageView
    元素需设置
    accessibleText
  • 遵循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
    fx:controller
    (all mode views must declare their controller class)
  • MUST flag any
    new Thread()
    without
    setDaemon(true)
  • MUST flag any import from
    javax.swing.*
    or
    org.eclipse.swt.*
  • MUST flag any CSS property without
    -fx-
    prefix
  • MUST flag blocking operations called directly on the FX Application Thread
  • 必须标记任何缺少
    fx:controller
    的FXML文件(所有模式视图必须声明其控制器类)
  • 必须标记任何未设置
    setDaemon(true)
    new Thread()
  • 必须标记任何来自
    javax.swing.*
    org.eclipse.swt.*
    的导入
  • 必须标记任何未使用
    -fx-
    前缀的CSS属性
  • 必须标记任何直接在FX应用线程上调用的阻塞操作