I try to create an application that contains 2 panes in a stackpane.
One pane is the main pane and is centered, the second is smaller and docked to the bottom-left of the stage.
The thing is that I've tried using 'setAlignment' and it does'nt seems to work (although the button is aligned). The small pane is always centered.
What is the problem and how I fix this?
I guess that maybe I can't align a pane, so how can I overcome this?
Pane pane = new Pane();
for (SerialPoint sp : points) {
Circle circle = new Circle(sp.getX(), sp.getY(), 6, Color.GREEN);
pane.getChildren().add(circle);
}
Pane smallPane = new Pane();
smallPane.setScaleX(0.25);
smallPane.setScaleY(0.25);
smallPane.setStyle("-fx-border-color: black;");
for (SerialPoint sp : points) {
Circle circle = new Circle(sp.getX(), sp.getY(), 6, Color.RED);
smallPane.getChildren().add(circle);
}
Button startBtn = new Button("Start");
StackPane stackPane = new StackPane(pane, smallPane, startBtn);
StackPane.setAlignment(smallPane, Pos.BOTTOM_LEFT);
StackPane.setAlignment(startBtn, Pos.TOP_RIGHT);
StackPane.setMargin(startBtn, new Insets(5));
Scene scene = new Scene(stackPane);
(SerialPoint is my inner class)
This is what I get:
Place the scaled Pane inside a Group. For layout purposes, StackPane (and other layout panes) will ignore scale factors and other transforms on nodes, but a Group will not.
From the Group javadoc:
Any transform, effect, or state applied to a Group will be applied to all children of that group. Such transforms and effects will NOT be included in this Group's layout bounds, however if transforms and effects are set directly on children of this Group, those will be included in this Group's layout bounds.
Sample App
import javafx.application.Application;
import javafx.geometry.*;
import javafx.scene.*;
import javafx.scene.control.Button;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import java.util.Random;
public class AligningPains extends Application {
private static final int N_POINTS = 5;
private static final int MAX_POINT_POS = 100;
private static final int POINT_RADIUS = 6;
private static final int PREF_PANE_SIZE = 300;
private static final int BUTTON_INSETS = 5;
Point2D[] points = new Point2D[N_POINTS];
Random random = new Random(42);
#Override
public void start(Stage stage) {
initPoints();
Pane pane = new Pane();
pane.setPrefSize(PREF_PANE_SIZE, PREF_PANE_SIZE);
addCircles(pane, Color.GREEN);
Pane smallPane = new Pane();
smallPane.setStyle("-fx-border-color: black;");
smallPane.setPrefSize(PREF_PANE_SIZE, PREF_PANE_SIZE);
smallPane.setScaleX(0.25);
smallPane.setScaleY(0.25);
addCircles(smallPane, Color.RED);
Group smallGroup = new Group(smallPane);
Button startBtn = new Button("Start");
StackPane stackPane = new StackPane(pane, smallGroup, startBtn);
StackPane.setAlignment(smallGroup, Pos.BOTTOM_LEFT);
StackPane.setAlignment(startBtn, Pos.TOP_RIGHT);
StackPane.setMargin(startBtn, new Insets(BUTTON_INSETS));
stackPane.setPrefSize(PREF_PANE_SIZE, PREF_PANE_SIZE);
Scene scene = new Scene(stackPane);
stage.setScene(scene);
stage.show();
}
private void addCircles(Pane pane, Color color) {
for (Point2D sp : points) {
Circle circle = new Circle(sp.getX(), sp.getY(), POINT_RADIUS, color);
pane.getChildren().add(circle);
}
}
private void initPoints() {
for (int i = 0; i < points.length; i++) {
points[i] = new Point2D(random.nextInt(MAX_POINT_POS), random.nextInt(MAX_POINT_POS));
}
}
public static void main(String[] args) {
launch(args);
}
}
Related
I want to implement some kind of notification system in my application but I have trouble with the calculation of the actual position of my notification. All notifications should appear in a separate stage and each notification should be aligned among themselves and each notification is a simple VBox with two labels (title and message).
I created a little standalone application with the issue I have.
As soon as you press the button on the main stage, a VBox will be created and added to a second notification stage. As soon as a seconds notification needs to be added, this second notification should be below the first notification and so on. Therefore I need to find the height of the first notification in order to position the second notification underneath.
I know I could use a VBox instead, but in my application the notification should make a smooth animation and push the other notifications further down. I removed the whole animation and removing part of notifications so the example stays as small as possible.
The problem is that all notification boxes have the same height - but they don't (if you modify the text and make it longer / smaller).
package whatever;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class NotificationTest {
private Stage notificationStage;
private Pane contentPane;
private static final Integer NOTIFICATION_WIDTH = 250;
private Double notificationOffset = 0.0;
private static final Integer SPACING_BETWEEN_NOTIFICATIONS = 20;
public void start() {
Stage mainStage = new Stage();
TextField textField = new TextField("Some long text for testing purpose with even more letters in oder to create at least one linebreak...");
Button button = new Button("Add Notification");
button.setOnAction(actionEvent -> {
addNotification(textField.getText());
});
VBox vBox = new VBox(10);
vBox.getChildren().addAll(textField, button);
mainStage.setScene(new Scene(vBox, 300, 300));
mainStage.show();
}
private void addNotification(String text) {
if(notificationStage == null) {
notificationStage = new Stage();
notificationStage.setWidth(NOTIFICATION_WIDTH);
notificationStage.setHeight(Screen.getPrimary().getVisualBounds().getHeight() - 50);
notificationStage.setX(Screen.getPrimary().getVisualBounds().getWidth() - 260);
notificationStage.setY(50);
contentPane = new Pane();
contentPane.setStyle("-fx-background-color: transparent");
notificationStage.setScene(new Scene(contentPane));
notificationStage.initStyle(StageStyle.TRANSPARENT);
notificationStage.getScene().setFill(Color.TRANSPARENT);
notificationStage.show();
}
VBox notificationBox = new VBox(10);
notificationBox.setMaxWidth(NOTIFICATION_WIDTH);
notificationBox.setMinWidth(NOTIFICATION_WIDTH);
notificationBox.setStyle("-fx-background-radius: 10; -fx-background-color: red");
notificationBox.getChildren().add(new Label("Title of Notification"));
Label message = new Label(text);
message.setWrapText(true);
notificationBox.getChildren().add(message);
notificationBox.setLayoutY(notificationOffset);
contentPane.getChildren().add(notificationBox);
// Needs to be done - otherwise the height would be 0
contentPane.layout();
System.out.println(notificationBox.getHeight());
notificationOffset += notificationBox.getHeight() + SPACING_BETWEEN_NOTIFICATIONS;
}
}
I used the ScenicView Tool to verify the height and it says that the height is 79, but the System.out tells me the height is 10.4. The 79 value seems to be correct, but how can I get this value in my application?
The short answer is use applyCss():
contentPane.applyCss();
contentPane.layout();
From the documentation:
If required, apply styles to this Node and its children, if any. This
method does not normally need to be invoked directly but may be used
in conjunction with Parent.layout() to size a Node before the next
pulse
The long and better answer is to use a VBox or a ListView.
To add layout animation use LayoutAnimator.java. You can find more details here.
Edit: an mre of using LayoutAnimator to animate newly added notifications:
import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.*;
public class NotificationTest extends Application {
private Stage notificationStage;
private Pane contentPane;
private static final int NOTIFICATION_WIDTH = 250, SPACING_BETWEEN_NOTIFICATIONS = 20;
private static final String LONG_TEXT = "Some long text for testing purpose with even more letters in oder to create "
+ "at least one linebreak...";
private int counter = 0;
#Override
public void start(Stage mainStage) throws Exception {
mainStage = new Stage();
TextField textField = new TextField(LONG_TEXT);
Button button = new Button("Add Notification");
button.setOnAction(actionEvent -> {
addNotification(textField.getText());
});
VBox vBox = new VBox(10, textField, button);
mainStage.setScene(new Scene(vBox, 300, 300));
mainStage.show();
}
private void addNotification(String text) {
if(notificationStage == null) {
notificationStage = new Stage();
notificationStage.setWidth(NOTIFICATION_WIDTH);
notificationStage.setX(Screen.getPrimary().getVisualBounds().getWidth() - 260);
notificationStage.setY(50);
contentPane = new VBox(SPACING_BETWEEN_NOTIFICATIONS);
contentPane.setStyle("-fx-background-color: transparent");
notificationStage.setScene(new Scene(contentPane));
notificationStage.initStyle(StageStyle.TRANSPARENT);
//animate using LayoutAnimator https://gist.github.com/jewelsea/5683558
LayoutAnimator ly = new LayoutAnimator();
ly.observe(contentPane.getChildren());
notificationStage.show();
}
VBox notificationBox = new VBox(10);
notificationBox.setMaxWidth(NOTIFICATION_WIDTH);
notificationBox.setMinWidth(NOTIFICATION_WIDTH);
notificationBox.setStyle("-fx-border-color: black");
notificationBox.getChildren().add(new Label("Title of Notification"));
Label message = new Label(counter++ + ": " +text);
message.setWrapText(true);
notificationBox.getChildren().add(message);
contentPane.getChildren().add(0, notificationBox);
}
public static void main(String[] args) {
launch(null);
}
}
I'm using JavaFx 8.
From the JavaDoc of javafx.scene.Node.scaleYProperty():
[...] This scale factor is not included in layoutBounds by default, which makes it ideal for scaling the entire node after all effects and transforms have been taken into account. [...]
How can I include the scaling factor in layoutBounds, though?
Some context:
In the following example, when pressing the button I would like the GridPane to react also to the scaling of the HBox whithout having to hardcode the prefHeight of the RowConstraints.
Being able to include the scaling factor into the layoutBounds probably would do the trick, but other solutions are welcome as well.
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ToggleButton;
import javafx.scene.layout.*;
import javafx.stage.Stage;
public class ScalingStandAlone extends Application {
private VBox vBox = new VBox();
private GridPane gridPane = new GridPane();
private HBox hBox = new HBox();
private ToggleButton button = new ToggleButton("Click to scale");
private Label firstRowLabel = new Label("Some content in text form");
private Label secondRowLabel = new Label("Some content for scaling");
private Label thirdRowLabel = new Label("Some moving content");
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage stage) throws Exception {
AnchorPane root = new AnchorPane();
AnchorPane.setTopAnchor(vBox, 5.);
AnchorPane.setLeftAnchor(vBox, 5.);
AnchorPane.setRightAnchor(vBox, 5.);
root.autosize();
Scene scene = new Scene(root);
stage.setTitle("GridRow Scale Demo");
stage.setWidth(400);
stage.setHeight(300);
stage.setScene(scene);
stage.show();
root.getChildren().add(vBox);
vBox.setAlignment(Pos.CENTER);
vBox.getChildren().add(gridPane);
vBox.getChildren().add(button);
vBox.setStyle("-fx-spacing: 15;");
configureGridPane(root);
button.setOnAction(event -> {
hBox.setScaleY(button.isSelected() ? 2 : 1);
});
}
private void configureGridPane(Pane root) {
hBox.getChildren().add(secondRowLabel);
// Styling //
firstRowLabel.setStyle("-fx-padding: 5;");
hBox.setStyle("-fx-background-color: #800000; -fx-padding: 5;");
secondRowLabel.setStyle("-fx-text-fill: white; -fx-padding: 5;");
thirdRowLabel.setStyle("-fx-padding: 5;");
gridPane.add(firstRowLabel, 0, 0);
gridPane.add(hBox, 0, 1);
gridPane.add(thirdRowLabel, 0, 2);
gridPane.setGridLinesVisible(true);
gridPane.getColumnConstraints().add(new ColumnConstraints());
gridPane.getColumnConstraints().get(0).setPercentWidth(100);
}
}
From the Javadocs for Group:
Any transform, effect, or state applied to a Group will be applied to
all children of that group. Such transforms and effects will NOT be
included in this Group's layout bounds, however if transforms and
effects are set directly on children of this Group, those will be
included in this Group's layout bounds.
(my emphasis added).
Therefore, if you simply wrap your HBox in a Group, you will achieve the desired effect:
// gridPane.add(hBox, 0, 1);
gridPane.add(new Group(hBox), 0, 1);
I would like to create a BorderPane layout in JavaFX with no center pane.
The code I have written so far only implements the left and right borders and is below:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class GUI_Practice extends Application {
#Override
public void start(Stage stage) throws Exception {
String blackBorder = "-fx-border-style: solid; -fx-border-width: 1; -fx-border-color: black";
/* Left column */
Button save = new Button("Save");
Button del = new Button("Delete");
HBox settings = new HBox(save, del);
VBox leftCol = new VBox(settings);
leftCol.setStyle(blackBorder);
/* Right column */
Button calculate = new Button("Calculate");
Button cancel = new Button("Cancel");
HBox runButtons = new HBox(calculate, cancel);
VBox rightCol = new VBox(runButtons);
rightCol.setStyle(blackBorder);
/* Set up borderpane */
BorderPane root = new BorderPane();
root.setPadding(new Insets(15));
root.setLeft(leftCol);
root.setRight(rightCol);
Scene scene = new Scene(root, 800, 600);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
The output it gives is shown in the image below:
However, I want it to look more like this:
Where the left and right columns are equal width and take up the entire width of the window. Additionally, the columns do not change width with the window, so the whitespace in the middle gets bigger as the window gets bigger.
What do I need to change to make the columns fill the width of the window?
(P.S. I'm still learning, so if the solution could avoid FXML (which I don't understand yet), that'd be great)
EDIT: as per #k88's suggestion, my start method now looks like so:
public void start(Stage stage) throws Exception {
String blackBorder = "-fx-border-style: solid; -fx-border-width: 1; -fx-border-color: black";
Button calculate = new Button("Calculate");
Button cancel = new Button("Cancel");
HBox runButtons = new HBox(calculate, cancel);
VBox rightCol = new VBox(runButtons);
rightCol.setStyle(blackBorder);
Button save = new Button("Save");
Button del= new Button("Delete");
HBox settings = new HBox(save, load);
VBox leftCol = new VBox(settings);
leftCol.setStyle(blackBorder);
HBox root = new HBox(leftCol, rightCol);
root.setPadding(new Insets(15));
Scene scene = new Scene(root, 800, 600);
stage.setScene(scene);
stage.show();
}
Giving a window looking like:
There are different ways to get this problem fixed.
If you want to still gain the benefits from BorderPane (like to have top and bottom panes), you can set a HBox/GridPane as the center (without setting left/right).
If you are not bothered about top and bottom layout implementations, then as #k88 suggested, you can use directly HBox or GridPane as your root node.
Using HBox:
HBox.setHGrow(leftCol,Priority.ALWAYS);
HBox.setHGrow(rightCol,Priority.ALWAYS);
HBox root = new HBox();
root.setPadding(new Insets(15));
root.getChildren().addAll(leftCol, rightCol);
Using GridPane:
GridPane root = new GridPane();
ColumnConstraints col1 = new ColumnConstraints();
col1.setPercentWidth(50);
ColumnConstraints col2 = new ColumnConstraints();
col2.setPercentWidth(50);
root.getColumnConstraints().addAll(col1,col2);
root.addRow(0, leftCol,rightCol);
Update: In either cases, if you want your buttons to auto stretch, bind the width of the buttons to its layout. This way you can control the buttons width proportion in the HBox.
Button calculate = new Button("Calculate");
Button cancel = new Button("Cancel");
HBox runButtons = new HBox(calculate, cancel);
calculate.prefWidthProperty().bind(runButtons.widthProperty().divide(2));
cancel.prefWidthProperty().bind(runButtons.widthProperty().divide(2));
Update 2: Please find below a sample demo.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.*;
import javafx.stage.Stage;
public class Sample extends Application {
public void start(Stage stage) throws Exception {
String blackBorder = "-fx-border-style: solid; -fx-border-width: 1; -fx-border-color: black";
Button calculate = new Button("Calculate");
Button cancel = new Button("Cancel");
HBox runButtons = new HBox(calculate, cancel);
calculate.prefWidthProperty().bind(runButtons.widthProperty().divide(2));
cancel.prefWidthProperty().bind(runButtons.widthProperty().divide(2));
VBox rightCol = new VBox(runButtons);
rightCol.setStyle(blackBorder);
Button save = new Button("Save");
Button del = new Button("Delete");
HBox settings = new HBox(save, del);
save.prefWidthProperty().bind(settings.widthProperty().divide(3)); // 1/3
del.prefWidthProperty().bind(settings.widthProperty().divide(3).multiply(2)); // 2/3
VBox leftCol = new VBox(settings);
leftCol.setStyle(blackBorder);
GridPane root = new GridPane();
ColumnConstraints col1 = new ColumnConstraints();
col1.setPercentWidth(50);
ColumnConstraints col2 = new ColumnConstraints();
col2.setPercentWidth(50);
root.getColumnConstraints().addAll(col1,col2);
root.addRow(0, leftCol,rightCol);
Scene scene = new Scene(root, 800, 600);
stage.setScene(scene);
stage.show();
}
public static void main(String... a) {
Application.launch(a);
}
}
I'm trying to build a javafx trimmer, I've done it, however when I added a setOnMouseClicked on ImageViews the trimmer I made hides these nodes, so the action is not handled.
I've have set Rectangle -50 in order to overlay the trimmer to the ImageView, unfortunately, this make event not working.
Rectangle rectangle = new Rectangle(0, -50, 80, 40);
the flowing controller builds is frame
package app.controller;
import java.io.File;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ScrollPane;
import javafx.scene.effect.DropShadow;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
public class Trimmer2 implements Initializable {
private Node selectedNode;
#FXML
private HBox hboxStream;
#FXML
private HBox hboxStreamTrim;
#FXML
private Label lblTime;
public void initialize(URL arg0, ResourceBundle arg1) {
buidTrimmer();
}
private void buidTrimmer() {
ImageView pic = null;
HBox hbox = new HBox();
DropShadow shadow = new DropShadow();
ClassLoader classLoader = getClass().getClassLoader();
File path = new File(classLoader.getResource("streams/sub_stream").getPath());
File [] files = path.listFiles();
Image[] images = new Image[files.length];
ImageView[] pics = new ImageView[files.length];
for (int i = 0; i < files.length; i++) {
final Image image = images[i] =
new Image(files[i].toURI().toString(), 80, 40, false, false);
pic = pics[i] =
new ImageView(image);
pic.setEffect(shadow);
pic.setOnMouseClicked((MouseEvent me) -> {
System.out.println("do action");
});
hbox.getChildren().add(pics[i]);
}
Rectangle rectangle = new Rectangle(0, -50, 80, 40);
rectangle.setFill(Color.rgb(33, 150, 243, 0.5));
Pane pane = new Pane( rectangle );
makeSelectable(rectangle, pane);
VBox vbox = new VBox();
vbox.setSpacing(10);
vbox.getChildren().addAll(hbox,pane);
ScrollPane scrollPane = new ScrollPane();
scrollPane.setContent(vbox);
scrollPane.setId("my_scrollPane");
hboxStream.getChildren().add(scrollPane);
}
private void makeSelectable(Node node, Pane root) {
node.setOnMouseClicked(event -> {
if (selectedNode != node) {
root.getChildren().removeIf(candidate -> candidate instanceof ResizingControl);
selectedNode = node;
node.toFront();
ResizingControl resizingControl = new ResizingControl(node);
root.getChildren().add(resizingControl);
}
System.out.println("here");
event.consume();
});
}
}
Try to add the Hbox and the rectangle inside your pane, by the way that pane must be empty. hope it helps :)
Rectangle rectangle = new Rectangle(0, 0, imgWidth, imgHeight);
rectangle.setFill(Color.rgb(33, 150, 243, 0.35));
Pane pane = new Pane();
VBox vbox = new VBox();
vbox.getChildren().addAll(pane);
pane.getChildren().addAll(hbox,rectangle);
Mouse events go to the topmost node that is not mouse transparent. Simply set the property to true and the mouse events will be handled at the usual node. One additional thing that can make your life easier is the fact that you can also place nodes inside a layout that are not affected by the layout algorithm of the parent and that do not contribute to the size calculations. Simply set managed to false. This allows you to implement the selection like this:
private Node selectedNode;
#Override
public void start(Stage primaryStage) throws Exception {
HBox container = new HBox();
File directory = new File("someDirectory");
Rectangle selection = new Rectangle(80, 40, Color.rgb(100, 100, 255, 0.5));
selection.setStrokeType(StrokeType.INSIDE);
selection.setStrokeWidth(4);
selection.setStroke(Color.WHITE);
selection.setVisible(false);
selection.setMouseTransparent(true);
selection.setManaged(false);
for (File file : directory.listFiles(f -> f.getName().endsWith(".jpg"))) {
Image image = new Image(file.toURI().toURL().toExternalForm(), 80, 40, false, false);
ImageView imageView = new ImageView(image);
container.getChildren().add(imageView);
imageView.setOnMouseClicked(evt -> {
if (selectedNode == imageView) {
selection.setVisible(false);
selectedNode = null;
} else {
selection.setVisible(true);
selection.setLayoutX(imageView.getLayoutX());
selectedNode = imageView;
}
});
}
container.getChildren().add(selection);
Scene scene = new Scene(new ScrollPane(container), 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
Note: I also removed the use of getResource and that is for a good reason. Resources are not guaranteed to be available as files. E.g. if you use a .jar file, you won't be able to access them via File. Either store outside of the classpath in a directory or make a list of the resources available as text resource to allow you multiple resources without hardcoding every single one of them. (Don't use File with the latter approach.)
I have a menu which should be used to navigate through different "views"(layout). The menu should always be visible. The only thing that should change is the displayed content, in dependency of the selected menu item.
I have a very basic example, using two classes, created below:
public class Main extends Application{
static final Pane pane = new Pane();
public void start(Stage stage) {
pane.getChildren().addAll(Menu.getPaneMenu());
Scene scene = new Scene(pane);
stage.setScene(scene);
stage.setMaximized(true);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
public class Menu {
private static final Pane paneMenu = new Pane();
static Pane getPaneMenu() {
Button btn1 = new Button("Menu 1");
btn1.relocate(100, 10);
btn1.setPrefSize(100, 20);
btn1.setOnAction(e -> {
Label lbl = new Label("You clicked Menu1");
lbl.relocate(200, 40);
Main.pane.getChildren().add(lbl);
});
Button btn2 = new Button("Menu 2");
btn2.relocate(300, 10);
btn2.setPrefSize(100, 20);
btn2.setOnAction(e -> {
Label lbl = new Label("You clicked Menu2");
lbl.relocate(200, 40);
Main.pane.getChildren().add(lbl);
});
paneMenu.getChildren().addAll(btn1, btn2);
return paneMenu;
}
}
The challenge I´m facing is, that when clicking a menu, the old menu stays(as you can see, the displayed text overwrites the other):
What would be an efficient way to solve this overlapping?
Here is an example that shows how to load two different Panes in a BorderPane.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Sample extends Application {
#Override public void start(Stage stage) {
BorderPane root = new BorderPane();
Button loadSceneOne = new Button("Load Scene One");
//Event hanlder to load scene one
loadSceneOne.setOnAction((event)->{
root.setCenter(getSceneOne());
});
//Event hanlder to load scene two
Button loadSceneTwo = new Button("Load Scene Two");
loadSceneTwo.setOnAction((event)->{
root.setCenter(getSceneTwo());
});
VBox menu = new VBox();
menu.getChildren().addAll(loadSceneOne, loadSceneTwo);
root.setLeft(menu);
Scene scene = new Scene(root);
stage.setMaximized(true);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
//Scene One
Pane getSceneOne()
{
Pane pane = new Pane();
Label label = new Label("Scene One!");
StackPane stackPane = new StackPane();
stackPane.getChildren().add(label);
pane.getChildren().add(stackPane);
return pane;
}
//Scene Two
Pane getSceneTwo()
{
Pane pane = new Pane();
Label label = new Label("Scene Two!");
StackPane stackPane = new StackPane();
stackPane.getChildren().add(label);
pane.getChildren().add(stackPane);
return pane;
}
}
What about declaring labels as fields of the Menu class (same with buttons)? Then you can add/remove them from the pane. I modified your code a little - it should works in this particular case:
class Menu {
private static final Pane paneMenu = new Pane();
private static final Button btn1 = new Button("Menu 1");
private static final Button btn2 = new Button("Menu 2");
private static final Label firstLabel = new Label("You clicked Menu1");
private static final Label secondLabel = new Label("You clicked Menu2");
static Pane getPaneMenu() {
ObservableList<Node> mainPaneChildren = Main.pane.getChildren();
btn1.relocate(100, 10);
btn1.setPrefSize(100, 20);
btn1.setOnAction(e -> {
firstLabel.relocate(200, 40);
if (mainPaneChildren.contains(secondLabel)) {
mainPaneChildren.remove(secondLabel);
}
if (!mainPaneChildren.contains(firstLabel)) {
mainPaneChildren.add(firstLabel);
}
});
btn2.relocate(300, 10);
btn2.setPrefSize(100, 20);
btn2.setOnAction(e -> {
secondLabel.relocate(200, 40);
if (mainPaneChildren.contains(firstLabel)) {
mainPaneChildren.remove(firstLabel);
}
if (!mainPaneChildren.contains(secondLabel)) {
mainPaneChildren.add(secondLabel);
}
});
paneMenu.getChildren().addAll(btn1, btn2);
return paneMenu;
}
}
If there are going to be more elements than just a Label it would be better if you put them in some kind of a Pane. Than you can add/remove that Pane.
By the way - have a look at Working With Layouts in JavaFX? I don't think that using relocate() is a good practice.