Issue
I would like to ask about two (or more) buttons handling using JavaFX. On my project I have two Toggle Buttons. I want to assign to one of them a style (green round border) when it is pressed. When I'm clicking on another button, I want to delete the style from first one and move it to second button. Currently, I have situation that two buttons are visible as "pressed" at the same time (picture attached). I would like to ask how to avoid this situation. Below is a code from my Controller file:
public void oneButton()
{
ToggleButton btn1=new ToggleButton();
btn1.getStyleClass().add(".toggle-button:selected");
}
public void twoButton()
{
ToggleButton btn2=new ToggleButton();
btn2.getStyleClass().add(".toggle-button:selected");
}
}
Solution
You can make use of a ToggleGroup
:
A class which contains a reference to all Toggles whose selected variables should be managed such that only a single Toggle within the ToggleGroup may be selected at any one time [...] (docs).
ToggleGroupExampleApp.java:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class ToggleGroupExampleApp extends Application {
@Override
public void start(Stage stage) {
// Create the toggle buttons:
ToggleButton firstToggleButton = new ToggleButton("First Toggle Button"),
secondToggleButton = new ToggleButton("Second Toggle Button");
// Create a toggle group so only one toggle button can be selected at a time:
ToggleGroup toggleGroup = new ToggleGroup();
// Add the buttons to the toggle group:
firstToggleButton.setToggleGroup(toggleGroup);
secondToggleButton.setToggleGroup(toggleGroup);
// Create a container:
VBox vBox = new VBox(firstToggleButton, secondToggleButton);
// Load a css file:
vBox.getStylesheets().add(ToggleGroupExampleApp.class.getResource("styling.css").toExternalForm());
// Prepare and show stage:
stage.setScene(new Scene(vBox, 200, 100));
stage.show();
}
public static void main(String[] args) {
launch();
}
}
For styling purposes you can make use of the already available selected pseudoclass.
styling.css:
.toggle-button {
-fx-border-radius: 10;
-fx-background-radius: 10;
-fx-border-width: 2;
-fx-pref-width: 180;
-fx-pref-height: 35;
}
.toggle-button:selected {
-fx-border-color: green;
}
VBox {
-fx-spacing: 10;
-fx-padding: 10;
}
Edit (variation with FXML file and Controller class added):
Controller.class:
package org.example;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToggleGroup;
import java.net.URL;
import java.util.ResourceBundle;
public class Controller implements Initializable {
@FXML
private ToggleButton
firstToggleButton,
secondToggleButton;
@Override
public void initialize(URL location, ResourceBundle resources) {
// Create a toggle group so only one toggle button can be selected at once:
ToggleGroup toggleGroup = new ToggleGroup();
// Add the buttons to the toggle group:
firstToggleButton.setToggleGroup(toggleGroup);
secondToggleButton.setToggleGroup(toggleGroup);
}
}
main.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.ToggleButton?>
<?import javafx.scene.layout.VBox?>
<VBox spacing="10.0" stylesheets="@styling.css" xmlns="http://javafx.com/javafx/11.0.1"
xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.example.Controller">
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
</padding>
<ToggleButton fx:id="firstToggleButton" mnemonicParsing="false" prefHeight="35.0"
prefWidth="150.0" text="First Toggle Button">
</ToggleButton>
<ToggleButton fx:id="secondToggleButton" mnemonicParsing="false" prefHeight="35.0"
prefWidth="150.0" text="Second Toggle Button">
</ToggleButton>
</VBox>
styling.css:
.toggle-button {
-fx-border-radius: 10;
-fx-background-radius: 10;
-fx-border-width: 2;
}
.toggle-button:selected {
-fx-border-color: green;
}
ToggleGroupExampleApp.java:
package org.example;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
public class ToggleGroupExampleApp extends Application {
@Override
public void start(Stage stage) throws IOException {
FXMLLoader loader = new FXMLLoader(ToggleGroupExampleApp.class.getResource("main.fxml"));
stage.setScene(new Scene(loader.load()));
stage.show();
}
public static void main(String[] args) {
launch();
}
}
Answered By - anko
Answer Checked By - Candace Johnson (JavaFixing Volunteer)