Im working with javaFX and i would like to add a Rectangle object to the game root from a seperate player class. Therfor i created a getRoot function in my Game, and tried adding the rectangle from my player class. Although for some reason it just dosent work.
public class Test {
public void run() {
System.out.println("loaded");
GameScene gameScene = new GameScene();
gameScene.getRoot().getChildren().add(new Sprite(50,50,50,50, Color.GREEN,"TEST", 1, 0));
}
}
public class GameScene {
private Group root = new Group();
public Scene getScene(){
Scene scene = new Scene(root, Color.BLACK);
Test test = new Test();
test.run();
return scene;
}
public Group getRoot() {
return root;
}
}
The gameScene is still just empty when i run it, although the "loaded" string is printed to the console.
Try this for modifying root by another class:
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class GameScene extends Application {
private Group root;
#Override
public void start(Stage stage) {
root = new Group();
stage.setScene(getScene());
stage.show();
}
public Scene getScene(){
Scene scene = new Scene(root);
Test test = new Test();
test.run(root);
return scene;
}
public static void main(String[] args) {
launch(args);
}
}
class Test {
public void run(Group root) {
root.getChildren().add(new Rectangle(50, 50, Color.BLACK));
}
}
Related
How can I make a custom Event that triggers on Stage.setScene()?
In my code, the button switches the Scenes and that works fine. However, I would like to extend the Stage to have an additional Event that is triggered when a button or possibly any other Element triggers a setScene.
Example:
package sample;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage stage) {
Group g1 = new Group();
Button b1 = new Button("2");
g1.getChildren().setAll(b1);
Scene scene1 = new Scene(g1, 50, 50);
Group g2 = new Group();
Button b2 = new Button("1");
g2.getChildren().setAll(b2);
Scene scene2 = new Scene(g2, 50, 50);
stage.setScene(scene1);
stage.setTitle("JavaFX Application Life Cycle");
b1.setOnAction(actionEvent -> {
System.out.println("1");
stage.setScene(scene2);
});
b2.setOnAction(actionEvent -> {
System.out.println("2");
stage.setScene(scene1);
});
stage.show();
}
}
You can add a ChangeListener<Scene> to your Stage like this:
stage.sceneProperty().addListener((observable, oldScene, newScene) -> {
System.out.println("New scene: " + newScene);
System.out.println("Old scene: " + oldScene);
});
I believe using a listener, as shown in the answer by #M.S., is probably the best and simplest way to react to scene changes. However, you ask about how to make a "custom event" that you can fire when the scene changes; by "event" I assume you mean a subclass of javafx.event.Event. So while I recommend sticking with a simple listener, here's an example of a custom event.
First, you need a custom event class:
import javafx.event.Event;
import javafx.event.EventType;
import javafx.scene.Scene;
import javafx.stage.Window;
public class SceneChangedEvent extends Event {
public static final EventType<SceneChangedEvent> SCENE_CHANGED =
new EventType<>(Event.ANY, "SCENE_CHANGED");
public static final EventType<SceneChangedEvent> ANY = SCENE_CHANGED;
private transient Window window;
private transient Scene oldScene;
private transient Scene newScene;
public SceneChangedEvent(Window window, Scene oldScene, Scene newScene) {
super(window, window, SCENE_CHANGED);
this.window = window;
this.oldScene = oldScene;
this.newScene = newScene;
}
public Window getWindow() {
return window;
}
public Scene getOldScene() {
return oldScene;
}
public Scene getNewScene() {
return newScene;
}
}
I'm not sure what information you want to carry with the event so I just added the source Window as well as the old and new Scenes. If you're wondering about the ANY = SCENE_CHANGED, I'm just following the pattern used by javafx.event.ActionEvent (which also only has a single event-type).
Then you simply need to fire the event when the scene changes. To implement this you're still going to need a change listener. As you mention wanting to extend Stage here's an example of that:
import javafx.beans.NamedArg;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.event.EventHandler;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class CustomStage extends Stage {
private final ObjectProperty<EventHandler<? super SceneChangedEvent>> onSceneChanged =
new SimpleObjectProperty<>(this, "onSceneChanged") {
#Override
protected void invalidated() {
setEventHandler(SceneChangedEvent.SCENE_CHANGED, get());
}
};
public final void setOnSceneChanged(EventHandler<? super SceneChangedEvent> handler) {
onSceneChanged.set(handler);
}
public final EventHandler<? super SceneChangedEvent> getOnSceneChanged() {
return onSceneChanged.get();
}
public final ObjectProperty<EventHandler<? super SceneChangedEvent>> onSceneChangedProperty() {
return onSceneChanged;
}
public CustomStage() {
this(StageStyle.DECORATED);
}
public CustomStage(#NamedArg(value = "style", defaultValue = "DECORATED") StageStyle style) {
super(style);
sceneProperty().addListener((obs, ov, nv) -> fireEvent(new SceneChangedEvent(this, ov, nv)));
}
}
This would let you react to the scene changing using any of the following:
CustomStage stage = new CustomStage();
// addEventFilter/addEventHandler
stage.addEventFilter(SceneChangedEvent.SCENE_CHANGED, e -> { ... });
stage.addEventHandler(SceneChangedEvent.SCENE_CHANGED, e -> { ... });
// setOnSceneChanged
stage.setOnSceneChanged(e -> { ... });
Keep in mind that the event will only target the CustomStage instance. In other words, only event handlers added to the CustomStage instance will be notified of the event. And as you can see, this is much more complicated than simply adding a change listener to the scene property of the Stage.
I am creating a JavaFX desktop app on which I am simulating some work load. I want the app to have a progress indicator that updates dynamically (with time passing at the moment) to show how the load process is progressing. This is my application class:
public class App extends Application {
#Override
public void init() throws InterruptedException{
//Simulation of time consuming code.
for(int i = 0; i<=10; i++) {
notifyPreloader(new Preloader.ProgressNotification(i/10));
System.out.println("Progress is being set by the app to: " + (i/10));
Thread.sleep(500);
}
}
#Override
public void start(Stage primaryStage) {
Parent root;
try {
root = FXMLLoader.load(getClass().getResource("/gui/fxml/App.fxml"));
Scene scene = new Scene(root, 600, 400);
scene.getStylesheets().add("/gui/style/app.css");
primaryStage.setScene(scene);
primaryStage.setTitle("Hello World!");
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
}
This is my preloader class:
public class AppPreloader extends Preloader {
private Stage preloaderStage;
private Parent root;
private Scene scene;
private ProgressIndicator progress_indicator;
#Override
public void start(Stage primaryStage) throws Exception {
this.preloaderStage = primaryStage;
this.preloaderStage.setScene(this.scene);
this.preloaderStage.show();
this.progress_indicator = (ProgressIndicator) scene.lookup("#progressIndicator");
}
#Override
public void init() throws Exception {
root = FXMLLoader.load(getClass().getResource("/gui/fxml/AppPreloader.fxml"));
Platform.runLater(new Runnable() {
#Override
public void run() {
scene = new Scene(root, 600, 400);
scene.getStylesheets().add("/gui/style/appPreloader.css");
}
});
}
#Override
public void handleProgressNotification(ProgressNotification pn) {
if(pn instanceof ProgressNotification){
progress_indicator.setProgress(pn.getProgress());
System.out.println("Progress is being set by the handle method to: " + pn.getProgress());
}
}
#Override
public void handleStateChangeNotification(StateChangeNotification evt) {
if (evt.getType() == StateChangeNotification.Type.BEFORE_START) {
preloaderStage.hide();
}
}
}
Whit the print sentences I've been able to identify two problems: First, the
handleProgressNotification method is being called twice, once to be set to 0 and other to be set to 1, before the loop of the init method of the App class starts. Who is making the call? How can I avoid it?
The second problem is that the print sentence inside the init method of the app class is always printing 0.0. How can that be possible? Is it a matter of concurrency?
In addition I need to say that I've checked both of this questions (progressbar in preloader does not update and javafx preloader not updating progress) and didn't find a solution for my problem.
Thanks a lot for your time.
First, you're not seeing the progress values you expect because you are using integer arithmetic: i and 10 are both integers, so i/10 is 0 for 0 <= i < 10 and 1 when i=10.
Second, the handleProgressNotification and handleStateChangeNotification methods are part of the lifecycle of the application that are related to loading the resources. These are really leftovers from the days when JavaFX still supported web deployments and are probably of limited use now.
To receive notifications from the application, you need to override the handleApplicationNotification(...) method instead. Here is a corrected version of the two classes (also modified to be stand-alone so they can be copied and run: please provide these kinds of examples in your questions) that works:
package application;
import javafx.application.Application;
import javafx.application.Preloader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class App extends Application {
#Override
public void init() throws InterruptedException{
//Simulation of time consuming code.
for(int i = 0; i<=10; i++) {
notifyPreloader(new Preloader.ProgressNotification(i/10.0));
System.out.println("Progress is being set by the app to: " + (i/10.0));
Thread.sleep(500);
}
notifyPreloader(new Preloader.StateChangeNotification(Preloader.StateChangeNotification.Type.BEFORE_START));
}
#Override
public void start(Stage primaryStage) {
Parent root;
root = new StackPane(new Label("Hello World"));
Scene scene = new Scene(root, 600, 400);
primaryStage.setScene(scene);
primaryStage.setTitle("Hello World!");
primaryStage.show();
}
}
package application;
import javafx.application.Preloader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class AppPreloader extends Preloader {
private Stage preloaderStage;
private Parent root;
private Scene scene;
private ProgressIndicator progress_indicator;
#Override
public void start(Stage primaryStage) throws Exception {
progress_indicator = new ProgressIndicator();
root = new StackPane(progress_indicator);
scene = new Scene(root, 600, 400);
this.preloaderStage = primaryStage;
this.preloaderStage.setScene(this.scene);
this.preloaderStage.show();
}
#Override
public void handleApplicationNotification(PreloaderNotification pn) {
if (pn instanceof ProgressNotification) {
//expect application to send us progress notifications
//with progress ranging from 0 to 1.0
double v = ((ProgressNotification) pn).getProgress();
progress_indicator.setProgress(v);
} else if (pn instanceof StateChangeNotification) {
StateChangeNotification scn = (StateChangeNotification) pn ;
if (scn.getType() == StateChangeNotification.Type.BEFORE_START) {
preloaderStage.hide();
}
}
}
}
In this answer a loader class is used to dynamically load a ComboBox while it is constructed (without post processing).
I wanted to use a similar technique to pre-process and reuse a Polygon , but a Polygon doesn't expose items property, or a similar one.
My solution was to extend Polygon and add such property:
public class DPolygon extends javafx.scene.shape.Polygon{
public ObjectProperty<ObservableList<Double>> items =
new SimpleObjectProperty<>(this, "items");
public DPolygon() {
items.addListener(new ChangeListener<ObservableList<Double>>() {
#Override
public void changed(ObservableValue<? extends ObservableList<Double>>
observable, ObservableList<Double> oldValue,
ObservableList<Double> newValue) {
addPolyPoints();
}
});
}
private void addPolyPoints() { super.getPoints().setAll(items.get());}
public ObjectProperty<ObservableList<Double>>itemsProperty() { return items; }
public final ObservableList<Double> getItems() {return items.get(); }
public final void setItems(ObservableList<Double> value) {items.set(value); }
}
Have a loader class to supply the points:
public class PointsLoader5 {
private ObservableList<Double> items;
public ObservableList<Double> getItems() {
items = FXCollections.observableArrayList(
new Double[] {100.,0.,100., 100., 0., 0.});
return items;
}
}
Use both in the FXML file:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.Double?>
<?import javafx.xml_polygon.DPolygon?>
<DPolygon fx:id="triangle" fill="GREEN" stroke="RED" strokeType="INSIDE"
items="${pointsLoader.items}" xmlns:fx="http://javafx.com/fxml/1" >
</DPolygon>
And put it all together to test:
public class DrawTrianle5 extends Application {
#Override
public void start(Stage primaryStage) throws IOException {
primaryStage.setTitle("Set points using loader");
Group group = new Group();
GridPane grid = new GridPane();
grid.setAlignment(Pos.CENTER);
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(25, 25, 25, 25));
group.getChildren().add(grid);
FXMLLoader loader = new FXMLLoader(getClass().getResource("triangle/Triangle5.fxml"));
loader.getNamespace().put("pointsLoader", new PointsLoader5());
DPolygon triangle = loader.load();
grid.add(triangle, 0, 0);
Scene scene = new Scene(group, 450, 175);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
It works with no problem.
My question is: is it the right approach, or is there a better way to dynamically loads point to a Polygon constructed byFXML, avoiding post-processing ?
The only thing preventing you doing this with the current Polygon class is that there is no suitable constructor argument with a #NamedArg annotation.
So one way to do this would be:
import java.util.List;
import javafx.beans.NamedArg;
import javafx.scene.shape.Polygon;
public class DPolygon extends Polygon {
public DPolygon(#NamedArg("points") List<Double> points) {
super(points.stream().mapToDouble(Double::doubleValue).toArray());
}
}
Then you just need
<DPolygon xmlns:fx="http://javafx.com/fxml/1" points="$points" fill="GREEN" stroke="RED" strokeType="INSIDE" />
Test:
import java.util.Arrays;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.shape.Polygon;
import javafx.stage.Stage;
public class PolygonTest extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("Polygon.fxml"));
loader.getNamespace().put("points", Arrays.asList(100.0,0.0,100.0,100.0,0.0,0.0));
Polygon poly = loader.load();
Scene scene = new Scene(new Group(poly), 450, 175);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
It feels like there should be an approach without the subclass at all, but I can't quite find one that works.
Is there a possibility to use a controller with a JavaFX GUI without using FXML.
I noticed that the FXML file contains an fx-controller attribute to bind the controller but i don't find it an easy way to work with it.
Any ideas about have an MVC arch with JavaFX without using the FXML file or JavaFX Scene Builder ?
Your question isn't particularly clear to me: you just create the classes and basically tie everything together with listeners. I don't know if this helps, but here is a simple example that just has a couple of text fields and a label displaying their sum. This is what I regard as "classical MVC": the view observes the model and updates the UI elements if the model changes. It registers handlers with the UI elements and delegates to the controller if events happen: the controller in turn processes the input (if necessary) and updates the model.
Model:
package mvcexample;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ReadOnlyIntegerWrapper;
import javafx.beans.property.SimpleIntegerProperty;
public class AdditionModel {
private final IntegerProperty x = new SimpleIntegerProperty();
private final IntegerProperty y = new SimpleIntegerProperty();
private final ReadOnlyIntegerWrapper sum = new ReadOnlyIntegerWrapper();
public AdditionModel() {
sum.bind(x.add(y));
}
public final IntegerProperty xProperty() {
return this.x;
}
public final int getX() {
return this.xProperty().get();
}
public final void setX(final int x) {
this.xProperty().set(x);
}
public final IntegerProperty yProperty() {
return this.y;
}
public final int getY() {
return this.yProperty().get();
}
public final void setY(final int y) {
this.yProperty().set(y);
}
public final javafx.beans.property.ReadOnlyIntegerProperty sumProperty() {
return this.sum.getReadOnlyProperty();
}
public final int getSum() {
return this.sumProperty().get();
}
}
Controller:
package mvcexample;
public class AdditionController {
private final AdditionModel model ;
public AdditionController(AdditionModel model) {
this.model = model ;
}
public void updateX(String x) {
model.setX(convertStringToInt(x));
}
public void updateY(String y) {
model.setY(convertStringToInt(y));
}
private int convertStringToInt(String s) {
if (s == null || s.isEmpty()) {
return 0 ;
}
if ("-".equals(s)) {
return 0 ;
}
return Integer.parseInt(s);
}
}
View:
package mvcexample;
import javafx.geometry.HPos;
import javafx.geometry.Pos;
import javafx.scene.Parent;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.control.TextFormatter.Change;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
public class AdditionView {
private GridPane view ;
private TextField xField;
private TextField yField;
private Label sumLabel;
private AdditionController controller ;
private AdditionModel model ;
public AdditionView(AdditionController controller, AdditionModel model) {
this.controller = controller ;
this.model = model ;
createAndConfigurePane();
createAndLayoutControls();
updateControllerFromListeners();
observeModelAndUpdateControls();
}
public Parent asParent() {
return view ;
}
private void observeModelAndUpdateControls() {
model.xProperty().addListener((obs, oldX, newX) ->
updateIfNeeded(newX, xField));
model.yProperty().addListener((obs, oldY, newY) ->
updateIfNeeded(newY, yField));
sumLabel.textProperty().bind(model.sumProperty().asString());
}
private void updateIfNeeded(Number value, TextField field) {
String s = value.toString() ;
if (! field.getText().equals(s)) {
field.setText(s);
}
}
private void updateControllerFromListeners() {
xField.textProperty().addListener((obs, oldText, newText) -> controller.updateX(newText));
yField.textProperty().addListener((obs, oldText, newText) -> controller.updateY(newText));
}
private void createAndLayoutControls() {
xField = new TextField();
configTextFieldForInts(xField);
yField = new TextField();
configTextFieldForInts(yField);
sumLabel = new Label();
view.addRow(0, new Label("X:"), xField);
view.addRow(1, new Label("Y:"), yField);
view.addRow(2, new Label("Sum:"), sumLabel);
}
private void createAndConfigurePane() {
view = new GridPane();
ColumnConstraints leftCol = new ColumnConstraints();
leftCol.setHalignment(HPos.RIGHT);
leftCol.setHgrow(Priority.NEVER);
ColumnConstraints rightCol = new ColumnConstraints();
rightCol.setHgrow(Priority.SOMETIMES);
view.getColumnConstraints().addAll(leftCol, rightCol);
view.setAlignment(Pos.CENTER);
view.setHgap(5);
view.setVgap(10);
}
private void configTextFieldForInts(TextField field) {
field.setTextFormatter(new TextFormatter<Integer>((Change c) -> {
if (c.getControlNewText().matches("-?\\d*")) {
return c ;
}
return null ;
}));
}
}
Application class:
package mvcexample;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class MVCExample extends Application {
#Override
public void start(Stage primaryStage) {
AdditionModel model = new AdditionModel();
AdditionController controller = new AdditionController(model);
AdditionView view = new AdditionView(controller, model);
Scene scene = new Scene(view.asParent(), 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I use JavaFX extensively and do not use FXML or scenebuilder. So I can vouch that it can be done.
Below is the auto generated code made by my IDE to get an JavaFX main class. This will be the root of your application. You will then add to it to create your application.
public class NewFXMain extends Application {
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("Hello World!");
}
});
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
For the rest of us... Here is a VERY simple example showing how to create a JavaFX form without the use of any FXML files. This example can be used within an app that is already running, so I've skipped the Main class and all that ... it's just meant to show the simplicity of JavaFX.
In a nutshell, you simply create your scene based on a container such as an AnchorPane, then you create your Stage and assign the Scene to the stage ... add your controls then show the stage
package javafx;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
public class SimpleFX {
private AnchorPane anchorPane;
private TextArea textArea () {
TextArea textArea = new TextArea();
textArea.setLayoutX(20);
textArea.setLayoutY(20);
textArea.setMaxWidth(450);
textArea.setMinHeight(380);
return textArea;
}
private TextField textField () {
TextField textField = new TextField();
textField.setLayoutX(20);
textField.setLayoutY(410);
textField.setMinWidth(450);
textField.setMinHeight(25);
return textField;
}
private Button button() {
Button button = new Button("Button");
button.setLayoutX(240);
button.setLayoutY(450);
return button;
}
private void addControls () {
anchorPane.getChildren().add(0,textArea());
anchorPane.getChildren().add(1,textField());
anchorPane.getChildren().add(2,button());
}
public void startForm () {
anchorPane = new AnchorPane();
Scene scene = new Scene(anchorPane, 500, 500);
Stage stage = new Stage();
stage.setScene(scene);
addControls();
stage.show();
}
}
I have problem with JavaFX. I created two scenes and switch button.
When I click that button I'm changing scene. But earlier i set fullscreen on true and after I pressed the button, windows taskbar shows for a moment. Is there any way to change scenes without having this taskbar visible?
There is the code:
----Main class----
import java.io.IOException;
public class Main {
public static void main(String[] args) throws InterruptedException, IOException {
DesktopApplication.launch(DesktopApplication.class);
}
}
----DesktopApplication class----
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.input.KeyCombination;
import javafx.scene.layout.HBox;
import javafx.scene.text.Text;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class DesktopApplication extends Application implements Runnable {
Scene firstScene;
Scene secondScene;
Scene scene;
public static Stage primaryStagePublic;
public DesktopApplication() {
}
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("Title");
primaryStage.initStyle(StageStyle.TRANSPARENT);
primaryStage.initStyle(StageStyle.UNDECORATED);
int width = (int) Screen.getPrimary().getBounds().getWidth();
int height = (int) Screen.getPrimary().getBounds().getHeight();
HBox mainLayout = new HBox();
mainLayout.getChildren().add(new Text("hello!"));
MyLayout myLayout = new MyLayout(this);
firstScene = new Scene(myLayout,width,height);
secondScene = new Scene(mainLayout, width, height);
scene = firstScene;
primaryStage.setScene(scene);
primaryStage.setFullScreen(true);
primaryStage.setFullScreenExitKeyCombination(KeyCombination.NO_MATCH);
primaryStage.show();
primaryStagePublic = primaryStage;
}
#Override
public void run() {
Thread thread = new Thread() {
public void run() {
launch(DesktopApplication.class);
}
};
thread.start();
while (true) {
}
}
public void swapScenes(Stage primaryStage){
primaryStage.setScene(secondScene);
primaryStage.setFullScreen(true);
primaryStage.setFullScreenExitKeyCombination(KeyCombination.NO_MATCH);
}
}
----MyLayout class----
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
public class MyLayout extends HBox{
private DesktopApplication desktopApplication;
public MyLayout(DesktopApplication desktopApplication) {
this.desktopApplication = desktopApplication;
init();
}
private void init(){
this.setStyle("-fx-background-color: #f8ff7d;");
Label text = new Label("testing");
Button button = new Button("Button");
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
desktopApplication.swapScenes(DesktopApplication.primaryStagePublic);
}
});
this.getChildren().addAll(text, button);
}
}
I had a similar issue and solved it like #James_D suggested: Do not replace the scene as a whole, but only the root element:
public void swapScenes(Parent newContent){
stage.getScene().setRoot(newContent);
}
This requires changing the rest of the initialisation code a bit:
public class DesktopApplication extends Application implements Runnable {
Parent myLayout;
Parent mainLayout;
Scene scene;
public static Stage stage; // if possible make this private and non static
public DesktopApplication() {
}
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("Title");
primaryStage.initStyle(StageStyle.TRANSPARENT);
primaryStage.initStyle(StageStyle.UNDECORATED);
int width = (int) Screen.getPrimary().getBounds().getWidth();
int height = (int) Screen.getPrimary().getBounds().getHeight();
mainLayout = new HBox();
mainLayout.getChildren().add(new Text("hello!"));
myLayout = new MyLayout(this);
scene = new Scene(myLayout,width,height);
primaryStage.setScene(scene);
primaryStage.setFullScreen(true);
primaryStage.setFullScreenExitKeyCombination(KeyCombination.NO_MATCH);
primaryStage.show();
primaryStagePublic = primaryStage;
}
...
I personally solved it (After a few months of looking) by, instead of doing primaryStage.setFullScreen(true), which glitches or something, doing primaryStage.setMaximized(true) along with primaryStage.setWidth(var1) and primaryStage.setHeight(var2). My hypothesis on why setFullScreen deosn't work is a bug in full screen exclusive mode. Or, there just isn't enough permissions or something and it bugs out.