JavaFX:如何让自定义ToggleSwitch组件在SceneBuilder中可调整大小?
解决自定义ToggleSwitch组件无法调整大小的问题
你的代码已经继承了Region,但没正确实现Region的尺寸逻辑,也没在resize时同步更新内部元素状态,导致无法调整大小。按以下步骤修改:
1. 重写Region的尺寸计算方法
Region需要明确告知布局系统自身的首选、最小尺寸,SceneBuilder才能识别并提供调整选项。添加以下方法:
@Override protected double computePrefWidth(double height) { return PREFERRED_WIDTH; } @Override protected double computePrefHeight(double width) { return PREFERRED_HEIGHT; } @Override protected double computeMinWidth(double height) { return PREFERRED_HEIGHT; // 最小宽度等于高度,保证滑块能正常显示 } @Override protected double computeMinHeight(double width) { return 20; // 设置合理的最小高度 }
2. 完善resize方法,同步更新内部元素
原来的resize方法只更新了变量,没有实际修改背景和滑块的尺寸、位置。修改resize方法:
private void resize() { // 计算扣除内边距后的可用空间 width = getWidth() - getInsets().getLeft() - getInsets().getRight(); height = getHeight() - getInsets().getTop() - getInsets().getBottom(); if (width > 0 && height > 0) { // 更新背景矩形的尺寸、圆角和位置 background.setWidth(width); background.setHeight(height); background.setArcWidth(height); // 圆角半径等于高度,保持胶囊形状 background.setArcHeight(height); background.setTranslateX(getInsets().getLeft()); background.setTranslateY(getInsets().getTop()); // 更新滑块的半径和位置 trigger.setRadius(height / 2); trigger.setCenterX(getInsets().getLeft() + (switchedOn.get() ? (width - height) : height / 2)); trigger.setCenterY(getInsets().getTop() + height / 2); // 更新动画的目标位置(基于当前组件尺寸) translateAnimation.setToX(switchedOn.get() ? (width - height) : 0); } }
3. 修正开关状态变化的动画逻辑
原来的动画目标位置写死了100-50,改成动态计算的width - height,确保组件大小变化时动画位置正确:
switchedOn.addListener((observableValue, oldState, newState) -> { boolean isOn = newState; translateAnimation.setToX(isOn ? (width - height) : 0); fillAnimation.setFromValue(isOn ? Color.WHITE : Color.LIGHTGREEN); fillAnimation.setToValue(isOn ? Color.LIGHTGREEN : Color.WHITE); animation.play(); });
4. 初始化时监听内边距变化(可选但推荐)
在构造函数里添加内边距监听,确保内边距变化时也能触发resize:
public ToggleSwitch(){ // 其他初始化代码... // 监听内边距变化 insetsProperty().addListener(o -> resize()); // 初始调用一次resize,保证布局正确 resize(); }
修改后的完整代码
import javafx.animation.FillTransition; import javafx.animation.ParallelTransition; import javafx.animation.TranslateTransition; import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.scene.layout.Region; import javafx.scene.paint.Color; import javafx.scene.shape.Circle; import javafx.scene.shape.Rectangle; import javafx.util.Duration; public class ToggleSwitch extends Region { private final double PREFERRED_WIDTH = 100; private final double PREFERRED_HEIGHT = 50; private final BooleanProperty switchedOn = new SimpleBooleanProperty(false); private final TranslateTransition translateAnimation = 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 double width; private double height; private Rectangle background; private Circle trigger; public ToggleSwitch() { width = PREFERRED_WIDTH; height = PREFERRED_HEIGHT; // 初始化背景(不设置固定尺寸,交给resize处理) background = new Rectangle(); background.setFill(Color.WHITE); background.setStroke(Color.LIGHTGRAY); // 初始化滑块(不设置固定尺寸) trigger = new Circle(); trigger.setFill(Color.WHITE); trigger.setStroke(Color.LIGHTGRAY); translateAnimation.setNode(trigger); fillAnimation.setShape(background); getChildren().addAll(background, trigger); switchedOn.addListener((observableValue, oldState, newState) -> { boolean isOn = newState; translateAnimation.setToX(isOn ? (width - height) : 0); fillAnimation.setFromValue(isOn ? Color.WHITE : Color.LIGHTGREEN); fillAnimation.setToValue(isOn ? Color.LIGHTGREEN : Color.WHITE); animation.play(); }); setOnMouseClicked(event -> { switchedOn.set(!switchedOn.get()); }); // 监听尺寸和内边距变化 widthProperty().addListener(o -> resize()); heightProperty().addListener(o -> resize()); insetsProperty().addListener(o -> resize()); // 初始布局 resize(); } private void resize() { width = getWidth() - getInsets().getLeft() - getInsets().getRight(); height = getHeight() - getInsets().getTop() - getInsets().getBottom(); if (width > 0 && height > 0) { // 更新背景 background.setWidth(width); background.setHeight(height); background.setArcWidth(height); background.setArcHeight(height); background.setTranslateX(getInsets().getLeft()); background.setTranslateY(getInsets().getTop()); // 更新滑块 trigger.setRadius(height / 2); trigger.setCenterX(getInsets().getLeft() + (switchedOn.get() ? (width - height) : height / 2)); trigger.setCenterY(getInsets().getTop() + height / 2); // 更新动画目标位置 translateAnimation.setToX(switchedOn.get() ? (width - height) : 0); } } @Override protected double computePrefWidth(double height) { return PREFERRED_WIDTH; } @Override protected double computePrefHeight(double width) { return PREFERRED_HEIGHT; } @Override protected double computeMinWidth(double height) { return PREFERRED_HEIGHT; } @Override protected double computeMinHeight(double width) { return 20; } public BooleanProperty switchedOnProperty() { return switchedOn; } }
修改后重新打包jar,导入SceneBuilder就能正常调整组件大小,内部的背景和滑块也会跟着组件尺寸自适应变化。
内容的提问来源于stack exchange,提问作者AnotherByte
相关产品推荐
相关产品推荐

