Using JavaFX 8, I would like to have a responsive layout with three radio buttons that changes from a horizontal layout to a vertical layout as soon as there is not enough horizontal space to put all radio buttons next to each other.
This means the default layout should be like this:
(x) Radio button 1 ( ) Radio button 2 ( ) Radio button 3
And as soon as there is not enough horizontal space to put all three buttons on a single line, I want the layout to change to this:
(x) Radio button 1
( ) Radio button 2
( ) Radio button 3
Note that I want to avoid intermediate states like:
(x) Radio button 1 ( ) Radio button 2
( ) Radio button 3
I have tried to achieve the desired behaviour with a flow pane that changes its orientation dynamically but I encountered small layout glitches when there was another control (e.g. a text area) positioned underneath the flow pane. Here's the code I have used:
import javafx.application.Application;
import javafx.geometry.Orientation;
import javafx.scene.Scene;
import javafx.scene.control.RadioButton;
import javafx.scene.control.TextArea;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class ResponsiveLayout extends Application {
RadioButton radioButton1 = new RadioButton("Radio button 1");
RadioButton radioButton2 = new RadioButton("Radio button 2");
RadioButton radioButton3 = new RadioButton("Radio button 3");
FlowPane flowPane = new FlowPane(radioButton1, radioButton2, radioButton3);
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
flowPane.setHgap(10);
flowPane.setPrefWrapLength(60);
flowPane.widthProperty().addListener((obs, oldWidth, newWidth) -> updateOrientation(newWidth));
VBox container = new VBox(flowPane, new TextArea());
Scene scene = new Scene(container, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
private void updateOrientation(Number paneWidth) {
double childrenPadding = flowPane.getHgap() * (flowPane.getChildren().size() - 1);
int extraPadding = 5;
double childrenWidth = radioButton1.getWidth() +
radioButton2.getWidth() +
radioButton3.getWidth() +
childrenPadding +
extraPadding;
if (paneWidth.doubleValue() < childrenWidth) {
flowPane.setOrientation(Orientation.VERTICAL);
} else {
flowPane.setOrientation(Orientation.HORIZONTAL);
}
}
}
When I run this application and carefully change the width of the scene, I can see the following layout glitch right after the orientation of the flow pane changed from vertical to horizontal:
As you can see, there is an unwanted empty space between the radio buttons and the text area. It vanishes when I increase the scene width further or if the window loses focus. In a bigger application with more controls and containers around the flow pane, I can see even more layout glitches so I'm wondering if there is a better approach to achieving the responsiveness I need.
What could I do to improve the flow pane behavior? Or is there a more suitable layout pane that I could use for this?
I'd appreciate any support. Thanks.
You could toggle between VBox
and HBox
. See if this meets your requirements.
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Control;
import javafx.scene.control.RadioButton;
import javafx.scene.control.TextArea;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class ResponsiveLayout extends Application
{
RadioButton radioButton1 = new RadioButton("Radio button 1");
RadioButton radioButton2 = new RadioButton("Radio button 2");
RadioButton radioButton3 = new RadioButton("Radio button 3");
HBox hBox = new HBox(radioButton1, radioButton2, radioButton3);
StackPane stackPane = new StackPane(hBox);
VBox vBox = new VBox();
boolean isHBox = true;
double controlWidth = -1;
public static void main(String[] args)
{
launch(args);
}
@Override
public void start(Stage primaryStage)
{
hBox.setMaxSize(Control.USE_PREF_SIZE, Control.USE_PREF_SIZE);
stackPane.setAlignment(Pos.CENTER_LEFT);
VBox container = new VBox(stackPane, new TextArea());
Scene scene = new Scene(container, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
container.widthProperty().addListener((obs, oldWidth, newWidth) -> updateOrientation(newWidth));
}
private void updateOrientation(Number paneWidth)
{
if (isHBox) {
if (hBox.getWidth() == stackPane.getWidth()) {
controlWidth = hBox.getWidth();
vBox.getChildren().addAll(hBox.getChildren());
stackPane.getChildren().clear();
stackPane.getChildren().add(vBox);
isHBox = false;
}
}
else {
if (controlWidth <= stackPane.getWidth()) {
hBox.getChildren().addAll(vBox.getChildren());
stackPane.getChildren().clear();
stackPane.getChildren().add(hBox);
isHBox = true;
}
}
}
}