I used the custom switch button in the custom SwitchButton answer. Now I would like to animate the circle part when the user toggles between the 2 values (state). I used KeyValue and KeyFrame in order to do so.
The snippet of the animation that I added to the SwitchButton() method :
KeyValue start = new KeyValue(button.alignmentProperty(), Pos.CENTER_RIGHT);
KeyValue end = new KeyValue(button.alignmentProperty(), Pos.CENTER_LEFT);
KeyFrame frame = new KeyFrame(Duration.seconds(4), start, end);
Timeline timeline = new Timeline(frame);
timeline.play();
How can I make the animation?
CodePudding user response:
You can use a TranslateTransition, to animate moving the knob for the switch on the track.
Although unrelated to the animation, this solution also modifies the original example to use style classes in a stylesheet, an on
pseudo-class, and an exposed on
state property for the custom control.
Control sizes are still hardcoded in this solution, but if preferred, you could modify the control to use a system based on em
sizes (similar to the standard JavaFX controls).
This answer is also based on a combination of ideas from previous questions:
Related question:
Example Code
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.css.PseudoClass;
import javafx.event.*;
import javafx.geometry.*;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.scene.shape.*;
import javafx.stage.Stage;
import javafx.util.Duration;
public class SwitchApp extends Application {
@Override
public void start(Stage stage) {
Switch lightSwitch = new Switch();
lightSwitch.onProperty().addListener((observable, wasOn, nowOn) -> {
System.out.println(nowOn ? "on" : "off");
});
StackPane layout = new StackPane(lightSwitch);
layout.setPadding(new Insets(30));
stage.setScene(new Scene(layout));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
class Switch extends StackPane {
private static final double TRACK_WIDTH = 30;
private static final double TRACK_HEIGHT = 10;
private static final double KNOB_DIAMETER = 15;
private static final Duration ANIMATION_DURATION = Duration.seconds(0.25);
public static final String CSS = "data:text/css," // language=CSS
"""
.switch > .track {
-fx-fill: #ced5da;
}
.switch > .knob {
-fx-effect: dropshadow(
three-pass-box,
rgba(0,0,0,0.2),
0.2, 0.0, 0.0, 2
);
-fx-background-color: WHITE;
}
.switch:on > .track {
-fx-fill: #80C49E;
}
.switch:on > .knob {
-fx-effect: dropshadow(
three-pass-box,
rgba(0,0,0,0.2),
0.2, 0.0, 0.0, 2
);
-fx-background-color: #00893d;
}
""";
private final TranslateTransition onTransition;
private final TranslateTransition offTransition;
public Switch() {
// construct switch UI
getStylesheets().add(CSS);
getStyleClass().add("switch");
Rectangle track = new Rectangle(TRACK_WIDTH, TRACK_HEIGHT);
track.getStyleClass().add("track");
track.setArcHeight(track.getHeight());
track.setArcWidth(track.getHeight());
Button knob = new Button();
knob.getStyleClass().add("knob");
knob.setShape(new Circle(KNOB_DIAMETER / 2));
knob.setMaxSize(KNOB_DIAMETER, KNOB_DIAMETER);
knob.setMinSize(KNOB_DIAMETER, KNOB_DIAMETER);
knob.setFocusTraversable(false);
setAlignment(knob, Pos.CENTER_LEFT);
getChildren().addAll(track, knob);
setMinSize(TRACK_WIDTH, KNOB_DIAMETER);
// define animations
onTransition = new TranslateTransition(ANIMATION_DURATION, knob);
onTransition.setFromX(0);
onTransition.setToX(TRACK_WIDTH - KNOB_DIAMETER);
offTransition = new TranslateTransition(ANIMATION_DURATION, knob);
offTransition.setFromX(TRACK_WIDTH - KNOB_DIAMETER);
offTransition.setToX(0);
// add event handling
EventHandler<Event> click = e -> setOn(!isOn());
setOnMouseClicked(click);
knob.setOnMouseClicked(click);
onProperty().addListener((observable, wasOn, nowOn) -> updateState(nowOn));
updateState(isOn());
}
private void updateState(Boolean nowOn) {
onTransition.stop();
offTransition.stop();
if (nowOn != null && nowOn) {
onTransition.play();
} else {
offTransition.play();
}
}
public void setOn(boolean on) {
this.on.set(on);
}
public boolean isOn() {
return on.get();
}
public BooleanProperty onProperty() {
return on;
}
public BooleanProperty on =
new BooleanPropertyBase(false) {
@Override protected void invalidated() {
pseudoClassStateChanged(ON_PSEUDO_CLASS, get());
}
@Override public Object getBean() {
return Switch.this;
}
@Override public String getName() {
return "on";
}
};
private static final PseudoClass
ON_PSEUDO_CLASS = PseudoClass.getPseudoClass("on");
}