I am working a calculator, and I want have a log stage opened on the side, now my problem is that I need to update the stage, I figured the easiest way would be to just check if the stage is showing, and if it is then hide it, that just doesn't seem to be working, here is my code:
public void start(Stage logStage) {
boolean open = logStage.isShowing();
System.out.println(open);
if(open == true){
logStage.hide();
System.out.println("Should be hidden now!");
}
logStage.setTitle("Log");
GridPane grid = new GridPane();
grid.setAlignment(Pos.TOP_CENTER);
grid.setHgap(5);
grid.setVgap(5);
TextArea logText = new TextArea();
for (String log : Logger.fraLog()) {
logText.setText(logText.getText() + log);
}
grid.add(logText, 0, 0, 10, 10);
Scene scene = new Scene(grid);
logStage.setHeight(210);
logStage.setWidth(300);
logStage.setX(1135);
logStage.setY(350);
logStage.setScene(scene);
logStage.setResizable(false);
logStage.setAlwaysOnTop(true);
logStage.show();
}
And after running this method 3 times my output is:
false
false
false
The way I call the method is:
Logger logger = new Logger();
logger.start(new Stage());
Any help is appreciated
A new Stage is hidden until you show it. Since you're creating new Stages every time, none of them is shown when you read the showing property.
Furthermore since you're calling show() for the Stage anyways, you undo any effects of the hide() call.
Probably you should reuse the same Stage/scene graph. Furthermore it's unclear what effect you expect from the hide() call since you're showing the window again; simply replacing the content without hiding the Stage would have the same effect.
If you need to update the TextArea from another class, then pass a reference to it, e.g. in the constructor of the other class. This might look like:
a) You class:
public class MyClass {
private TextArea textarea;
public MyClass(TextArea t){
this.textarea = t;
}
public void someMethodThatNeedsToLog(){
this.textarea.appendText("the next log message ...");
}
}
b) Your initialization code:
public void start(Stage logStage) {
// ...
TextArea logText = new TextArea();
MyClass instance = new MyClass(logText);
// ...
logStage.show();
Hope that helps ...
BTW: This is not a matter of JavaFX or UI Programming. You might want to have a look on principles of object oriented programming like "inversion of control", "dependency injection/constructor injection".
Related
I have 2 fxml files:
Layout (header, menubars and content)
Anchorpane (it's supposed to be placed inside the content from the other fxml file)
I would like to know how can I load the second file inside the content space from the "Master" scene. And is that a good thing to do working in javaFX or is it better to load a new scene?
I'm trying to do something like this, but it doesn't work:
#FXML
private AnchorPane content;
#FXML
private void handleButtonAction(ActionEvent event) {
content = (AnchorPane) FXMLLoader.load("vista2.fxml");
}
Thanks for the help.
Why your code does not work
The loader creates a new AnchorPane, but you never add the new pane to a parent in the scene graph.
Quick Fix
Instead of:
content = (AnchorPane) FXMLLoader.load("vista2.fxml");
Write:
content.getChildren().setAll(FXMLLoader.load("vista2.fxml"));
Replacing the content children with your new vista. The content itself remains in the scene graph, so when you set it's children, you are also attaching them to the scene graph at the same time.
You might need to play around with layout (e.g. work with auto resizing layouts like StackPanes rather than AnchorPanes) to get the exact behaviour you want.
Rather than just adopting the quick fix, I would advise reviewing the simple framework linked below as that might provide you with a more general purpose mechanism to get the behaviour you want.
Reference FXML Navigation Framework
I created a small framework for swapping fxml controlled content panes in and out of a portion of the main scene.
The mechanism of the framework is the same as suggested in kithril's answer.
A main pane for the outer fxml acts as a holder for child panes.
The main controller for the outer fxml supplies a public method that can be used to swap the child panes.
A convenience navigator class is statically initialized with the main controller for the outer layout.
The navigator provides a public static method to load a new child pane into the main container (by invoking a method on the main controller).
Child panes are generated in the navigator by their respective fxml loaders.
Why a Framework
The framework seems like overkill for answering your question, and perhaps it is. However, I have found that the two most asked topic related to FXML are:
Navigation between panes generated by FXML (this question).
How to pass data between FXML controllers.
So I felt that a small demo framework was warranted for this case.
Sample Framework Output
The first screen shows the application layout displaying the first vista. The contents are a header which is defined in the main application layout and an aliceblue colored interchangable child content pane.
In the next screen, the user has navigated to the second vista, which retains the constant header from the main layout and replaces the original child pane with a new coral colored child content pane. The new child has been loaded from a new fxml file.
Looking for Something More Substantial?
A lightweight framework which is more extensive and better supported than the sample framework from this question is afterburner.fx.
Looking for Something Even Simpler?
Just swap out the scene root: Changing Scenes in JavaFX.
Other Options?
Animated Transitions and others: Switch between panes in JavaFX
Im not sure about how effective this is, but seems to be just fine and what's more, much simpler to methods above.
https://www.youtube.com/watch?v=LDVztNtJWOo
As far as I understood what is happening here is this(its really similiar to what is happening in Start() method in application class) :
private void buttonGoToWindow3Action(ActionEvent event) throws IOException{
Parent window3; //we need to load the layout that we want to swap
window3 = (StackPane)FXMLLoader.load(getClass().getResource("/ScenePackage/FXMLWindow3"));
Scene newScene; //then we create a new scene with our new layout
newScene = new Scene(window3);
Stage mainWindow; //Here is the magic. We get the reference to main Stage.
mainWindow = (Stage) ((Node)event.getSource()).getScene().getWindow();
mainWindow.setScene(newScene); //here we simply set the new scene
}
However Im not a java expert and quite new to programing so it would be good if someone experienced would evaluate it.
EDIT:
Ive found even simpler method;
Go to MainApplication class and make static Stage parentWindow.
public static Stage parentWindow;
#Override
public void start(Stage stage) throws Exception {
parentWindow = stage;
Parent root = FXMLLoader.load(getClass().getResource("/ScenePackage/FXMLMainScene.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
Now you get acces to your main Stage so anywhere in a program you can do something like that to change the scene:
Parent window1;
window1 = FXMLLoader.load(getClass().getResource("/ScenePackage/FXMLWindow1.fxml"));
//Scene newSceneWindow1 = new Scene(window1);
Stage mainStage;
//mainStage = (Stage) ((Node)event.getSource()).getScene().getWindow();
mainStage = MainApplication.parentWindow;
mainStage.getScene().setRoot(newSceneWindow1); //we dont need to change whole sceene, only set new root.
Others may have a better solution, but my solution has been to have a simple container like VBox in the outer fxml, then load the new content and add it as a child of the container. If you're only loading one or two forms, this might be the way go to go. However, for a more complete framework, I found this blog post helpful: https://blogs.oracle.com/acaicedo/entry/managing_multiple_screens_in_javafx1 She has source code for her framework which includes fancy transitions. Although it's meant to manage top level scenes, I found it easy to adapt for managing inner content regions too.
My example of the mask.
Using:
Main.getNavigation().load(View2.URL_FXML).Show();
Main.getNavigation().GoBack();
In this case, I recommend you to use custom component instead. First create a custom component for your content:
class Content2 extends AnchorPane {
Content() {
FXMLLoader loader = new FXMLLoader(getClass().getResource("vista2.fxml");
loader.setRoot(this);
loader.setController(this);
loader.load();
}
}
Replace the AnchorPane markup in the root of your vista2.fxml file with fx:root:
<fx:root type="javafx.scene.layout.AnchorPane" xmlns:fx="http://javafx.com/fxml">
...
</fx:root>
Then, you can do this simply by using custom event binding and arrow function. Add event handler property to your Content class:
private final ObjectProperty<EventHandler<ActionEvent>> propertyOnPreviousButtonClick = new SimpleObjectProperty<EventHandler<ActionEvent>>();
#FXML
private void onPreviousButtonClick(ActionEvent event) {
propertyOnPreviousButtonClick.get().handle(event)
}
public void setOnPreviousButtonClick(EventHandler<ActionEvent> handler) {
propertyOnPreviousButtonClick.set(handler);
}
Finally, bind your custom event handler in your java code or fxml:
#FXML
onNextButtonClick() {
Content2 content2 = new Content2();
content2.setOnPreviousButtonClick((event) -> {
Content1 content1 = new Content1();
layout.getChildren().clear();
layout.getChildren().add(content1);
});
layout.getChildren().clear();
layout.getChildren().add(content2);
}
If you don't want to add content dynamically, just setVisible() to true or false
Got stuck up in this too
Tried out most of the answers, wasn't what I wanted so I just used the ideals given to do this:
public class Main extends Application {
public static Stage homeStage;
#Override
public void start(Stage primaryStage) throws Exception{
homeStage = primaryStage;
Parent root = FXMLLoader.load(getClass().getResource("mainView.fxml"));
root.getStylesheets().add(getClass().getResource("stylesheet/custom.css").toExternalForm());
homeStage.setTitle("Classification of Living Organisms");
homeStage.setScene(new Scene(root, 600, 500));
homeStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
this is my main class. Main.java with the landing window/page mainView.fxml.
Used a little of #Tomasz idea, although confused me a lil before I did this in my mainController.java class:
public void gotoSubMenu(Event event) {
Parent window1;
try {
window1 = FXMLLoader.load(getClass().getResource("src/displayView.fxml"));
Stage window1Stage;
Scene window1Scene = new Scene(window1, 600, 500);
window1Stage = Main.homeStage;
window1Stage.setScene(window1Scene);
} catch (IOException e) {
e.printStackTrace();
}
}
created a new Parent window called 'window1' that loaded the second fxml file called 'displayView.fxml' in the src directory.
created an object of the main view stage and set the scene to the newly created scene whose root is window1.
Hope this helps the ones coming into #JavaFX now.
If you're looking for a way to make the button call the new fxml file, this worked for me.
#FXML
private void mainBClicked(ActionEvent event) throws IOException {
Stage stage;
Parent root;
stage=(Stage) ((Button)(event.getSource())).getScene().getWindow();
root = FXMLLoader.load(getClass().getResource("MainMenu.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
Keeping the same scene container but changing the view inside the scene container...
Say the scene container you want to pass a new view and controller into is a GridPane layout named sceneContainer.
Establish a FXMLLoader object of the new view.
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("Notifications.fxml"));
Create a matching container and loading the contents of the new view into it.
GridPane yourNewView = fxmlLoader.load();
set the new view to the sceneContainer. (setAll clears all children first)
sceneContainer.getChildren().setAll(yourNewView);
Get the controller object for the new view and call your method that starts the class logic
Notifications notifications = fxmlLoader.getController();
notifications.passUserName(userName);
A full example would look like this :
#FXML
public void setNotificationsViewToScene() {
try {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("Notifications.fxml"));
GridPane yourNewView = fxmlLoader.load();
sceneContainer.getChildren().setAll(yourNewView);
Notifications notifications = fxmlLoader.getController();
notifications.passUserName(userName);
} catch (IOException e) {
e.printStackTrace();
}
}
Wow, >8 years old with lots of complicated answers and no mention of the >10 year old solution.
There is a dedicated fx:include element for nesting FXML files. It's not in the SceneBuilder library, so you need to write it manually. SceneBuilder can load and display nested FXML just fine though.
<AnchorPane fx:id="content">
<children>
<fx:include fx:id="nestedVista" source="vista2.fxml" />
</children>
</AnchorPane>
You can reference the nested Pane and controller using the fx:id and fx:id+"Controller"
#FXML
Pane nestedVista;
#FXML
VistaController nestedVistaController;
Documentation link: https://docs.oracle.com/javafx/2/api/javafx/fxml/doc-files/introduction_to_fxml.html#nested_controllers
enter image description hereI made a GUI on JavaFX that's for creating timetables. when I open the app I can add plans (Buttons) to day columns (VBox). ofc the changes aren't saved after I close the app: the next time I open it the table is empty.
my question is how can I make it save nodes that the user creates so the next time i open the app they're there?
this is the exact part where the nodes get added for what it's worth:
void ask_add_plan(ActionEvent event)
{
Button b = (Button) event.getSource();
VBox v = (VBox) b.getParent();
AnchorPane pop_up = new AnchorPane(); //this pop up is to specify things about the plan
//but i removed unnecessary code for simplicity
VBox pop_up_v = new VBox();
Button add = new Button("Add");
add.setOnAction(e->{
Button plan = new Button(what_to_do.getText());
v.getChildren().add(plan);
container.getChildren().remove(pop_up); //container is the anchor pane everything's in
});
pop_up_v.getChildren().add(add);
pop_up.getChildren().add(pop_up_v);
container.getChildren().add(pop_up); //container is the anchor pane everything's in
}
The JavaFX nodes are just the presentation of your data. They are not meant to be saved. Store the actual data itself in a private field in your class.
In your Application.stop method, write the data to a file.
In your Application.start method, read that file and use it to rebuild JavaFX nodes.
private static final Path PLANS_FILE =
Path.of(System.getProperty("user.home"), "plans.txt");
private final List<String> plans = new ArrayList<>();
void ask_add_plan(ActionEvent event) {
// ...
add.setOnAction(e -> {
String planText = what_to_do.getText();
plans.add(planText);
Button plan = new Button(planText);
v.getChildren().add(plan);
container.getChildren().remove(pop_up);
});
}
#Override
public void start(Stage primaryStage)
throws Exception {
// ...
if (Files.exists(PLANS_FILE)) {
plans.addAll(Files.readAllLines(PLANS_FILE));
// Add UI elements for each stored plan.
for (String planText : plans) {
Button planButton = new Button(planText);
v.getChildren().add(planButton);
container.getChildren().remove(pop_up);
}
}
}
#Override
public void stop()
throws IOException {
Files.write(PLANS_FILE, plans);
}
The above is just a simplified example. The file doesn’t have to store just strings.
I get the impression that the data created in the application is more complex than just plan strings. For complex data, XML may be more a suitable file format. Or you can use Java serialization. Or, you can invent your own file format.
You also don’t have to read from and write to a file. You can use a database, if you’re comfortable with database programming. Or you can use Preferences (though Preferences are not well suited to storing lists of complex data).
You should use MVP (model-view-presenter) pattern. Saving data in UI layer is not a good practice. Create a model with data and then serialize it.
So I am using JavaFX and want to set up an experiment where I have to play many videos and interact with the user. For this setup I would like to write a function that receives a filename and plays the video with that name. But it should only return when the video is over, and not before. So something like this:
private void play(URI name){
MediaPlayer mediaPlayer = new MediaPlayer(new Media(name));
mediaPlayer.setAutoPlay(true);
view.setMediaPlayer(mediaPlayer);
}
However this function returns before the video is over, making the execution of the program a complete mess. I know MediaPlayer has a setOnEndOfMedia(Runnable) function but I am not sure how I could potentially use that to achieve my goal.
This is the code I have at the moment. But it does not work. The problem being that both videos are played at the same time, likely because the play function returns as soon as it has started the video. I have also tried just adding a while(true) loop at the end of the play function so that it gets stuck there until the return; in .setOnEndOfMedia is callled, that prevented any video's from being played at all.
CODE:
static MediaView view;
#Override
public void start(Stage stage) throws IOException, URISyntaxException, InterruptedException {
// Create full screen scene
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
stage.setFullScreen(true);
Scene scene = new Scene(new Group(), dim.getWidth(), dim.getHeight());
stage.setScene(scene);
stage.show();
// Create the view and add it to the Scene.
view = new MediaView();
((Group)scene.getRoot()).getChildren().add(view);
play("videol");
play("video2");
}
private static void play(String name) throws InterruptedException {
Media media = new Media(uri(name));
MediaPlayer player = new MediaPlayer(media);
player.setOnReady(() -> {
player.play();
player.setOnEndOfMedia(() -> {
player.dispose();
return;
});
});
view.setMediaPlayer(player);
}
private static String uri(String name) {
return Paths.get("clips/" + name + ".mp4").toUri().toString();
}
public static void main(String[] args) {
launch(args);
}
Keeping my original stuff since it is useful for others.
Anyways the problem with your code is that you are calling the method twice, right after itself. You need to call the method from within the EndOfMedia part. If you wish you can create a list and for the parameter when using recursion you can use list.poll() to take and remove the first string in the list. Then when the list is null you can just return to the interface. Just some ideas.
What I would suggest you use is to create a method for playing your music which takes a String as a parameter, this string would be then used to find its equivalent in a hashmap, say HashMap<String, MediaPlayer>. Then to the part where you actually need help. I suggest you write something like this:
mediaPlayer.setOnReady(() -> {
mediaPlayer.play();
mediaPlayer.setOnEndOfMedia(() -> {
mediaPlayer.dispose();
MusicPlayer();
});
});
Basically, you are playing the media when ready and then at the end disposing of it and calling the method again and in where my code uses recursion and calls the method MusicPlayer you can instead 'return'. Sorry if this is poorly explained, if you need further help just comment below.
I have written a piece of code for downloading a file from internet (in background service) and showing the progress of download in a popup stage. The code compiles successfully and there is no runtime error. However no download takes place and progress indicator remains indeterminate.
The code is tailored for illustrating my point. Please have a look at it and let me understand where I have gone wrong.
Thanks!
public class ExampleService extends Application {
URL url;
Stage stage;
public void start(Stage stage)
{
this.stage = stage;
stage.setTitle("Hello World!");
stage.setScene(new Scene(new StackPane(addButton()), 400, 200));
stage.show();
}
private Button addButton()
{
Button downloadButton = new Button("Download");
downloadButton.setOnAction(new EventHandler<ActionEvent>()
{
public void handle(ActionEvent e)
{
FileChooser fileSaver = new FileChooser();
fileSaver.getExtensionFilters().add(new FileChooser.ExtensionFilter("PDF", "pdf"));
File file = fileSaver.showSaveDialog(stage);
getDownloadService(file).start();
}
});
return downloadButton;
}
private Service getDownloadService(File file)
{
Service downloadService = new Service()
{
protected Task createTask()
{
return doDownload(file);
}
};
return downloadService;
}
private Task doDownload(File file)
{
Task downloadTask = new Task<Void>()
{
protected Void call() throws Exception
{
url = new URL("http://www.daoudisamir.com/references/vs_ebooks/html5_css3.pdf");
// I have used this url for this context only
org.apache.commons.io.FileUtils.copyURLToFile(url, file);
return null;
}
};
showPopup(downloadTask);
return downloadTask;
}
Popup showPopup(Task downloadTask)
{
ProgressIndicator progressIndicator = new ProgressIndicator();
progressIndicator.progressProperty().bind(downloadTask.progressProperty());
Popup progressPop = new Popup();
progressPop.getContent().add(progressIndicator);
progressPop.show(stage);
return progressPop;
// I have left out function to remove popup for simplicity
}
public static void main(String[] args)
{
launch(args);
}}
The line:
org.apache.commons.io.FileUtils.copyURLToFile(url, file);
...doesn't provide you any information about the progress of your download (there is no callback or any other indication of its progress). It just downloads something without giving you feedback.
You will have to use something else that gives you feedback on the progress.
Take a look at this questions answers for solutions with feedback (it is for Swing, but you should be able to adapt them for JavaFX): Java getting download progress
You bind the ProgressIndicator's progress property to the Task's progress property, so that changes in the latter will be reflected in the former. However you never actually update your Task's progress.
If you want the progress indicator to show something, you're going to have to call updateProgress(workDone, max) within your task's body (or elsewhere). And that might be tricky if the download logic you're using doesn't give you any progress callbacks. (You could, perhaps, spawn a thread to repeatedly check the size of the file on the filesystem and use that as your current workDone; but you'd need to know what the eventual/complete size of the file would be in order to turn this into a percentage, which may or may not be easy.)
I have 2 fxml files:
Layout (header, menubars and content)
Anchorpane (it's supposed to be placed inside the content from the other fxml file)
I would like to know how can I load the second file inside the content space from the "Master" scene. And is that a good thing to do working in javaFX or is it better to load a new scene?
I'm trying to do something like this, but it doesn't work:
#FXML
private AnchorPane content;
#FXML
private void handleButtonAction(ActionEvent event) {
content = (AnchorPane) FXMLLoader.load("vista2.fxml");
}
Thanks for the help.
Why your code does not work
The loader creates a new AnchorPane, but you never add the new pane to a parent in the scene graph.
Quick Fix
Instead of:
content = (AnchorPane) FXMLLoader.load("vista2.fxml");
Write:
content.getChildren().setAll(FXMLLoader.load("vista2.fxml"));
Replacing the content children with your new vista. The content itself remains in the scene graph, so when you set it's children, you are also attaching them to the scene graph at the same time.
You might need to play around with layout (e.g. work with auto resizing layouts like StackPanes rather than AnchorPanes) to get the exact behaviour you want.
Rather than just adopting the quick fix, I would advise reviewing the simple framework linked below as that might provide you with a more general purpose mechanism to get the behaviour you want.
Reference FXML Navigation Framework
I created a small framework for swapping fxml controlled content panes in and out of a portion of the main scene.
The mechanism of the framework is the same as suggested in kithril's answer.
A main pane for the outer fxml acts as a holder for child panes.
The main controller for the outer fxml supplies a public method that can be used to swap the child panes.
A convenience navigator class is statically initialized with the main controller for the outer layout.
The navigator provides a public static method to load a new child pane into the main container (by invoking a method on the main controller).
Child panes are generated in the navigator by their respective fxml loaders.
Why a Framework
The framework seems like overkill for answering your question, and perhaps it is. However, I have found that the two most asked topic related to FXML are:
Navigation between panes generated by FXML (this question).
How to pass data between FXML controllers.
So I felt that a small demo framework was warranted for this case.
Sample Framework Output
The first screen shows the application layout displaying the first vista. The contents are a header which is defined in the main application layout and an aliceblue colored interchangable child content pane.
In the next screen, the user has navigated to the second vista, which retains the constant header from the main layout and replaces the original child pane with a new coral colored child content pane. The new child has been loaded from a new fxml file.
Looking for Something More Substantial?
A lightweight framework which is more extensive and better supported than the sample framework from this question is afterburner.fx.
Looking for Something Even Simpler?
Just swap out the scene root: Changing Scenes in JavaFX.
Other Options?
Animated Transitions and others: Switch between panes in JavaFX
Im not sure about how effective this is, but seems to be just fine and what's more, much simpler to methods above.
https://www.youtube.com/watch?v=LDVztNtJWOo
As far as I understood what is happening here is this(its really similiar to what is happening in Start() method in application class) :
private void buttonGoToWindow3Action(ActionEvent event) throws IOException{
Parent window3; //we need to load the layout that we want to swap
window3 = (StackPane)FXMLLoader.load(getClass().getResource("/ScenePackage/FXMLWindow3"));
Scene newScene; //then we create a new scene with our new layout
newScene = new Scene(window3);
Stage mainWindow; //Here is the magic. We get the reference to main Stage.
mainWindow = (Stage) ((Node)event.getSource()).getScene().getWindow();
mainWindow.setScene(newScene); //here we simply set the new scene
}
However Im not a java expert and quite new to programing so it would be good if someone experienced would evaluate it.
EDIT:
Ive found even simpler method;
Go to MainApplication class and make static Stage parentWindow.
public static Stage parentWindow;
#Override
public void start(Stage stage) throws Exception {
parentWindow = stage;
Parent root = FXMLLoader.load(getClass().getResource("/ScenePackage/FXMLMainScene.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
Now you get acces to your main Stage so anywhere in a program you can do something like that to change the scene:
Parent window1;
window1 = FXMLLoader.load(getClass().getResource("/ScenePackage/FXMLWindow1.fxml"));
//Scene newSceneWindow1 = new Scene(window1);
Stage mainStage;
//mainStage = (Stage) ((Node)event.getSource()).getScene().getWindow();
mainStage = MainApplication.parentWindow;
mainStage.getScene().setRoot(newSceneWindow1); //we dont need to change whole sceene, only set new root.
Others may have a better solution, but my solution has been to have a simple container like VBox in the outer fxml, then load the new content and add it as a child of the container. If you're only loading one or two forms, this might be the way go to go. However, for a more complete framework, I found this blog post helpful: https://blogs.oracle.com/acaicedo/entry/managing_multiple_screens_in_javafx1 She has source code for her framework which includes fancy transitions. Although it's meant to manage top level scenes, I found it easy to adapt for managing inner content regions too.
My example of the mask.
Using:
Main.getNavigation().load(View2.URL_FXML).Show();
Main.getNavigation().GoBack();
In this case, I recommend you to use custom component instead. First create a custom component for your content:
class Content2 extends AnchorPane {
Content() {
FXMLLoader loader = new FXMLLoader(getClass().getResource("vista2.fxml");
loader.setRoot(this);
loader.setController(this);
loader.load();
}
}
Replace the AnchorPane markup in the root of your vista2.fxml file with fx:root:
<fx:root type="javafx.scene.layout.AnchorPane" xmlns:fx="http://javafx.com/fxml">
...
</fx:root>
Then, you can do this simply by using custom event binding and arrow function. Add event handler property to your Content class:
private final ObjectProperty<EventHandler<ActionEvent>> propertyOnPreviousButtonClick = new SimpleObjectProperty<EventHandler<ActionEvent>>();
#FXML
private void onPreviousButtonClick(ActionEvent event) {
propertyOnPreviousButtonClick.get().handle(event)
}
public void setOnPreviousButtonClick(EventHandler<ActionEvent> handler) {
propertyOnPreviousButtonClick.set(handler);
}
Finally, bind your custom event handler in your java code or fxml:
#FXML
onNextButtonClick() {
Content2 content2 = new Content2();
content2.setOnPreviousButtonClick((event) -> {
Content1 content1 = new Content1();
layout.getChildren().clear();
layout.getChildren().add(content1);
});
layout.getChildren().clear();
layout.getChildren().add(content2);
}
If you don't want to add content dynamically, just setVisible() to true or false
Got stuck up in this too
Tried out most of the answers, wasn't what I wanted so I just used the ideals given to do this:
public class Main extends Application {
public static Stage homeStage;
#Override
public void start(Stage primaryStage) throws Exception{
homeStage = primaryStage;
Parent root = FXMLLoader.load(getClass().getResource("mainView.fxml"));
root.getStylesheets().add(getClass().getResource("stylesheet/custom.css").toExternalForm());
homeStage.setTitle("Classification of Living Organisms");
homeStage.setScene(new Scene(root, 600, 500));
homeStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
this is my main class. Main.java with the landing window/page mainView.fxml.
Used a little of #Tomasz idea, although confused me a lil before I did this in my mainController.java class:
public void gotoSubMenu(Event event) {
Parent window1;
try {
window1 = FXMLLoader.load(getClass().getResource("src/displayView.fxml"));
Stage window1Stage;
Scene window1Scene = new Scene(window1, 600, 500);
window1Stage = Main.homeStage;
window1Stage.setScene(window1Scene);
} catch (IOException e) {
e.printStackTrace();
}
}
created a new Parent window called 'window1' that loaded the second fxml file called 'displayView.fxml' in the src directory.
created an object of the main view stage and set the scene to the newly created scene whose root is window1.
Hope this helps the ones coming into #JavaFX now.
If you're looking for a way to make the button call the new fxml file, this worked for me.
#FXML
private void mainBClicked(ActionEvent event) throws IOException {
Stage stage;
Parent root;
stage=(Stage) ((Button)(event.getSource())).getScene().getWindow();
root = FXMLLoader.load(getClass().getResource("MainMenu.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
Keeping the same scene container but changing the view inside the scene container...
Say the scene container you want to pass a new view and controller into is a GridPane layout named sceneContainer.
Establish a FXMLLoader object of the new view.
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("Notifications.fxml"));
Create a matching container and loading the contents of the new view into it.
GridPane yourNewView = fxmlLoader.load();
set the new view to the sceneContainer. (setAll clears all children first)
sceneContainer.getChildren().setAll(yourNewView);
Get the controller object for the new view and call your method that starts the class logic
Notifications notifications = fxmlLoader.getController();
notifications.passUserName(userName);
A full example would look like this :
#FXML
public void setNotificationsViewToScene() {
try {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("Notifications.fxml"));
GridPane yourNewView = fxmlLoader.load();
sceneContainer.getChildren().setAll(yourNewView);
Notifications notifications = fxmlLoader.getController();
notifications.passUserName(userName);
} catch (IOException e) {
e.printStackTrace();
}
}
Wow, >8 years old with lots of complicated answers and no mention of the >10 year old solution.
There is a dedicated fx:include element for nesting FXML files. It's not in the SceneBuilder library, so you need to write it manually. SceneBuilder can load and display nested FXML just fine though.
<AnchorPane fx:id="content">
<children>
<fx:include fx:id="nestedVista" source="vista2.fxml" />
</children>
</AnchorPane>
You can reference the nested Pane and controller using the fx:id and fx:id+"Controller"
#FXML
Pane nestedVista;
#FXML
VistaController nestedVistaController;
Documentation link: https://docs.oracle.com/javafx/2/api/javafx/fxml/doc-files/introduction_to_fxml.html#nested_controllers