I know, this question has been asked hundreds of times and I spent the last 3 hours reading all of them. I am writing a little Application with JavaFX (and I have done so before...). Everything is working besides putting content into my column:
#FXML public void initialize() {
exerciseColumn.setCellValueFactory(new PropertyValueFactory<Exercise, String>("name"));
}
This is my initialize method and this:
public StringProperty nameProperty() {
return name;
}
is the Property Getter for "StringProperty name". The really funny thing is, that the code is the exact copy of another project I made a while ago (I double and triple checked everything of course) that I compiled again today and that is working perfectly. I read the doc for, as it feels, half JavaFX to find a solution but it just does not make any sense. If you think you need more code to help me, then I can provide it, of course.
edit: another example with the same issue (at least I hope so)
Main:
package test;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
import test.model.Exercise;
import test.view.Controller;
public class Main extends Application {
private Stage primaryStage;
private AnchorPane pane;
private ObservableList<Exercise> activeSession = FXCollections.observableArrayList();
public Main() {
activeSession.add(new Exercise("ednhzrd"));
}
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
this.primaryStage = primaryStage;
this.primaryStage.setTitle("Test");
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/view.fxml"));
pane = (AnchorPane) loader.load();
Scene scene = new Scene(pane);
primaryStage.setScene(scene);
primaryStage.show();
Controller controller = new Controller();
controller.setMainApp(this);
} catch(Exception e) {
e.printStackTrace();
}
}
}
Controller:
package test.view;
import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import test.Main;
import test.model.Exercise;
public class Controller {
#FXML private TableView<Exercise> exerciseTable;
#FXML private TableColumn<Exercise, String> exerciseColumn;
private Main main;
public Controller() {}
#FXML
public void initialize() {
exerciseColumn.setCellValueFactory(cellData -> cellData.getValue().nameProperty());
}
public void setMainApp(Main main) {
this.main = main;
}
}
Exercise:
enter codepackage test.model;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Exercise {
private StringProperty name;
public Exercise(String name) {
this.name = new SimpleStringProperty(name);
}
public StringProperty nameProperty() {
return name;
}
}
fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane prefHeight="400.0" prefWidth="209.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="test.view.Controller">
<children>
<TableView fx:id="exerciseTable" layoutX="200.0" layoutY="100.0" prefHeight="400.0" prefWidth="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<columns>
<TableColumn fx:id="exerciseColumn" prefWidth="75.0" text="C1" />
</columns>
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
</columnResizePolicy>
</TableView>
</children>
</AnchorPane>
There is no problem with your cellValueFactory. The only issue here is that you never put any items in the table. If you modify the controller to add a test item to the table:
package test.view;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import test.Main;
import test.model.Exercise;
public class Controller {
#FXML
private TableView<Exercise> exerciseTable;
#FXML
private TableColumn<Exercise, String> exerciseColumn;
private ObservableList<Exercise> activeSession = FXCollections.observableArrayList();
private Main main;
public Controller() {
}
#FXML
public void initialize() {
exerciseColumn.setCellValueFactory(cellData -> cellData.getValue().nameProperty());
activeSession.add(new Exercise("hrrykane"));
exerciseTable.setItems(activeSession);
}
public void setMainApp(Main main) {
this.main = main;
}
}
then you see the desired effect:
Related
I'm currently trying to figure out how to implement a feature in which the user clicks a button and a popup appears, and then the user enters data into that popup, clicks a confirm button, and the data becomes visible in the popups owner. I was able to make data from the owner stage carry over to the popup stage after follow Bro Code's tutorial on making controllers communicate, however getting it to work the other way around is proving troublesome. Below is a project in which I've isolated the issue to try and figure it out.
App.java
package org.example;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.util.Objects;
public class App extends Application {
public static Stage stage;
#Override
public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(Objects.requireNonNull(getClass().getResource("primary.fxml")));
Scene scene = new Scene(root);
stage = new Stage();
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
PrimaryController.java
package org.example;
import java.io.IOException;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Modality;
import javafx.stage.Stage;
public class PrimaryController {
#FXML
Label label;
#SuppressWarnings("unused")
public void login(ActionEvent event) throws IOException{
FXMLLoader loader = new FXMLLoader(getClass().getResource("secondary.fxml"));
Parent root = loader.load();
SecondaryController secondaryController = loader.getController();
secondaryController.stage = new Stage();
secondaryController.stage.initModality(Modality.APPLICATION_MODAL);
secondaryController.stage.initOwner(App.stage);
Scene scene = new Scene(root);
secondaryController.stage.setScene(scene);
secondaryController.stage.show();
}
public void displayMessage(String message){
label.setText(message);
}
}
SecondaryController.java
package org.example;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
import java.io.IOException;
public class SecondaryController {
#FXML
TextField textField;
public Stage stage;
#SuppressWarnings("unused")
public void writeToOwner(ActionEvent event) throws IOException {
String message = textField.getText();
FXMLLoader loader = new FXMLLoader(getClass().getResource("primary.fxml"));
Parent root = loader.load();
PrimaryController primaryController = loader.getController();
primaryController.displayMessage(message);
stage.close();
}
}
primary.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.example.PrimaryController">
<children>
<Button layoutX="270.0" layoutY="230.0" mnemonicParsing="false" onAction="#login" text="Login" />
<Label fx:id="label" layoutX="280.0" layoutY="191.0" />
</children>
</AnchorPane>
secondary.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.example.SecondaryController">
<children>
<TextField fx:id="textField" layoutX="219.0" layoutY="187.0" onAction="#writeToOwner" />
</children>
</AnchorPane>
The current behavior that I get from this code is almost what I want, except when the user would submit the text, it closes the popup but doesn't change the popups owner.
In your SecondaryController, it looks like you're creating a second instance of PrimaryController, leaving the first unchanged when invoking writeToOwner().
One approach is to arrange for the controllers to see a common model, much as they share a common stage in your example. The simplest such model is a single ObservableValue, illustrated here. To see the effect,
Add a StringProperty named text to your SecondaryController and make it accessible. In writeToOwner(), simply update the text and the bound label will follow.
public class SecondaryController {
#FXML
TextField textField;
public Stage stage;
private final StringProperty text = new SimpleStringProperty();
public StringProperty textProperty() {
return text;
}
#SuppressWarnings("unused")
public void writeToOwner(ActionEvent event) throws IOException {
text.set(textField.getText());
stage.close();
}
}
In your PrimaryController, bind the label's textProperty() to the textProperty() of the SecondaryController.
public class PrimaryController {
#FXML
Label label;
public StringProperty text = new SimpleStringProperty();
#SuppressWarnings("unused")
public void login(ActionEvent event) throws IOException {
…
secondaryController.stage.initOwner(App.stage);
label.textProperty().bind(secondaryController.textProperty());
…
}
}
In practice, you'll want to avoid public access to class members.
I got a Button called "Transfer" with the onclick method and a VBox with 3 Buttons in one window.
The second window only has a HBox container.
I wanna understand how to send the VBox to the HBox in the other window by pressing the transfer Button.
And what would also help is feedback regarding my code/file organization.
Main Class:
package testproject;
import javafx.application.Application;
import testproject.screen1.Screen1;
import testproject.screen2.Screen2;
import javafx.stage.Stage;
public class Main extends Application {
private Screen1 screen1;
private Screen2 screen2;
#Override
public void start(Stage primaryStage) throws Exception{
screen1 = new Screen1(this, new Stage());
screen2 = new Screen2(this, primaryStage);
}
public static void main(String[] args) {
Application.launch(args);
}
}
Screen1 Class:
package testproject.screen1;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import testproject.Main;
import java.io.IOException;
public class Screen1 {
public Screen1(Main main, Stage stage){
FXMLLoader loader = new FXMLLoader();
try {
Parent root =
loader.load(getClass().getResourceAsStream("/testproject/screen1/screen1.fxml"));
stage.setTitle("Screen 1");
stage.setScene(new Scene(root, 300, 275));
}
catch (IOException e) {
e.printStackTrace();
}
stage.show();
}
}
Screen2 Class:
package testproject.screen2;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import testproject.Main;
import java.io.IOException;
public class Screen2 {
public Screen2(Main main, Stage stage) {
FXMLLoader loader = new FXMLLoader();
try {
Parent root =
loader.load(getClass().getResourceAsStream("/testproject/screen2/screen2.fxml"));
stage.setTitle("Screen 2");
stage.setScene(new Scene(root, 300, 275));
}
catch (IOException e) {
e.printStackTrace();
}
stage.show();
}
}
Screen1Controller Class:
package testproject.screen1;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.VBox;
public class Screen1Controller {
#FXML
private AnchorPane Pane1;
#FXML
private VBox VBoxScreen1;
#FXML
private Button TransferButton;
#FXML
void transferToScreen2(MouseEvent event){
}
}
Screen2Controller Class:
package testproject.screen2;
import javafx.fxml.FXML;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
public class Screen2Controller {
#FXML
private AnchorPane Pane2;
#FXML
private HBox HBoxScreen2;
}
One way I can think of is to pass a callback/consumer to the screen2 controller to let it know what to do with its node. Having said that, there can be many other approaches to this requirement.
As per my view, you dont need separate classes to load the screens. You can check the below working demo.
Main.java
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader2 = new FXMLLoader(getClass() .getResource("screen2.fxml"));
VBox screen2 = loader2.load();
Screen2Controller screen2Controller = loader2.getController();
Stage screen2Stage = new Stage();
Scene scene2 = new Scene(screen2);
screen2Stage.setScene(scene2);
screen2Stage.setTitle("Screen 2");
screen2Stage.setX(900);
screen2Stage.setY(100);
screen2Stage.show();
FXMLLoader loader1 = new FXMLLoader(getClass() .getResource("screen1.fxml"));
VBox screen1 = loader1.load();
Screen1Controller screen1Controller = loader1.getController();
// Set a consumer to the screen1 to let it know what to do
screen1Controller.setTransferer(screen2Controller::moveNode);
Scene scene1 = new Scene(screen1);
primaryStage.setScene(scene1);
primaryStage.setTitle("Screen 1");
primaryStage.setX(100);
primaryStage.setY(100);
primaryStage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}
Screen1Controller.java
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import java.util.function.Consumer;
public class Screen1Controller {
#FXML
private VBox pane1;
#FXML
private VBox vBoxScreen1;
#FXML
private Button transferButton;
private Consumer<Node> transferer;
#FXML
void transferToScreen2(ActionEvent event) {
// First remove the node from the parent.
pane1.getChildren().remove(vBoxScreen1);
// Then send the node to do the other operation.
this.transferer.accept(vBoxScreen1);
}
public void setTransferer(Consumer<Node> transferer) {
this.transferer = transferer;
}
}
screen1.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?>
<VBox fx:id="pane1" xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="com.stackoverflow.javafx.issue7.Screen1Controller"
prefHeight="400.0" prefWidth="600.0" spacing="10">
<children>
<VBox fx:id="vBoxScreen1" spacing="10" style="-fx-border-width:2px;-fx-border-color:red;-fx-background-color:yellow;" prefWidth="200" maxWidth="200" prefHeight="200">
<children>
<Button text="Button 1"/>
<Button text="Button 2"/>
<Button text="Button 3"/>
</children>
<padding>
<Insets topRightBottomLeft="10" />
</padding>
</VBox>
<Button fx:id="transferButton" text="Transfer" onAction="#transferToScreen2"/>
</children>
<padding>
<Insets topRightBottomLeft="10" />
</padding>
</VBox>
Screen2Controller.java
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
public class Screen2Controller {
#FXML
private VBox pane2;
#FXML
private HBox hBoxScreen2;
public void moveNode(Node node){
hBoxScreen2.getChildren().add(node);
}
}
screen2.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?>
<VBox fx:id="pane2" xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="com.stackoverflow.javafx.issue7.Screen2Controller"
prefHeight="400.0" prefWidth="600.0" spacing="10">
<children>
<HBox fx:id="hBoxScreen2" spacing="10">
<padding>
<Insets topRightBottomLeft="10" />
</padding>
</HBox>
</children>
<padding>
<Insets topRightBottomLeft="10" />
</padding>
</VBox>
I want to use a Media View in Scene Builder such that if I click on a button that it should play a video from YouTube link that I gave it before. How would I go about doing this?
I don't want that a browser open when I click,I want that video playing in Media viewer field.
Here is a rather minimal example to get you started making use of Web View Control. It takes a YouTube URL and convert it into an embedded one.
Preview Image
I have provided two examples one as a complete single class and another using the scene builder.
Example 1: Complete Single class
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Main extends Application {
public class YTPlayer extends VBox{
private Button plyButton;
private WebView webView4 = new WebView();
private HBox hBox;
private TextArea textArea;
private String url = "";
private final Pattern pattern = Pattern.compile("(?<=watch\\?v=|/videos/|embed\\/|youtu.be\\/|\\/v\\/|\\/e\\/|watch\\?v%3D|watch\\?feature=player_embedded&v=|%2Fvideos%2F|embed%\u200C\u200B2F|youtu.be%2F|%2Fv%2F)[^#\\&\\?\\n]*");
public YTPlayer(double width, double height){
super();
this.setPrefHeight(height);
this.setPrefWidth(width);
hBox = new HBox();
hBox.setPrefHeight(0.045*height);
hBox.setPrefWidth(width);
textArea = new TextArea();
textArea.setPrefHeight(0.175*height);
textArea.setPrefWidth(0.913*width);
textArea.setPromptText("URL");
plyButton = new Button();
plyButton.setPrefHeight(0.095*height);
plyButton.setPrefWidth(0.0867*width);
plyButton.setText("Play");
plyButton.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
if(!textArea.getText().equals("")){
setUrl(textArea.getText());
play();
}
}
});
hBox.getChildren().addAll(plyButton,textArea);
webView4.setPrefHeight(0.9025*height);
webView4.setPrefWidth(width);
this.getChildren().addAll(webView4,hBox);
}
void play (){
webView4.getEngine().load(this.url);
}
void setUrl(String url){
Matcher matcher = pattern.matcher(url);
if(matcher.find()){
this.url ="https://www.youtube.com/embed/"+matcher.group(0);
System.out.println(this.url);
}else{
System.out.println("Invalid Url!");
}
}
}
#Override
public void start(Stage primaryStage) throws Exception{
double sceneWidth = 600;
double sceneHeight =400;
YTPlayer player =new YTPlayer(sceneWidth,sceneHeight);
Pane root = new Pane();
root.getChildren().addAll(player);
Scene myscene =new Scene(root, sceneWidth,sceneHeight);
primaryStage.setScene(myscene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Example 2: Using Scene Builder
FXML file (sample.fxml) :
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.web.*?>
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
<children>
<WebView fx:id="webView" prefHeight="361.0" prefWidth="600.0" />
<HBox prefHeight="18.0" prefWidth="600.0">
<children>
<Button mnemonicParsing="false" onAction="#play" prefHeight="38.0" prefWidth="52.0" text="Play" />
<TextArea fx:id="urlTxtArea" prefHeight="7.0" prefWidth="548.0" promptText="URL" />
</children>
</HBox>
</children>
</VBox>
Controller class (Controller.java):
import javafx.fxml.FXML;
import javafx.scene.control.TextArea;
import javafx.scene.web.WebView;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Controller {
private final Pattern pattern = Pattern.compile("(?<=watch\\?v=|/videos/|embed\\/|youtu.be\\/|\\/v\\/|\\/e\\/|watch\\?v%3D|watch\\?feature=player_embedded&v=|%2Fvideos%2F|embed%\u200C\u200B2F|youtu.be%2F|%2Fv%2F)[^#\\&\\?\\n]*");
private String url;
#FXML
private WebView webView;
#FXML
private TextArea urlTxtArea;
#FXML
void play() {
if(!urlTxtArea.getText().equals("")){
Matcher matcher = pattern.matcher(this.urlTxtArea.getText());
if(matcher.find()){
this.url ="https://www.youtube.com/embed/"+matcher.group(0);
webView.getEngine().load(this.url);
System.out.println(this.url);
}else{
System.out.println("Invalid Url!");
}
}
}
}
Main class (Main.java):
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{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Simple Youtube video Player");
primaryStage.setScene(new Scene(root, 600, 400));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I have a simple two tab application build around JavaFXML and scenebuilder. The tabs do nothing at present because I cannot get past a nullpointer exception when trying to load them.
The java and fxml files are arranged in a Netbeans project like this:
Main Application: The application main class MainApp.java sets the scene and is declared as follows:
package tabpane;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class MainApp extends Application {
private static Stage primaryStage;
private AnchorPane rootLayout;
public MainApp() {}
#Override
public void start(Stage primaryStage) {
primaryStage.initStyle(StageStyle.UNDECORATED);
MainApp.primaryStage = primaryStage;
MainApp.primaryStage.setTitle("Account Names");
initRootLayout();
}
public void initRootLayout() {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("view/MainView.fxml"));
rootLayout = (AnchorPane) loader.load();
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
Logger.getLogger(MainApp.class.getName()).log(Level.SEVERE, null, e);
System.out.println("MainApp: IOException in method: initRootLayout" + e.getMessage());
System.exit(0);
}
}
public Stage getPrimaryStage() {
return primaryStage;
}
public static void main(String[] args) {
launch(args);
}
}
The MainController class declares the two tabs, the Input tab and Account tab, for the application and an exit button for closing the program. This is the MainView. fxml file:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Tab?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="tabpane1.view.MainController">
<children>
<TabPane fx:id="tabPane" prefHeight="345.0" prefWidth="600.0" style="-fx-background-color: blue;" tabClosingPolicy="UNAVAILABLE">
<tabs>
<Tab fx:id="inputTab" closable="false" text="Input">
<content>
<fx:include source="InputView.fxml" />
</content>
</Tab>
<Tab fx:id="detailTab" closable="false" text="Detail">
<content>
<fx:include source="DetailView.fxml" />
</content>
</Tab>
</tabs>
</TabPane>
<Button fx:id="exitBtn" layoutX="529.0" layoutY="355.0" mnemonicParsing="false" onAction="#handleExitBtn" text="Exit" />
</children>
</AnchorPane>
This is the java controller class for the main view contained in MainController.java:
package tabpane1.view;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
public class MainController implements Initializable {
#FXML public TabPane tabPane;
#FXML public Tab inputTab;
#FXML public Tab accountTab;
#FXML private Button exitBtn;
public DetailController detailController;
public InputController inputController;
#Override
public void initialize(URL location, ResourceBundle resources) {
detailController = new FXMLLoader(getClass().getResource("DetailView.fxml")).getController();
// System.out.println(detailController.getClass());
inputController = new FXMLLoader(getClass().getResource("InputView.fxml")).getController();
// System.out.println(inputController.getClass());
}
#FXML private void handleExitBtn() {
System.exit(0);
}
}
The two println statements are simply trying to get the references to the two tabs, which are detailController and inputController, to do something. However, when these are uncommented and run, the following dump is output:
MainApp: IOException in method: initRootLayout
SEVERE: null
file:/G:/J2EE/TabPane_1/dist/run1314937364/TabPane_1.jar!/tabpane1/view/MainView.fxml
javafx.fxml.LoadException:
file:/G:/J2EE/TabPane_1/dist/run1314937364/TabPane_1.jar!/tabpane1/view/MainView.fxml
at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2601)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2579)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2441)
at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2409)
at tabpane1.MainApp.initRootLayout(MainApp.java:43)
at tabpane1.MainApp.start(MainApp.java:33)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$161(LauncherImpl.java:863)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$174(PlatformImpl.java:326)
at com.sun.javafx.application.PlatformImpl.lambda$null$172(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$173(PlatformImpl.java:294)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$147(WinApplication.java:177)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.NullPointerException
at tabpane1.view.MainController.initialize(MainController.java:24)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2548)
... 13 more
When the printlns are commented out, the outcome is as expected, that is the tab pane is correctly displayed:
As mentioned the detail and input controllers are empty, but for completeness they are as follows:
InputView.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="tabpane1.view.InputController">
</AnchorPane>
The controller InputController.java:
package tabpane1.view;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.Initializable;
public class InputController implements Initializable {
#Override
public void initialize(URL location, ResourceBundle resources) {}
}
DetailView.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" style="-fx-background-color: transparent;" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="tabpane1.view.DetailController" />
The controller DetailController.java:
package tabpane1.view;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.Initializable;
public class InputController implements Initializable {
#Override
public void initialize(URL location, ResourceBundle resources) {}
}
The way to get reference to the controller of an embedded FXML is described in this article.
You simply have to give an fx:id to the included resource:
<fx:include fx:id="IncludedView" source="InputView.fxml" />
Get a reference to IncludedView in the MainController:
#FXML private Parent IncludedView;
Get a reference to its controller simply by appending the word Controller in addition to the variable name of the embedded element:
#FXML private InputController IncludedViewController;
That is all.
Test it with:
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
public class MainController implements Initializable {
#FXML private Parent IncludedView;
#FXML private InputController IncludedViewController; // $IncludedView;+Controller
#Override
public void initialize(URL location, ResourceBundle resources) {
System.out.println(IncludedView.getClass());
System.out.println(IncludedViewController.getClass() );
}
#FXML private void handleExitBtn() {
System.exit(0);
}
}
An MRE for the sake of future readers:
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class GetEmbeddedController extends Application {
#Override
public void start(Stage primaryStage) {
try {
Pane root = (Pane) FXMLLoader.load(getClass().getResource("MainView.fxml"));
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(null);
}
}
MainView.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/10.0.1"
xmlns:fx="http://javafx.com/fxml/1" fx:controller="MainController">
<children>
<fx:include fx:id="includedView" source="IncludedView.fxml" />
<Button layoutX="401.0" layoutY="354.0" onAction="#handleTestBtn" text="Test Included Controller" />
</children>
</AnchorPane>
MainController.java
import javafx.fxml.FXML;
import javafx.scene.Parent;
public class MainController{
#FXML private Parent includedView; //not used in this demo
/*
* Get a reference to IncludedView controller simply by appending
* the word Controller in addition to the variable name of the embedded
* element:$IncludedView;+Controller
*/
#FXML private IncludedViewController includedViewController;
#FXML private void handleTestBtn() {
includedViewController.showAlert();
}
}
IncludedView.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Text?>
<AnchorPane prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/10.0.1"
xmlns:fx="http://javafx.com/fxml/1" fx:controller="IncludedViewController">
<children>
<Text layoutX="277.0" layoutY="196.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Included Fxml" />
</children>
</AnchorPane>
IncludedViewController.java
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
public class IncludedViewController{
void showAlert(){
Alert alert = new Alert(AlertType.INFORMATION);
alert.setContentText("Alert invoked by IncludedViewController ");
alert.show();
}
}
question is simple i have created small program that have 2 stages the main and the secondary, the main window has a button to launch the secondary stage and the secondary stage has text field and button and label when i enter a text in the text field and pressed the button the text will be shown in the label , what i want to do is when i close the secondary stage and the program is still running i want the data to be same so when pressed button and launch the secondary stage again i will see the last result as if i never closed the secondary stage
here is the main class
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.io.IOException;
public class Main extends Application {
private Stage primaryStage;
#Override
public void start(Stage primaryStage) throws Exception{
this.primaryStage=primaryStage;
mainWindow();
}
void mainWindow() {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("FirstWindow.fxml"));
Parent root = loader.load();
Controller controller = loader.getController();
controller.setMain(this ,primaryStage);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}catch (IOException e){
e.printStackTrace();
}
}
public void secondaryWindow() {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("SecondWindow.fxml"));
Parent root = loader.load();
Scene scene = new Scene(root);
Stage secondaryStage=new Stage();
secondaryStage.initOwner(primaryStage);
secondaryStage.initModality(Modality.WINDOW_MODAL);
swController controller = loader.getController();
controller.setMain(this,secondaryStage);
secondaryStage.setScene(scene);
secondaryStage.show();
}catch (IOException e){
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
and the secondstage control class
package sample;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
import java.net.URL;
import java.util.ResourceBundle;
/**
* Created by ahmednageeb on 11/29/16.
*/
public class swController implements Initializable {
private Main main;
private Stage secondaryStage;
public void setMain(Main main, Stage secondaryStage) {
this.main = main;
this.secondaryStage = secondaryStage;}
#FXML
private Label label;
#FXML
private TextField text;
#FXML
private void change(ActionEvent event) {
String a=text.getText();
label.setText(a);
text.clear();
}
public void goBack() {
secondaryStage.close();
}
#Override
public void initialize(URL location, ResourceBundle resources) {
}
}
and finally the firststage controller
package sample;
import javafx.stage.Stage;
public class Controller {
private Main main;
private Stage primaryStage;
public void setMain(Main main,Stage primaryStage){
this.main=main;
this.primaryStage =primaryStage;
}
public void close(){
primaryStage.close();
}
public void changeWindow(){
main.secondaryWindow();
}
}
One solution could be to load the secondary window on start up but hide it. Then use the button to show it. When you close it, hide it again.
See if something like this works!
Main:
package hidepopup;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class HidePopup extends Application {
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.setTitle("first stage");
stage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
First FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane id="AnchorPane" prefHeight="200" prefWidth="320" xmlns:fx="http://javafx.com/fxml/1" fx:controller="hidepopup.FXMLDocumentController">
<children>
<Button layoutX="126" layoutY="90" text="Click Me!" onAction="#handleButtonAction" fx:id="button" />
<Label layoutX="126" layoutY="120" minHeight="16" minWidth="69" fx:id="label" />
</children>
</AnchorPane>
First Controller:
package hidepopup;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;
public class FXMLDocumentController implements Initializable {
#FXML
private Label label;
FXMLLoader loader;
Parent root2;
Stage stage2;
#FXML
private void handleButtonAction(ActionEvent event) {
try
{
Scene scene2 = new Scene(root2);
stage2.setScene(scene2);
stage2.setTitle("second stage");
stage2.showAndWait();
}
catch(IllegalArgumentException ex)
{
stage2.show();
}
}
#Override
public void initialize(URL url, ResourceBundle rb) {
try
{
loader = new FXMLLoader(getClass().getResource("FXMLPopup.fxml"));
root2 = loader.load();
FXMLPopupController dac = (FXMLPopupController) loader.getController();
stage2 = new Stage();
}
catch (IOException ex)
{
Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
Second FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane id="AnchorPane" prefHeight="200" prefWidth="320" xmlns:fx="http://javafx.com/fxml/1" fx:controller="hidepopup.FXMLPopupController">
<children>
<Button layoutX="126" layoutY="90" text="Click Me!" onAction="#handleButtonAction" fx:id="button" />
<Label layoutX="126" layoutY="120" minHeight="16" minWidth="69" fx:id="label" />
</children>
</AnchorPane>
Second Controller
package hidepopup;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
public class FXMLPopupController implements Initializable {
#FXML
public Label label;
#FXML
private void handleButtonAction(ActionEvent event){
label.getScene().getWindow().hide();
}
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
}
The layout for both windows are exactly the same, so after you press the button you might want slide the top layout over. Also, I didn't test to see if the second stage held its data. Add a text box to the second stage and test it out. I hope this helps.