如何将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方法
操作步骤:
- 编译修改后的
ToggleSwitch.java,确保无报错 - 打开Scene Builder,点击左侧库面板的
Import JAR/FXML File按钮 - 选择项目编译后的class文件路径或项目JAR包
- 导入后,
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
相关产品推荐
相关产品推荐

