Switch scenes from FXML to pure JavaFX UI class - java

I'm pretty new to JavaFX. I have learnt how to switch scenes between FXML files or classes purely coded in JavaFX only.
My biggest challenge now is to switch between an FXML UI and another built in JavaFX or vice-versa, so I know it's possible but I just can't get it right. My code for my Application controller is:
package com.example.fxmltojavafx;
import com.sun.javafx.stage.EmbeddedWindow;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.stage.Stage;
import java.net.URL;
import java.util.ResourceBundle;
public class HelloController extends Application {
#FXML
private Label welcomeText;
#FXML
private Button bt_switch;
#FXML
protected void onHelloButtonClick() {
//switch scene method from fxml to pure javafx
//bt_switch.setOnAction(e -> window.setScene(InterfaceSwitch));
welcomeText.setText("Welcome to JavaFX Application!");
}
#Override
public void start(Stage stage) throws Exception {
bt_switch.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
bt_switch.setOnAction(e -> window.setScene(InterfaceSwitch));
}
});
}
}
How do I reference the JavaFX class I want to load from a click of a fxml button? I tried window.setScene(), but it doesn't get my reference right. Of course I can't use FMXMLLoader.load().
The scene I want to load from this button is coded in this class:
(The buttons coded into it are just a test)
package com.example.fxmltojavafx;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
/***
* Intefaccia puramente in JavaFX
*/
public class InterfaceSwitch extends Application {
private Stage stage;
#Override
public void start(Stage stageInterface) throws Exception {
VBox parent = new VBox();
parent.getChildren().add(new Label("SERVER DOMINATOR : Inserisci dati"));
//setup process
HBox username = new HBox(new Label("username :")); //username field
TextField usernametext = new TextField();
username.getChildren().add(usernametext);
parent.getChildren().add(username);
HBox password = new HBox(new Label("password :")); //password field
TextField passwordtext = new TextField();
password.getChildren().add(passwordtext);
parent.getChildren().add(password);
HBox port = new HBox(new Label("port :")); //port field
TextField porttext = new TextField();
port.getChildren().add(porttext);
parent.getChildren().add(port);
HBox signupQuest = new HBox(new Label("Set up my game!"));
parent.getChildren().add(signupQuest);
Button button1 = new Button("Set up");
parent.getChildren().add(button1);
//Scene scene = new Scene(new Label("Server Dominator : Set up"));
stage.setScene(new Scene(parent));
stage.show();
}
}
My FXML UI is basically just 2 buttons, one for "hello world" and the other is the bt_switch
My main class:
package com.example.fxmltojavafx;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
public class HelloApplication extends Application {
#Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 320, 240);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
It must be a simple set up issue but I can't get my head around it. How can I do it?

The Application class represents the entire application, and specifically its lifecycle. Its purpose is to manage the startup and shutdown of the application via methods like start(), init(), and stop(). Consequently, each application should have only one Application subclass and there should only be one instance of it (which is created for you when the application is launched).
In the code you posted, HelloApplication represents the application lifecycle. HelloController is just a controller, and should not be a subclass of Application. InterfaceSwitch does not represent the application either (it just represents a UI), so it should not be a subclass of Application.
You should have something like
public class SomeUI {
private VBox root ;
public SomeUI() {
root = new VBox();
root.getChildren().add(new Label("SERVER DOMINATOR : Inserisci dati"));
//setup process
HBox username = new HBox(new Label("username :")); //username field
TextField usernametext = new TextField();
username.getChildren().add(usernametext);
root.getChildren().add(username);
HBox password = new HBox(new Label("password :")); //password field
TextField passwordtext = new TextField();
password.getChildren().add(passwordtext);
root.getChildren().add(password);
HBox port = new HBox(new Label("port :")); //port field
TextField porttext = new TextField();
port.getChildren().add(porttext);
root.getChildren().add(port);
HBox signupQuest = new HBox(new Label("Set up my game!"));
root.getChildren().add(signupQuest);
Button button1 = new Button("Set up");
root.getChildren().add(button1);
}
public Parent getRoot() {
return root ;
}
}
Since you said your controller is a controller for an FXML file with two buttons, it needs two event handler methods (one for each button). The handler for the button which switches scenes just does so in the normal way. Obviously, the controller should not be an Application.
public class HelloController {
#FXML
private Label welcomeText;
#FXML
protected void onHelloButtonClick() {
welcomeText.setText("Welcome to JavaFX Application!");
}
#FXML
protected void switchScenes() {
SomeUI newScene = new SomeUI();
Parent root = newScene.getRoot();
welcomeText.getScene().setRoot(root);
}
}
The FXML would look something like:
<VBox spacing="10" xmlns = "http://javafx.com/javafx/16" xmlns:fx = "http://javafx.com/fxml/1" fx:controller="com.example.HelloController">
<Label fx:id="welcomeText" />
<Button text="Say Hello" onAction="#onHelloButtonClick"/>
<Button text="Switch scenes" onAction="#switchScenes" />
</VBox>

Related

JavaFX - Splitting GUI into separate classes while using tabs

I am trying to make a program with a tabbed GUI where each tab's contents are their own class. I essentially want to do what is done here, but with tabs. Mere is the Manager class:
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.stage.Stage;
public class Manager extends Application {
private TabPane tabPane;
private Tab dcTab;
public Manager() {
dcTab = new Tab();
tabPane = new TabPane();
DistributionCenter dcMenu = new DistributionCenter();
dcTab.setText("Distribution Center");
dcTab.setContent(dcMenu);
tabPane.getTabs().add(dcTab);
}
#Override
public void start(Stage primaryStage) throws Exception {
Group root = new Group();
Scene scene = new Scene(tabPane, 600, 500);
DistributionCenter dcMenu = new DistributionCenter();
root.getChildren().add(dcMenu);
dcTab.setContent(dcMenu);
primaryStage.setScene(scene);
primaryStage.setTitle("909th Wing");
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
And here is my DistributionCenter class:
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
public class DistributionCenter extends Pane {
private Button testBt1;
public DistributionCenter() {
Pane dcMenu = new Pane();
testBt1 = new Button("Button");
dcMenu.getChildren().addAll(testBt1);
testBt1.setLayoutX(30);
testBt1.setLayoutY(100);
}
}
Note that the contents of DistributionCenter are just a test at this point.
You are never adding your Button to the actual DistributionCenter pane. Instead, you are creating a new Pane within that class and adding the button to it.
Remember, DistributionCenter is a Pane so it has its own children. You do not need to create a new Pane within it.
Update your DistributionCenter class to just configure itself:
public DistributionCenter() {
testBt1 = new Button("Button");
getChildren().addAll(testBt1);
}
That will solve your problem, but you should never extend a class unless you need to implement different behavior. If you are just configuring a Node like this, you should just create an instance of a Pane and configure it properly.

JavaFX Textfield data to TableView in different Stage

I'm trying to input user data from TextField in new window into TableView in the main window. But it is not populating table, and I'm not getting any Exception. On button clicked new window just closes. I'm creating very simple app just to figure out how to communicate properly between controllers.
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 {
static Stage primaryStage;
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
this.primaryStage= primaryStage;
this.primaryStage.setTitle("Hello World");
this.primaryStage.setScene(new Scene(root));
this.primaryStage.show();
}
public void closeStage(){
primaryStage.close();
}
public static void main(String[] args) {
launch(args);
}
}
Controller class for main window where are table and button for opening new Stage:
package sample;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Scene;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
public class Controller implements Initializable {
#FXML TableView<radnici> tabela;
#FXML TableColumn<radnici, String> kolona;
#FXML Button dodajRadnikaDugme;
#FXML Button closeDugme;
Main main = new Main();
static Stage stage = new Stage();
Scene scene;
BorderPane borderPane;
#Override
public void initialize(URL location, ResourceBundle resources) {
kolona.setCellValueFactory(new PropertyValueFactory<radnici, String>("ime"));
}
public TableView<radnici> getTabela() {
return tabela;
}
public TableColumn<radnici, String> getKolona() {
return kolona;
}
//Opening new Stage on button clicked
public void dodajRadnikaDugemKlik() throws IOException {
FXMLLoader loader = new FXMLLoader();
borderPane = new BorderPane();
loader.setLocation(getClass().getResource("dodajRadnika.fxml"));
borderPane = loader.load();
scene = new Scene(borderPane);
stage.setTitle("Dodaj Radnika");
stage.setScene(scene);
stage.showAndWait();
}
//Closing main window
public void closeDugmeKlik(){
main.closeStage();
}
//Method for closing new window
public void CloseStage(){
stage.close();
}
}
Controller class for the new Window where are just TextField and Button:
package sample;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import java.io.IOException;
public class dodajRadnikaKontroler {
#FXML TextField upišiRadnika;
#FXML Button dodajRadnika;
BorderPane borderPane;
public void initialize(){
System.out.println("učitavanje podataka...");
}
//Method for adding data on button clicked from TextField into table in main window
#FXML public void dodajRadnikaKlik() throws IOException {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("sample.fxml"));
borderPane = new BorderPane();
borderPane = loader.load();
Controller controller = loader.getController();
ObservableList<radnici> lista = FXCollections.observableArrayList();
lista.add(new radnici(upišiRadnika.getText()));
controller.tabela.setItems(lista);
upišiRadnika.clear();
controller.CloseStage();
}
}
Model Class for workers ("radnici"):
package sample;
public class radnici {
private String ime;
public radnici(String ime) {
this.ime = ime;
}
public String getIme() {
return ime;
}
public void setIme(String ime) {
this.ime = ime;
}
}
Please can someone help me so that I can finally do this properly.
Note: please use proper naming conventions. I have changed some names of classes so that they follow standard conventions.
You don't see the changes to your table, because you are loading a entire new UI structure from the FXML file, including a new TableView, and then you set the items in that new table view.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("sample.fxml"));
borderPane = new BorderPane();
// load a new UI from the FXML file:
borderPane = loader.load();
// get the controller for the new UI:
Controller controller = loader.getController();
ObservableList<radnici> lista = FXCollections.observableArrayList();
lista.add(new radnici(upišiRadnika.getText()));
// set the table items in the new UI:
controller.tabela.setItems(lista);
upišiRadnika.clear();
controller.CloseStage();
Since you don't even display this new UI, you don't see the items in the table.
Presumably what you actually want to do is update the items in the existing table. All you need for this is a reference to the table's backing list. You need to pass that to the controller for the second FXML file. Add a field and method to DodajRadnikaKontroler:
public class DodajRadnikaKontroler {
// Existing code omitted:
private ObservableList<Radnici> items ;
public void setItems(ObservableList<Radnici> items) {
this.itmes = items ;
}
}
and then pass the table's list to the new controller when you load the FMXL file:
//Opening new Stage on button clicked
public void dodajRadnikaDugemKlik() throws IOException {
FXMLLoader loader = new FXMLLoader();
borderPane = new BorderPane();
loader.setLocation(getClass().getResource("dodajRadnika.fxml"));
borderPane = loader.load();
DodajRadnikaKontroler controller = loader.getController();
controller.setItems(tabela.getItems());
scene = new Scene(borderPane);
stage.setTitle("Dodaj Radnika");
stage.setScene(scene);
stage.showAndWait();
}
and now in the dodajRadnikaKlik() method you just update the list:
#FXML public void dodajRadnikaKlik() throws IOException {
// if you want to add an item:
items.add(new radnici(upišiRadnika.getText()));
// if you want to replace all items with the new one:
// items.setAll(new radnici(upišiRadnika.getText()));
upišiRadnika.clear();
controller.CloseStage();
}

Make TextField/TextArea caret visible in JavaFX GUI

I was wondering how to make the caret visible and/or flash whilst using a TextField or TextArea. The ones I have created in my GUI work as the letters appear when typed but there is no visible caret.
I've looked through the TextField documentation but none are about making it visible or not. I expected to find something along the lines "setCaretVisible(Boolean);"
Do I have to make it visible via CSS? If so, any suggestions would be most welcome!
Please see code that I've quickly put together to illustrate the problem:
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCombination;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class Test extends Application {
public static void main(String[] arguments) { launch(arguments); }
#Override public void start(Stage stage) {
Scene scene = new Scene(new Group());
scene.setRoot(new BuildLayout(stage));
stage.setTitle("Application Name");
stage.setScene(scene);
stage.initStyle(StageStyle.UNDECORATED);
stage.initStyle(StageStyle.TRANSPARENT);
stage.setFullScreen(true);
stage.setFullScreenExitHint("");
stage.setFullScreenExitKeyCombination(KeyCombination.NO_MATCH);
stage.show();
}
}
final class BuildLayout extends BorderPane {
protected BuildLayout(Stage stage) {
TabPane tabpane = new TabPane();
Tab tab = new Tab();
tab.setGraphic(new Text("Video Browser"));
tab.setClosable(false);
tab.setContent(new Input(1));
tabpane.getTabs().addAll(tab);
setTop(new Toolbar(stage));
setCenter(tabpane);
}
}
final class Input extends VBox {
Input(int id) {
HBox hbox = new HBox();
TextField videoTitle = new TextField("video_title");
TextArea description = new TextArea( "video_description");
Button share = new Button("share");
Button unshare = new Button("unshare");
setPadding(new Insets(5,10,10,5));
setAlignment(Pos.TOP_CENTER);
hbox.getChildren().addAll(videoTitle, new Text(" Creator: " + " Date: " + " Views: " + " "));
if(true) {
videoTitle.setEditable(true);
description.setEditable(true);
if(true) {
hbox.getChildren().add(share);
} else { hbox.getChildren().add(unshare); }
}
getChildren().addAll(hbox, description);
}
}
final class Toolbar extends HBox {
protected Toolbar(Stage stage) {
Button close = new Button("close");
close.setOnAction((ActionEvent e) -> { stage.close(); });
setAlignment(Pos.CENTER_LEFT);
getChildren().addAll(close);
}
}
Many thanks,
This problem only occurs on Mac and not Windows (untested Linux). The fix is to downgrade to a JavaFX 2.2 compliant maximised window as the setMaximized() from JavaFX 8 also isn't Mac compatible.
Modifying the start method with some code found here:
#Override public void start(Stage stage) {
Scene scene = new Scene(new Group());
scene.setRoot(new BuildLayout(stage));
Screen screen = Screen.getPrimary();
Rectangle2D bounds = screen.getVisualBounds();
stage.setTitle("Application Name");
stage.setScene(scene);
stage.setX(bounds.getMinX());
stage.setY(bounds.getMinY());
stage.setWidth(bounds.getWidth());
stage.setHeight(bounds.getHeight());
stage.show();
}
Produces a fullscreen application with visible and flashing caret.

TextField right alignment issue javafx

I have textfield that containing a value and that value should be Right alignment.
When i run the application it display the text left alignment but i set the Right alignment.
and problem with 3rd textfield.
After clicking the update button it works fine.
So may i know why it is behave different.
Code:
import java.io.File;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
public class TextFieldAlignment extends Application {
TextField rText;
File file;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
rText = new TextField("updated right1 updated right2 updated right3 updated right4");
rText.setAlignment(Pos.CENTER_RIGHT);
Button btn = new Button("update");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
rText.setText("updated right1 updated right2 updated right3 updated right4");
// applyWorkaround();
}
});
final Label labelFile = new Label();
Button btn2 = new Button();
btn2.setText("Open FileChooser'");
btn2.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
FileChooser fileChooser = new FileChooser();
//Set extension filter
FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("AVI files (*.exml)", "*.exml");
fileChooser.getExtensionFilters().add(extFilter);
//Show open file dialog
file = fileChooser.showOpenDialog(null);
// rText.setText(file.getPath());
}
});
VBox root = new VBox();
root.getChildren().addAll(rText, btn, btn2);
stage.setScene(new Scene(root, 200, 300));
stage.show();
}
}
Put the setalignment and settext after you add the TextField to the Scene.
Ref: Java API docs for Node
Node objects may be constructed and modified on any thread as long
they are not yet attached to a Scene. An application must attach nodes
to a Scene, and modify nodes that are already attached to a Scene, on
the JavaFX Application Thread.

Better way for Getting id of the clicked Object in JavaFX controller

I`m looking for a better way for getting the id of the clicked object inside the event handler for this object.
I already found this:
javafx pass fx:id to controller or parameter in fxml onAction method
But that did not work for me.
Now I'm using the getId() function of the node class like this:
Button btn = (Button) event.getSource();
String id = btn.getId();
But i want to use this method not only for buttons.
Since fx:id is used to bind controls between FXML and Controller, this answer is taking into consideration that OP wants the id of the controls when clicked.
import javafx.application.Application;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Control;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class IdForControlsOnClick extends Application{
#Override
public void start(Stage stage) throws Exception {
BorderPane borderPane = new BorderPane();
VBox vBox = new VBox(20);
borderPane.setCenter(vBox);
Button button = new Button("Hi");
button.setId("Button");
Label label = new Label("Label");
label.setId("Label");
CheckBox checkBox = new CheckBox();
checkBox.setId("CheckBox");
button.addEventHandler(MouseEvent.MOUSE_CLICKED, new MyEventHandler());
label.addEventHandler(MouseEvent.MOUSE_CLICKED, new MyEventHandler());
checkBox.addEventHandler(MouseEvent.MOUSE_CLICKED, new MyEventHandler());
vBox.getChildren().addAll(button, label, checkBox);
Scene scene = new Scene(borderPane, 200, 200);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
private class MyEventHandler implements EventHandler<Event>{
#Override
public void handle(Event evt) {
System.out.println(((Control)evt.getSource()).getId());
}
}
}
I use this for getting the id of ImageView objects that all share the same event code. Here is a simple example using MouseEvent:
#FXML
private void selectImage(MouseEvent event)
{
String source1 = event.getSource().toString(); //yields complete string
String source2 = event.getPickResult().getIntersectedNode().getId(); //returns JUST the id of the object that was clicked
System.out.println("Full String: " + source1);
System.out.println("Just the id: " + source2);
System.out.println(" " + source2);
}
Here is the output in my situation, where I used SceneBuilder to assign the selectImage method to the 'On Mouse Pressed' event, then running the code and randomly clicking on three different ImageView objects:
Full String: ImageView[id=iv1, styleClass=image-view]
Just the id: iv1
Full String: ImageView[id=iv4, styleClass=image-view]
Just the id: iv4
Full String: ImageView[id=iv6, styleClass=image-view]
Just the id: iv6
I hope this helps someone. :-)

Categories