Change Scene without resize window in JavaFX - java

I'm trying to change Scenes on JavaFX, but without change the window size. However, when I set stage.setScene(scene2); the window size decreases, and I want to keep the both Scenes maximized. I've tried to stage.setMaximized(true) after the stage.setScene(scene2); but the result is the same.
How can I fix it?
My code:
package controller;
import java.io.IOException;
import javafx.animation.FadeTransition;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.util.Duration;
public class App extends Application {
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("../view/fxml/Loading.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.setTitle("Project");
stage.setMaximized(true);
stage.show();
FadeTransition fadeIn = new FadeTransition(Duration.seconds(1), root);
fadeIn.setFromValue(0);
fadeIn.setToValue(1);
FadeTransition fadeOut = new FadeTransition(Duration.seconds(1), root);
fadeOut.setFromValue(1);
fadeOut.setToValue(0);
fadeIn.play();
fadeIn.setOnFinished((e) -> {
fadeOut.play();
});
fadeOut.setOnFinished((e) -> {
try {
Parent root2 = FXMLLoader.load(getClass().getResource("../view/fxml/Welcome.fxml"));
Scene scene2 = new Scene(root2);
stage.setScene(scene2);
} catch (IOException e1) {
e1.printStackTrace();
}
});
}
public static void main(String[] args) {
launch(args);
}
}
When I compile:
Then the fadeOut/fadeIn occurs and (It is here that I want to keep maximized):

It's probably better here just to replace the root of the existing scene, than to create a new scene:
fadeOut.setOnFinished((e) -> {
try {
Parent root2 = FXMLLoader.load(getClass().getResource("../view/fxml/Welcome.fxml"));
scene.setRoot(root2);
} catch (IOException e1) {
e1.printStackTrace();
}
});
If you really do need to replace the scene, for some reason, you can set the new scene's size to the same as the existing scene:
fadeOut.setOnFinished((e) -> {
try {
Parent root2 = FXMLLoader.load(getClass().getResource("../view/fxml/Welcome.fxml"));
Scene scene2 = new Scene(root2, scene.getWidth(), scene.getHeight());
stage.setScene(scene2);
} catch (IOException e1) {
e1.printStackTrace();
}
});

I have been dealing with an issue similar to yours for the last 4-5 hours and given #James_D's answer I saw the way to simply fix the resizing issue with scene changes. This answer still directly goes off of #James_D, but I did not feel there was a clear explanation as to what was going on, so more than anything I am posting this to help anybody who runs across this thread understand why the second solution #James_D provided (i.e. Changing scenes, not the root of the scene) works.
Note: I did run across a few answers stating that setting a new root for the scene may be a better option than changing the whole scene, but that did not work in my case, so changing scenes was the best option for me.
Anyways, I am pretty sure the reason the scene changes sizes when swapped is because you do not set the scene width and height attributes on the scene object explicitly. Not specifically setting the size appears to make the scene adjust to the bounds of the objects it contains automatically when the new scene is loaded to the stage.
Basically, to fix the problem all you need to do is set the width and height attributes when you create your scene, like below.
Before
Scene scene2 = new Scene(root2);
After
Scene scene2 = new Scene(root2, 900, 600);//or whatever size you want
stage.setScene(scene2);//now we are set if initial scene is 900w X 600h, scene size will stay the same

Related

NotificationPane doesn't show up in Scene

I would like to display a NotificationPane after certain user actions. My application has multiple scenes and the NotificationPane should be showed up in the currently active scene.
The whole thing works with Notification, it pops up when I need it.
But I can't figure out how to make this work for NotificationPane.
Steps I made so far:
I tryed to put NotificationPane directly to my scene and call
show() - it works.
Now the Idea is to get the current pane by calling
stage.getScene().getRoot(), wrap it to NotificationPane and then call
show() - it doesn't work and I have no idea why.
((BorderPane) pane).setCenter(new Label("TEST")); this line is replacing buttons with text label, so stage.getScene().getRoot() is returning the right object
I made a simple program to test the behaviour. One button to call NotificationPane.
Any suggestions?
Here is my test program:
package application;
import org.controlsfx.control.NotificationPane;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Button notificationPaneButton = new Button("NotificationPane");
notificationPaneButton.setOnAction(e -> showNotificationPane(primaryStage, "Notification text"));
VBox vbox = new VBox(5);
vbox.setAlignment(Pos.CENTER);
vbox.getChildren().addAll(notificationPaneButton);
BorderPane borderPane = new BorderPane();
borderPane.setCenter(vbox);
primaryStage.setTitle("Notifications test");
primaryStage.setScene(new Scene(borderPane, 300, 200));
primaryStage.show();
}
public void showNotificationPane(Stage stage, String message) {
Parent pane = stage.getScene().getRoot();
// ((BorderPane) pane).setCenter(new Label("TEST"));
NotificationPane notificationPane = new NotificationPane(pane);
notificationPane.setText(message);
if (notificationPane.showingProperty().get()) {
notificationPane.hide();
System.err.println("hide");
} else {
notificationPane.show();
System.err.println("show");
}
}
}
Ok, I see the problem now. Wrapping current pane is not enough, I also have to add the NotificationPane to the scene. Right?
Anyway my current solution is following:
get current scene
get current pane
wrap pane
replace current scene with the new one
To avoid wrapping NotificationPane multiple times I check if current pane is already a NotificationPane and then call show().
public void showNotificationPane(Stage stage) {
Scene scene = stage.getScene();
Parent pane = scene.getRoot();
if (!(pane instanceof NotificationPane)){
NotificationPane notificationPane = new NotificationPane(pane);
scene = new Scene(notificationPane, scene.getWidth(), scene.getHeight());
stage.setScene(scene);
notificationPane.show();
} else {
((NotificationPane)pane).show();
}
}

Trouble having javafx GUI background stay centered when resizing frame

So this is the code i have that launches the GUI i read on a previous post that it maybe has to do with the fact that my root is of type Group but i wasn't able to figure out how to implement any other way. The content inside the GUI gets eaten up when i try to resize the main frame manually.I want it to stay center and resize with the frame.
package view;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Stage;
import singleton.MainModel;
public class MainView extends Application {
#Override
public void start(Stage primaryStage) {
// Initialize mainController.
MainController mainController = new MainController();
// Add the controller to the singleton.
MainModel.getModel().getMainData().setMainController(mainController);
// Initialize display components.
Group root = new Group();
Scene scene = new Scene(root, 1280, 720);
// Add mainController.
root.getChildren().addAll(mainController);
// Pin the root to scene and display it.
primaryStage.setScene(scene);
primaryStage.show();
// Properly terminate the application if the user presses the "X" window button.
primaryStage.setOnCloseRequest(event -> {
mainController.closeApplication();
stop();
});
// Set the title and make the application a fixed size.
primaryStage.setTitle("Visual Earth Modelling System");
primaryStage.setResizable(true);
primaryStage.sizeToScene();
// Add the stage to the singleton.
MainModel.getModel().getMainData().setMainStage(primaryStage);
// Go to the first screen.
mainController.goToLoginScreen();
}
/**
* To destroy resources upon application close. Should be called in all instances of a properly closed JavaFX application.
*/
#Override
public void stop() {
if (MainModel.getModel().getNetworkData().isHandlerSet())
MainModel.getModel().getNetworkData().closeHandler();
}
/**
* This method is actually not used in a correctly deployed JavaFX application. Instead, the start method above is called. This main serves as a fallback in case of improper configuration.
*/
public static void main(String[] args) {
launch(args);
}
}
Replace Group with StackPane. StackPane by default sets alignment of children to center. Just to be sure that alignment is correct you can set it explicity by StackPane.setAlignment(Pos value).
StackPane root = new StackPane();
root.setAlignment(Pos.CENTER);
root.getChildren().add(mainController);
Scene scene = new Scene(root, 1280, 720);

Invisible Password

In one of my recent projects I want to implement a hidden page. I want to be able to reach it by just typing the password without anything showing on the screen. I tried to just set a PasswordField as visible(false). However that didn't work. Also I would like the hidden page to pop up without having to press enter after typing the password. Is there a way for a simple javafx application to behave like that?
You could use a KeyListener. Though you need to press the screen once for the keypresses to register. And make sure to add the keylistener to the JFrame, I always forget that. This will look for keys, but requires a window to be shown, this can be empty though.
If you don't want a window at all, you can use the external library jnativehook it looks for keypresses globally.
You can add an event filter to the scene that keeps track of what has been typed.
Here is a simple example (type "secret" with the main window focussed to show the popup window; press enter if you mistype to clear the hidden text):
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.Tooltip;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class OpenSecretWindow extends Application {
#Override
public void start(Stage primaryStage) {
Label label = new Label("Type the secret password\nto open the secret window");
label.setTooltip(new Tooltip("The secret password is \"secret\""));
StackPane root = new StackPane(label);
Scene scene = new Scene(root, 400, 400);
StringBuilder typedText = new StringBuilder();
scene.addEventFilter(KeyEvent.KEY_TYPED, e -> {
switch(e.getCharacter()) {
case "\n":
case "\r":
typedText.delete(0, typedText.length());
break ;
default:
typedText.append(e.getCharacter());
}
if ("secret".equals(typedText.toString())) {
openSecretWindow(primaryStage);
typedText.delete(0, typedText.length());
}
});
// handle backspace and delete:
scene.addEventFilter(KeyEvent.KEY_RELEASED, e -> {
if (e.getCode() == KeyCode.BACK_SPACE || e.getCode() == KeyCode.DELETE) {
if (typedText.length() > 0) {
typedText.delete(typedText.length()-1, typedText.length());
}
}
});
primaryStage.setScene(scene);
primaryStage.show();
}
private void openSecretWindow(Stage owner) {
Stage stage = new Stage();
StackPane root = new StackPane(new Label("You have found\nthe secret window!"));
Scene scene = new Scene(root, 300, 180);
stage.setScene(scene);
stage.initOwner(owner);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I believe you could set the foreground of the JPasswordField to be the same as the background colour but don't quote me on that. Something like:
JPasswordField.SetForeground(Color.RED);

How to create a new scene without errors?

I'm relatively new to Java and I'm having difficulty with running a program. Now, as a heads up, this is a homework assignment. The problem is to create a program with the output as "Welcome to Java" in a circle.
Here is my code thus far:
import java.awt.Color;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.Parent;
import javafx.scene.layout.Pane;
import javafx.scene.text.*;
import javafx.stage.Stage;
public class Characters extends Application {
public void start(Stage stage) {
Pane canvas = new Pane();
canvas.setStyle("-fx-background-color: black;");
canvas.setPrefSize(200, 200); // set size of pane
Font f = Font.font("Times New Roman", FontWeight.BOLD, 35);
String s = "Welcome to Java";
String c;
double d = 25.0, x = 10.0, y = 20.0;
for (int i = 0; i < s.length(); i++) {
c = "" + s.charAt(i);
Text t = new Text(x, y, c);
t.setFont(f);
t.setRotate(d);
d++;
x++;
y++;
canvas.getChildren().add(t);
}
Scene scene = new Scene(root,500, 500, Color.BLACK);
stage.setTitle("Characters around a circle");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I keep receiving an error at Scene scene = new Scene(root,500, 500, Color.BLACK); and I haven't been able to find a solution. Any help is appreciated. Thanks.
root is not declared anywhere in your program. Try adding canvas to a layout and add the layout to the scene.
BorderPane rootLayout = new BorderPane();
rootLayout.getChildren().add(canvas);
Scene scene = new Scene(rootLayout, 500,500);
It doesn't look like root (the first parameter in your constructor) is defined anywhere in the scope.
In the docs they do this:
Group root = new Group();
Scene s = new Scene(root, 300, 300, Color.BLACK);
But I'm not sure if you want to put in the canvas somewhere.
Something to note (copy-pasted from the docs):
The application must specify the root Node for the scene graph by setting the root property. If a Group is used as the root, the contents of the scene graph will be clipped by the scene's width and height and changes to the scene's size (if user resizes the stage) will not alter the layout of the scene graph. If a resizable node (layout Region or Control is set as the root, then the root's size will track the scene's size, causing the contents to be relayed out as necessary.
Basically, if you want the components to be forced within the Scene, it looks like you want a Group. If you want the components to change the size of the Scene then use a resizable node (e.g. Region or Control).

How do you add to an existing scene in javafx?

I would like to add more javafx objects to my scene but i am not sure how. I have tried looking it up but i could not find anything.
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
public void start(Stage primaryStage) {
try {
Parent root = FXMLLoader.load(getClass().getResource("/fxml/Main.fxml"));
Scene scene = new Scene(root,600,400);
// how would i add something here or further on?
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setResizable(false);
primaryStage.setScene(scene);
primaryStage.setTitle("Test");
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
For example how would i add a polygon to this?
You don't add them to scene, but to root, the Parent node of the scene. You have to change Parent to whatever type of node you're using in the FXML file. The default in netbeans is AnchorPane so that's what I used.
public void start(Stage primaryStage) throws Exception {
try {
AnchorPane root = FXMLLoader.load(getClass().getResource("fxml/Main.fxml"));
Scene scene = new Scene(root, 600, 400);
//how would i add something here or further on?
root.getChildren().add(new Polygon(10,20,30,10,20,30));
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setResizable(false);
primaryStage.setScene(scene);
primaryStage.setTitle("Test");
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
// don't leave me hanging bro!
Platform.exit();
}
}
I would recommend a different approach entirely. If you are using the NetBeans IDE, you can download a tool called SceneBuilder. This application lets you build and edit simple or complex JavaFX applications.
As your application becomes more and more complex, it makes more sense to use a tool like SceneBuilder. I used SceneBuilder to create a fairly complex client GUI in less than an hour. Life in JavaFX is easier with NetBeans and SceneBuilder (and I'm a guy who prefers Eclipse!)

Categories