You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将JavaFX滑动开关代码转换为Scene Builder 2.0控件

将纯Java实现的JavaFX滑动开关转为Scene Builder可识别控件

我正在尝试把自己用纯Java代码实现的JavaFX滑动开关转换成Scene Builder 2.0能识别的控件,方便在Scene Builder里使用。

我参考了两个YouTube视频:

  • 制作滑动开关
  • 创建Scene Builder控件

但这两个视频都是先在Scene Builder里设计,和我用纯Java实现的路径不一样,想问问有没有办法把已有的Java代码转成Scene Builder可识别的控件?

我的滑动开关完整代码

package application;
    
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.animation.FillTransition;
import javafx.animation.ParallelTransition;
import javafx.animation.TranslateTransition;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.scene.Parent;
import javafx.scene.effect.DropShadow;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.FontSmoothingType;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment;
import javafx.util.Duration;

public class Main extends Application {
    static Text text;
    static Pane root = new Pane();
    
    private Parent createContent() {
        root.setPrefSize(300, 300);
        TranslateTransition translateAnimation2 = new TranslateTransition(Duration.seconds(0.25));
        ParallelTransition animation2 = new ParallelTransition(translateAnimation2);
        Rectangle bg = new Rectangle(300, 300);
        ToggleSwitch toggle = new ToggleSwitch();

        toggle.setTranslateX(100);
        toggle.setTranslateY(100);
        text.setTranslateX(toggle.getTranslateX());
        text.setTranslateY(toggle.getTranslateY() + 9);
        translateAnimation2.setNode(text);
        root.getChildren().addAll(toggle, text);
        
        return root;
    }

    private static class ToggleSwitch extends Parent {
        private BooleanProperty switchedOn = new SimpleBooleanProperty(false);
        private TranslateTransition translateAnimation = new TranslateTransition(Duration.seconds(0.25));
        private TranslateTransition translateAnimation2 = new TranslateTransition(Duration.seconds(0.25));
        private FillTransition fillAnimation = new FillTransition(Duration.seconds(0.25));
        private ParallelTransition animation = new ParallelTransition(translateAnimation, fillAnimation);
        private ParallelTransition animation2 = new ParallelTransition(translateAnimation2);
        
        public BooleanProperty switchedOnProperty() {
            return switchedOn;
        }

        public boolean checkSwitch() {
            switchedOn.set(!switchedOn.get());
            return switchedOn.get();
        }

        public ToggleSwitch() {
            Rectangle background = new Rectangle(30, 10);
            background.setArcWidth(10);
            background.setArcHeight(10);
            background.setFill(Color.rgb(255, 161, 161));
            background.setStroke(Color.SLATEGREY);

            Circle trigger = new Circle(10);
            trigger.setCenterX(8);
            trigger.setCenterY(6);
            trigger.setFill(Color.rgb(245, 245, 245));
            trigger.setStroke(Color.SLATEGREY);

            DropShadow shadow = new DropShadow();
            shadow.setRadius(2);
            trigger.setEffect(shadow);
            
            text = new Text();
            text.setFont(Font.font("Verdana", FontWeight.MEDIUM, 8));
            text.setFill(Color.rgb(0, 0, 0));
            text.setTextAlignment(TextAlignment.CENTER);
            text.setFontSmoothingType(FontSmoothingType.LCD);
            text.textProperty().bind(Bindings.when(switchedOnProperty()).then("ON").otherwise("OFF"));
            text.setOnMouseClicked(e -> {
                switchedOn.set(!switchedOn.get());
            });

            translateAnimation.setNode(trigger);
            translateAnimation2.setNode(text);
            
            fillAnimation.setShape(background);

            getChildren().addAll(background, trigger);
            
            switchedOn.addListener((obs, oldState, newState) -> {
                boolean isOn = newState.booleanValue();
                
                translateAnimation.setToX(isOn ? 45 - 30 : 0);
                translateAnimation2.setToX(isOn ? this.getTranslateX() + 17 : this.getTranslateX());
                fillAnimation.setFromValue(isOn ? Color.rgb(255, 161, 161) : Color.rgb(156, 240, 168));
                fillAnimation.setToValue(isOn ? Color.rgb(156, 240, 168) : Color.rgb(255, 161, 161));
                animation.play();
                animation2.play();
            });

            setOnMouseClicked(event -> {
                switchedOn.set(!switchedOn.get());
            });
        }
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        primaryStage.setScene(new Scene(createContent()));
        primaryStage.show();
    }
    
    public static void main(String[] args) {
        launch(args);
    }
}

尝试的FXML代码

<?xml version="1.0" encoding="UTF-8"?>

<?import application.ToggleSwitch?>
<?import javafx.scene.layout.AnchorPane?>

<AnchorPane fx:id="root" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="300.0" prefWidth="300.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.Controller">
   <children>
      <ToggleSwitch fx:id="toggle" layoutX="129.0" layoutY="131.0" />
      <ToggleSwitch fx:id="toggle4" layoutX="129.0" layoutY="183.0" />
      <ToggleSwitch fx:id="toggle2" layoutX="129.0" layoutY="76.0" />
      <ToggleSwitch fx:id="toggle1" layoutX="129.0" layoutY="21.0" />
   </children>
</AnchorPane>

解决步骤

1. 重构ToggleSwitch类,使其符合FXML复用要求

你的ToggleSwitch目前是Main的内部静态类,且依赖外部静态text变量,会导致多实例共用同一文本控件,完全无法在FXML中复用。需要做以下修改:

  • 将ToggleSwitch提取为独立公共类
  • 把text移到类内部作为成员变量,避免静态共享
  • 补充标准的JavaFX属性getter/setter方法
  • 调整控件尺寸,让文本能正常显示

修改后的ToggleSwitch.java:

package application;

import javafx.animation.FillTransition;
import javafx.animation.ParallelTransition;
import javafx.animation.TranslateTransition;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.scene.Parent;
import javafx.scene.effect.DropShadow;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.FontSmoothingType;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment;
import javafx.util.Duration;

public class ToggleSwitch extends Parent {
    private final BooleanProperty switchedOn = new SimpleBooleanProperty(false);
    private final TranslateTransition translateAnimation = new TranslateTransition(Duration.seconds(0.25));
    private final TranslateTransition textTranslateAnimation = new TranslateTransition(Duration.seconds(0.25));
    private final FillTransition fillAnimation = new FillTransition(Duration.seconds(0.25));
    private final ParallelTransition animation = new ParallelTransition(translateAnimation, fillAnimation);
    private final ParallelTransition textAnimation = new ParallelTransition(textTranslateAnimation);
    private final Text statusText;

    // 标准属性访问方法
    public BooleanProperty switchedOnProperty() {
        return switchedOn;
    }

    public boolean isSwitchedOn() {
        return switchedOn.get();
    }

    public void setSwitchedOn(boolean switchedOn) {
        this.switchedOn.set(switchedOn);
    }

    public ToggleSwitch() {
        // 背景矩形
        Rectangle background = new Rectangle(60, 20);
        background.setArcWidth(20);
        background.setArcHeight(20);
        background.setFill(Color.rgb(255, 161, 161));
        background.setStroke(Color.SLATEGREY);

        // 开关滑块
        Circle trigger = new Circle(10);
        trigger.setCenterX(10);
        trigger.setCenterY(10);
        trigger.setFill(Color.rgb(245, 245, 245));
        trigger.setStroke(Color.SLATEGREY);
        trigger.setEffect(new DropShadow(2, Color.GRAY));

        // 状态文本
        statusText = new Text();
        statusText.setFont(Font.font("Verdana", FontWeight.MEDIUM, 8));
        statusText.setFill(Color.BLACK);
        statusText.setTextAlignment(TextAlignment.CENTER);
        statusText.setFontSmoothingType(FontSmoothingType.LCD);
        statusText.textProperty().bind(Bindings.when(switchedOnProperty()).then("ON").otherwise("OFF"));
        statusText.setTranslateX(5);
        statusText.setTranslateY(14);

        // 动画配置
        translateAnimation.setNode(trigger);
        textTranslateAnimation.setNode(statusText);
        fillAnimation.setShape(background);

        // 添加所有子节点到ToggleSwitch
        getChildren().addAll(background, trigger, statusText);

        // 状态变化监听
        switchedOn.addListener((obs, oldState, newState) -> {
            boolean isOn = newState;
            translateAnimation.setToX(isOn ? background.getWidth() - 20 : 0);
            textTranslateAnimation.setToX(isOn ? background.getWidth() - 25 : 5);
            fillAnimation.setFromValue(isOn ? Color.rgb(255, 161, 161) : Color.rgb(156, 240, 168));
            fillAnimation.setToValue(isOn ? Color.rgb(156, 240, 168) : Color.rgb(255, 161, 161));
            animation.play();
            textAnimation.play();
        });

        // 点击切换状态
        setOnMouseClicked(event -> setSwitchedOn(!isSwitchedOn()));
    }
}

2. 让Scene Builder识别控件

Scene Builder会自动扫描符合要求的JavaFX控件,只要满足:

  • 类是公共的
  • 继承自javafx.scene.Node或其子类(Parent符合要求)
  • 有公共无参构造函数
  • 包含JavaFX属性和对应getter/setter方法

操作步骤:

  1. 编译修改后的ToggleSwitch.java,确保无报错
  2. 打开Scene Builder,点击左侧库面板的Import JAR/FXML File按钮
  3. 选择项目编译后的class文件路径或项目JAR包
  4. 导入后,ToggleSwitch会出现在Custom分类下,可直接拖到设计界面使用

3. 测试FXML加载

修改Main类加载FXML文件:

package application;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception {
        Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
        primaryStage.setScene(new Scene(root));
        primaryStage.setTitle("Toggle Switch Demo");
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

现在你可以在Scene Builder中可视化编辑滑动开关布局,运行时每个开关实例独立工作。


内容的提问来源于stack exchange,提问作者StealthRT

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 15:30:55