Hey all I am trying to figure out how to transform my JavaFX slider switch to a Scene Builder 2.0 control so that I can use it within scene Builder.
I currently have been watching these 2 YouTube videos:
To make the slider switch:
Youtube Vid #1
To create a scene builder control:
Youtube Vid #2
The issue with the 2 YouTube videos above is that it shows how to do this with first starting out designing it within Scene Builder 2.0 which is not the way I did it when following the first YouTube video.
So is there a way to transfer what I have in java to the Scene Builder to create the control?
My complete slider switch code:
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);
}
}
other code
<?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>
2
Approach
Following the steps from:
- JavaFX custom component usage in SceneBuilder
In this example, I compressed everything into a single module and project.
The steps are similar, so I won’t repeat them here.
Output
Custom control displayed in Scene Builder:
Custom control loaded in FXML generated by SceneBuilder and displayed in a standalone JavaFX application:
Sample code
ToggleSwitch.java
The ToggleSwitch code is a copy of the code from your question with minor modifications, I didn’t try to change or improve it in any functional way.
package org.example.customcomponent;
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.*;
import javafx.util.Duration;
public 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);
private final Text text = new Text();
public BooleanProperty switchedOnProperty() {
return switchedOn;
}
public boolean checkSwitch() {
switchedOn.set(!switchedOn.get());
return switchedOn.get();
}
public Text getText() {
return text;
}
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.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;
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()));
}
}
ToggleController.java
package org.example.customcomponent;
import javafx.fxml.FXML;
import javafx.scene.layout.VBox;
public class ToggleController {
@FXML
private VBox toggleContainer;
@FXML
private ToggleSwitch toggleSwitch;
@FXML
private void initialize() {
toggleContainer.getChildren().add(
toggleSwitch.getText()
);
toggleSwitch.switchedOnProperty().addListener((o, wasOn, isOn) ->
System.out.println("Switch state: " + isOn)
);
}
}
ToggleApp.java
package org.example.customcomponent;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
public class ToggleApp extends Application {
@Override
public void start(Stage stage) throws IOException {
FXMLLoader loader = new FXMLLoader(ToggleApp.class.getResource("toggle.fxml"));
Parent toggle = loader.load();
stage.setScene(new Scene(toggle));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
toggle.fxml
This FXML was generated by SceneBuilder.
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.VBox?>
<?import org.example.customcomponent.ToggleSwitch?>
<VBox fx:id="toggleContainer" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" spacing="6.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.example.customcomponent.ToggleController">
<children>
<ToggleSwitch fx:id="toggleSwitch" />
</children>
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
</padding>
</VBox>
module-info.java
module org.example.customcomponent {
requires javafx.controls;
requires javafx.fxml;
opens org.example.customcomponent to javafx.fxml;
exports org.example.customcomponent;
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>customcomponent</artifactId>
<version>1.0-SNAPSHOT</version>
<name>customcomponent</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>21.0.3</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>21.0.3</version>
</dependency>
</dependencies>
</project>
Related
While it is slightly different, some of the ideas in this light bulb example might help in the implementation of a reusable toggle-type component.
There are also various toggle controls in the JavaFX source. You could examine them to see how they work. However, copying the way they are written by extending javafx.scene.control.Control
or trying to greatly change the look (beyond CSS skinning) or behavior of existing controls is a complex thing that is overkill for many tasks.
Similarly, there are toggles in MaterialFX. The MaterialFX toggles are very similar to yours, so I would advise using them instead of creating your own.
2