Dynamically adding nodes in JavaFX - java

I'm trying to build a chat application that implements Group chat in JavaFX. I want to make a Scroll Pane inside of a Border pane that will contain all Groups in which the User is member of. The Groups icons (ImageViews) need to be added dynamically(cannot be done in Scene Builder) to the Scroll Pane(inside of a HBox) as the User is joining them.
Currently I'm using a SceneController class that is responsible for all Stage and Scene changes.
package com.ong.Controllers;
import com.ong.Main;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.stage.Stage;
import java.io.IOException;
import java.util.HashMap;
public class ScreenController {
private static HashMap<String, Scene> screenMap = new HashMap<>();
private static Stage main;
public ScreenController(Stage _main){
main = _main;
}
public static void addScreen(String name, FXMLLoader fxmlLoader){
Scene scene = null;
try {
scene = new Scene(fxmlLoader.load());
} catch (IOException e) {
e.printStackTrace();
}
screenMap.put(name,scene);
}
public static void addScene(String name, Scene scene){
screenMap.put(name,scene);
}
public static void removeScreen(String name){
screenMap.remove(name);
}
public static void setMinimumDimensions(int width, int height){
main.setMinWidth(width);
main.setMinHeight(height);
}
public static void setMaximized(boolean full){
main.setMaximized(full);
}
public static void activate(String name){
main.setScene(screenMap.get(name));
main.getIcons().add(new Image(Main.class.getResource("Images/not_logo.png").toString()));
main.setTitle(name);
main.show();
}
}
I already created an FXML file(using scene builder) which contains Border Pane and a Scroll Pane as top child.
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<BorderPane fx:id="borderPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="1080.0" prefWidth="1920.0" xmlns="http://javafx.com/javafx/11.0.2" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.ong.Controllers.HomepageController">
<top>
<ScrollPane fx:id="groupsMenuScrollPane" hbarPolicy="NEVER" prefHeight="62.0" prefWidth="1920.0" stylesheets="#../CSS/groups_menu.css" vbarPolicy="NEVER" BorderPane.alignment="CENTER" />
</top>
</BorderPane>
My plan is to populate all Groups(on initialization) to the Scroll Pane in the Page controller.
package com.ong.Controllers;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.shape.Circle;
import java.net.URL;
import java.util.ResourceBundle;
public class HomepageController implements Initializable {
#FXML
private ScrollPane groupsMenuScrollPane;
#FXML
private BorderPane borderPane;
#Override
public void initialize(URL location, ResourceBundle resources) {
final ImageView imageView = new ImageView("com/ong/Images/not_logo.png");
final Circle clip = new Circle(300, 200, 200);
imageView.setClip(clip);
HBox hbox = new HBox();
hbox.getChildren().add(imageView);
groupsMenuScrollPane.setContent(hbox);
borderPane.setTop(groupsMenuScrollPane);
Scene scene = new Scene(borderPane);
ScreenController.addScene("Homepage", scene);
ScreenController.activate("Homepage");
}
}
I already tried to create a Scene with the updated Border Pane, but I get an error "borderPane is already set as root of another scene". I also tried to directly change the root of the already existing scene, but I get a NullPointerException in that case.
Could you please advise which is the best way to add Nodes dynamically to a Scroll Pane. I will be grateful for comments about the project structure too.

Could you please advise which is the best way to add Nodes dynamically to a Scroll Pane.
You need a reference to the content of the ScrollPane (the HBox in this case), and a method to add things to it:
public class HomepageController implements Initializable {
#FXML
private ScrollPane groupsMenuScrollPane;
#FXML
private BorderPane borderPane;
private HBox groupContainer ;
#Override
public void initialize(URL location, ResourceBundle resources) {
final ImageView imageView = new ImageView("com/ong/Images/not_logo.png");
final Circle clip = new Circle(300, 200, 200);
imageView.setClip(clip);
groupContainer = new HBox();
groupContainer.getChildren().add(imageView);
groupsMenuScrollPane.setContent(groupContainer);
// this is not needed since it's already done in FXML:
// borderPane.setTop(groupsMenuScrollPane);
// ...
}
public void addGroup(...) {
// not sure exactly what you want to add, but for example,
// to add a new image view you would do:
ImageView imageView = new ImageView(...);
groupContainer.getChildren().add(imageView);
}
}
When you load the FXML, keep a reference to the controller:
FXMLLoader loader = new FXMLLoader(getClass().getResource(...));
Parent root = loader.load();
HomepageController controller = loader.getController();
And then you can add things to the scroll pane with
controller.addGroup(...);
As mentioned in another answer, a ListView may be a better approach to this.
You should also probably consider a MVC approach (see, for example, Applying MVC With JavaFx), where the model uses an ObservableList<...> to store the list of groups. The controller can observe that list and modify the scroll pane content when it changes. Then all you have to do is add something to the list via the model.

Your question and problem are separate issues.
For your question. Dynamically adding elements you'll want to look into ListView.
https://docs.oracle.com/javase/8/javafx/api/javafx/scene/control/ListView.html
Anything like recyclerview or reusable views for javafx
As for your problem. Scenes can only have one root node. Within this root, you should have your boarder pane and within that should be your ListView.

Related

How do you transition between controllers within the original window that was created

I currently have 3 classes.
ScreenController (controller class):
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.layout.AnchorPane;
import java.net.URL;
import java.util.ResourceBundle;
public class ScreenController implements Initializable
{
private AnchorPane window;
public ScreenController()
{
super();
}
public ScreenController(AnchorPane window)
{
setWindow(window);
}
public void setWindow(AnchorPane window)
{
this.window = window;
}
public void setScreen(String screen)
{
try
{
Parent root = FXMLLoader.load(getClass().getResource("/com/app/client/resources/fxml/" + screen + ".fxml"));
window.getChildren().setAll(root);
}
catch (Exception e)
{
e.printStackTrace();
}
}
#Override
public void initialize(URL location, ResourceBundle resources)
{
}
}
LoginScreen (primary screen):
import com.app.client.java.controllers.ScreenController;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.layout.AnchorPane;
import java.io.IOException;
public class LoginScreen extends ScreenController
{
#FXML
private AnchorPane loginWindow;
#FXML
private Button goButton;
public LoginScreen()
{
super();
setWindow(loginWindow);
}
#FXML
public void goButtonPressed(ActionEvent event) throws IOException
{
setScreen("Home");
System.out.println("Success.");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane fx:id="loginWindow" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" opacity="0.5" prefHeight="500.0" prefWidth="850.0" xmlns="http://javafx.com/javafx/8.0.172-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.app.client.java.classes.LoginScreen">
<children>
<Button fx:id="goButton" layoutX="205.0" layoutY="60.0" mnemonicParsing="false" onAction="#goButtonPressed" text="Button" />
</children>
</AnchorPane>
HomeScreen (secondary screen):
import com.app.client.java.controllers.ScreenController;
import javafx.fxml.FXML;
import javafx.scene.layout.AnchorPane;
public class HomeScreen extends ScreenController
{
#FXML
private static AnchorPane homeWindow = new AnchorPane();
public HomeScreen()
{
super (homeWindow);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane fx:id="homeWindow" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.172-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.app.client.java.classes.HomeScreen">
<children>
<TextArea layoutX="200.0" layoutY="100.0" prefHeight="200.0" prefWidth="200.0" text="aksajkasjkasja" />
</children>
</AnchorPane>
I would like to be able to move from the primary screen to the secondary screen using the setScreen() function. However, I'm finding that the process doesn't complete successfully.
Another approach I've found that works is (Although it resizes the window, rather than filling the initial window with the contents of the new one):
Parent root = FXMLLoader.load(getClass().getResource("/com/app/client/resources/fxml/" + screen + ".fxml"));
Stage stage = (Stage) loginWindow.getScene().getWindow();
Scene scene = new Scene(root);
stage.setScene(scene);
However, I'd prefer to use the initial implementation due to it being more concise, readable and, theoretically, provides the exact behaviour I would like.
There are a couple issues with what you currently have:
In your LoginScreen constructor you call setWindow with the value of a yet-to-be-injected field:
public LoginScreen()
{
super();
setWindow(loginWindow);
}
No FXML fields will have been injected while the constructor of the controller is executing—meaning loginWindow is null. The reason for this self-evident: The FXMLLoader has to first construct the controller instance before it can start injecting the appropriate fields.
The order of events are: (1) Controller instantiated, (2) fields injected, (3) initialize method invoked; I believe linking any event handlers/change listeners is included in step two. What this means is any initialization that needs to happen regarding FXML fields should be done in the initialize method.
You have the same problem in your HomeScreen constructor with super(homeWindow), though there are other problems there which are addressed in the next point.
In addition to trying to access a yet-to-be-injected field in the constructor, there are two other problems with the following:
#FXML
private static AnchorPane homeWindow = new AnchorPane();
The first problem is you initialize a field that is meant to be injected. Never do this. A good rule of thumb is: If the field is annotated with #FXML then don't manually assign a value to it. The FXML field will eventually be injected which means any value you assign to it beforehand will simply be replaced. This can lead to subtle problems since any code with a reference to the previous value won't be using the object that was actually added to the scene graph.
The other problem is your field is static. Injecting static fields is not supported in JavaFX 8+. It used to be possible in older versions, from what I understand, but that behavior was never officially supported (i.e. was an implementation detail). Besides, it doesn't make sense to have something inherently instance-based (FXML+controllers) set a static field which would affect all instances.
A bonus problem: When you make homeWindow non-static you can no longer use super(homeWindow) because you can't reference it before the super constructor is invoked.
Using the two modified classes should allow your code to run:
LoginScreen.java:
public class LoginScreen extends ScreenController {
#FXML private AnchorPane loginWindow;
#FXML private Button goButton;
#Override
public void initialize(URL location, ResourceBundle resources) {
super.initialize(location, resources);
setWindow(loginWindow); // set window in initialize method
}
#FXML
public void goButtonPressed(ActionEvent event) throws IOException {
setScreen("Home");
System.out.println("Success.");
}
}
HomeScreen.java:
public class HomeScreen extends ScreenController {
#FXML private AnchorPane homeWindow;
#Override
public void initialize(URL location, ResourceBundle resources) {
super.initialize(location, resources);
setWindow(homeWindow); // set window in initialize method
}
}
However don't use:
window.getChildren().setAll(root);
In your ScreenController#setScreen method—it causes a subtle problem. You're adding a root as a child of the window node. But when this happens, the new instance of ScreenController (associated with the new root) has its window == root. In other words, the window created with LoginScreen is now the parent of the window created with HomeScreen. Depending on how a more complex application is designed, this can lead to progressively deeper nesting of "roots".
That said, you already have another approach where you actually replace the entire Scene. The issue you're having there, as you stated, is that the Stage resizes to fit the new Scene. This can be fixed by replacing the root of the Scene, rather than the Scene itself:
window.getScene().setRoot(root);
Some potentially helpful resources:
Introduction to FXML
JavaFX FXML Tutorial (javacodegeeks.com)
JavaFX FXML (jenkov.com)
what does initialize() mean in javafx?
JavaFX : Pass parameters while instantiating controller class
How to swap screens in a javafx-application in the controller-class?
Loading new fxml in the same scene
Passing Parameters JavaFX FXML
Switch between panes in JavaFX
afterburner.fx
mvvmFX
Curated list of JavaFX-related things
You can get the main stage of a JavaFX Application during initialization. Other scene classes should have a Stage field with getter and setter and so you will be able to pass the main stage through their Controller. As for the window resize, you can fix that by adding getStage().getWidth() and getStage().getHeight() in the setScene() statement.
A small example of what I am trying to point:
public class MainClass extends Application {
#Override
public void start(Stage stage) throws Exception {
InputStream sceneStream = MainClass.class.getResourceAsStream("/fxml"+
"/newScene/main.fxml");
FXMLLoader loader = new FXMLLoader();
Parent root = loader.load(sceneStream);
Scene scene = new Scene(root);
stage.setTitle("App title");
NewScene controller = loader.getController();
controller.setMainStage(stage);
stage.setScene(scene, stage.getWidth(), stage.getHeight());
stage.show();
}
So the above function starts from MainClass where the main stage is created. Notice the part in the middle which is a bit separeted from the rest of the code, where by getting the controller of the loaded Scene I am passing the stage to it. You can pass the stage that way to all of your scenes. Also notice the part where the scene is set, where I use two more parameters extracted from the stage; the width and the height. Other than that, there are more ways to get the stage in pretty much every scene that runs on the primary stage, just by doing:
#FXML private Button aButton;
public Button getAButton(){
return aButton;
}
Stage stage = (Stage) getAButton().getScene().getWindow();
This will work in all scenes based in the primary stage and it only requires you to have a registered in the Scene Graph Node no matter the type.

JavaFX, switching panes in a root window and retaining memory

As stated in the title, I have fxml files, I have a UI that is set up with three labels/buttons up top and the lower half of the window has a pane. Every time a label/button is clicked, the pane must switch to that corresponding fxml file. So in other words, the pane must always be in the same position, kind of like a tabbed layout but without tabs.
I know I can achieve this with just loading a new instance of an fxml file but, I want to avoid that because when a user click on a tab he previously was on, he should be able to see his earlier input.
I have some main.java that starts the program. Some controller.java that controls the UI when it is first loaded, and some fxml file corresponding to that initial view. How can I go about implementing this transition functionality? P.S. I am very novice at JavaFX.
Here is a MCVE of how you can achieve it.
It can of course be implemented using FXML :
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class StageTest extends Application{
private Pane pane1, pane2, mainPane;
#Override
public void start(Stage stage) throws Exception {
stage.setTitle("Switch Panes");
Button button1 = new Button("Show Pane 1");
button1.setOnAction(e -> showPane1());
Button button2 = new Button("Show Pane 2");
button2.setOnAction(e -> showPane2());
HBox buttonsPane = new HBox(5.);
buttonsPane.getChildren().addAll(button1, button2);
pane1 = getPane("PANE ONE");
pane2 = getPane("PANE TWO");
mainPane = new StackPane(pane1);
BorderPane root = new BorderPane();
root.setTop(buttonsPane);
root.setCenter(mainPane);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
private void showPane1() {
mainPane.getChildren().clear();
mainPane.getChildren().add(pane1);
}
private void showPane2() {
mainPane.getChildren().clear();
mainPane.getChildren().add(pane2);
}
private Pane getPane(String txt) {
VBox pane = new VBox();
pane.getChildren().addAll(new TextArea(txt+" add text here: "));
return pane;
}
public static void main(String[] args) {
launch(args);
}
}

How to lookup the thumb of a slider in a controller class for fxml?

I want to customize a Slider and found a working example here on stackoverflow:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class SliderWithLabeledThumb extends Application {
#Override
public void start(Stage primaryStage) {
Slider slider = new Slider();
StackPane root = new StackPane(slider);
root.setPadding(new Insets(20));
Scene scene = new Scene(root);
slider.applyCss();
slider.layout();
Pane thumb = (Pane) slider.lookup(".thumb");
Label label = new Label();
label.textProperty().bind(slider.valueProperty().asString("%.1f"));
thumb.getChildren().add(label);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Unfortunately, if I look up the thumb of the slider in my controller class for an fxml view, I will always get a NullPointerException when trying to add the Label to the thumb's children… Thumb can obviously not be looked up. Then I found another answer here that says lookup() must be called after stage.show(), so here is my question again:
How can I look up a slider thumb in a controller class that manages fxml components (that does not have a stage.show())?
I tried creating a Slider in code only and also used one from fxml, both lookups return null when calling that in the controller's initialize(…) method.Is it somehow possible to customize a Slider's thumb in a controller class?
EDIT:
The code that produces the exception is in the controller class that gets automatically instantiated due to an entry in the corresponding xml file. No matter if I use a Slider loaded from fxml by annotation, having just a member and initialize that or create a new Slider object. As long as I do that in the controller class, I will keep getting NullPointerExceptions from the line commented in the code below:
public class MainViewController implements Initializable {
#FXML private VBox root;
private Slider timeSlider;
#Override
public void initialize(URL location, ResourceBundle resources) {
timeSlider = new Slider();
root.getChildren.add(timeSlider);
timeSlider.setMax(360);
timeSlider.setMin(0);
timeSlider.setCursor(Cursor.OPEN_HAND);
timeSlider.applyCss();
timeSlider.layout();
Pane thumb = (Pane) timeSlider.lookup(".thumb");
Label label = new Label();
label.textProperty().bind(timeSlider.valueProperty().asString("%.1f"));
// the following line points to thumb, which is null then… (debugger proof)
thumb.getChildren().add(label);
}
}
In the Main.java I just load the fxml file:
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("MainView.fxml"));
Parent root = loader.load();
Scene scene = new Scene(root,800,600);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
Finally, the fxml file:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.VBox?>
<VBox fx:id="root" maxHeight="600" maxWidth="800" prefHeight="600"
prefWidth="800" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="application.MainViewController">
</VBox>
Put the lookup code in the slider listener like below:
volumeBar.valueProperty().addListener((observable, oldValue, newValue) -> {
Pane thumb = (Pane) volumeBar.lookup(".thumb");
if(thumb.getChildren().size() == 0){
Label label = new Label();
label.textProperty().bind(volumeBar.valueProperty().asString("%.0f"));
thumb.getChildren().add(label);
}
});

Align content of a TextArea in JavaFX

I'm making a simple text editor for training purposes. I want to create a button which would center my text input from TextArea just like in MS Word. I have my button in FXML, but I don't know what method should I use for my TextArea object, I tried setStyle() or getChild() but neither worked.
<Button onAction="#toTheCenter" text="center"/>
Thats my button in FXML
<center>
<TextArea fx:id="textArea"/>
</center>
Thats TextArea
#FXML
private void toTheCenter(ActionEvent event){
String text = textArea.getText();
}
And thats the method from controller.
You need to set the -fx-text-alignment property on the text node of the text area.
The best way to do this dynamically is to define a custom CSS pseudoclass for the text area:
PseudoClass centered = PseudoClass.getPseudoClass("centered");
and then in your external CSS file you can do
.text-area:centered .text {
-fx-text-alignment: center ;
}
Then you can call
textArea.pseudoClassStateChanged(centered, true);
to switch centering on, and
textArea.pseudoClassStateChanged(centered, false);
to switch it off.
Here is a SSCCE (put the CSS code above in centering-text-area.css):
import javafx.application.Application;
import javafx.css.PseudoClass;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.control.ToggleButton;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class TextAreaWithCentering extends Application {
#Override
public void start(Stage primaryStage) {
TextArea textArea = new TextArea();
PseudoClass centered = PseudoClass.getPseudoClass("centered");
ToggleButton center = new ToggleButton("Center");
center.selectedProperty().addListener((obs, wasCentered, isNowCentered) ->
textArea.pseudoClassStateChanged(centered, isNowCentered));
BorderPane.setAlignment(center, Pos.CENTER);
BorderPane.setMargin(center, new Insets(5));
BorderPane root = new BorderPane(textArea, null, null, center, null);
Scene scene = new Scene(root, 600, 600);
scene.getStylesheets().add("centering-text-area.css");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
#text-area *.text { -fx-text-alignment: center; }
Create a style.css file in your project folder and then add the following line to the java code.
scene.getStylesheets().addAll(this.getClass().getResource("style.css").toExternalForm()); primaryStage.setScene(scene);

Change/Update FXML components in another class

My problem is: I want to change something from the .fxml but whatever I do, nothing changes. This is just a simply example.
I went trough the whole internet but none of the solutions worked for me.
Here I want to change the text of the label, by calling the corresponding method from the main class.
Calling the same method (here setLabel()) when clicking a Button, with an event handler in the controller class, everything works fine, but a soon as I try to modify something from another class nothing works.
Main class:
package sample;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
Controller controller = new Controller();
Platform.runLater(()->controller.setLabel());
}
FXML Code:
<BorderPane xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
<center>
<Label fx:id="label" text="This" BorderPane.alignment="CENTER" />
</center>
</BorderPane>
Controller class:
package sample;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
public class Controller {
#FXML
private Label label=new Label();
public void setLabel(){
label.setText("Test");
}
}
There are actually two problems with your code.
1) In the start method of your Application you load sample.fxml using an FXMLLoader, which is correct. But creating a new controller like Controller controller = new Controller(); is incorrect, as you should get the controller from the FXMLLoader itself using getController method, and also you should not use the static load function of FXMLLoader, but you should create an instance of it.
FXMLLoader loader = new FXMLLoader();
Parent root = loader.load(getClass().getResource("sample.fxml"));
Controller controller = loader.getController();
When you call load on an FXMLLoader it will load the object hierarchy from the FXML file and it will also create a controller (referenced in the FXML file).
2) In Controller you inject the Label from FXML file, but you re-create it. When the FXMLLoader injects a control to your controller based on the fx:id it also ensures initialization. If you create a new Label it will not point to the Label instance created by the loader.
This
#FXML private Label label= new Label();
should be replaced with
#FXML private Label label;
That is pretty simple... You can change the the label text from your Main class either by adding a getter method in your Controller class for the required label and then get it in the Main class using the controller object (loader.getController()) and update its text. Or call the setter method inside the Controller class using controller object in Main.
As DVagra said, use loader.getController() to get the controller object.
(where loader is the object of FXMLoader).
Moreover you do not need Platform.runLater() to update the gui controls. As you're already running on the FX thread.
Anyway, here is what you need.
Main Class
package sample;
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 {
#Override
public void start(Stage primaryStage) throws Exception{
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("sample.fxml"));
Parent root = loader.load();
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
Controller controller = loader.getController();
controller.setLabel("Test");
// Or
// controller.getLabel().setText("Test");
}
public static void main(String[] args) {
launch(args);
}
}
Controller Class
package sample;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import java.net.URL;
import java.util.ResourceBundle;
public class Controller implements Initializable{
#FXML
Label label;
//setter
public void setLabel(String labelText){
label.setText(labelText);
}
//getter for label
public Label getLabel() {
return label;
}
#Override
public void initialize(URL location, ResourceBundle resources) {
}
}
Sample.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.BorderPane?>
<BorderPane xmlns="http://javafx.com/javafx/8.0.60"
xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
<center>
<Label fx:id="label" text="This" BorderPane.alignment="CENTER" />
</center>
</BorderPane>

Categories

Resources