package Data_Project;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
public class Main extends Application {
public Stage window;
#Override
public void start(Stage primaryStage) throws Exception{
window = primaryStage;
//Scene1
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
Parent root1 = FXMLLoader.load(getClass().getResource("Admin.fxml"));
Controller a1 = new Controller();
a1.getSubmitButton().setOnAction(e -> {
window.setScene(new Scene(root1,500,500));
});
window.setTitle("Log in");
window.setScene(new Scene(root,500,500));
window.show();
}
public static void main(String[] args) throws IOException {
launch(args);
}
}
Hey there, im getting error with this code, I can't figure out what's wrong.
Exception in Application start method Exception in thread "main"
java.lang.RuntimeException: Exception in Application start method at
com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:875)
at
com.sun.javafx.application.LauncherImpl.lambda$launchApplication$147(LauncherImpl.java:157)
at
com.sun.javafx.application.LauncherImpl$$Lambda$1/989110044.run(Unknown
Source) at java.lang.Thread.run(Thread.java:745) Caused by:
java.lang.NullPointerException at
Data_Project.Main.start(Main.java:20)
XML:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<GridPane alignment="center" hgap="10" vgap="10" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Data_Project.Controller">
<children>
<Label text="Username" GridPane.columnIndex="1" GridPane.rowIndex="1" />
<TextField fx:id="userField" GridPane.columnIndex="2" GridPane.rowIndex="1" />
<Label text="Password" GridPane.columnIndex="1" GridPane.rowIndex="3" />
<PasswordField fx:id="passField" GridPane.columnIndex="2" GridPane.columnSpan="1" GridPane.rowIndex="3" />
<Button fx:id="submitButton" onAction="#submitForm" text="Log in" GridPane.columnIndex="2" GridPane.columnSpan="2" GridPane.rowIndex="4" />
<Label fx:id="errorLabel" textFill="#c30808" GridPane.columnIndex="2" GridPane.columnSpan="2" GridPane.rowIndex="6" />
</children>
</GridPane>
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<GridPane alignment="center" hgap="10" vgap="10" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Data_Project.Admin">
<children>
<Label text="Username" GridPane.columnIndex="1" GridPane.rowIndex="1" />
<TextField fx:id="userField" GridPane.columnIndex="2" GridPane.rowIndex="1" />
<Label text="Password" GridPane.columnIndex="1" GridPane.rowIndex="3" />
<PasswordField fx:id="passField" GridPane.columnIndex="2" GridPane.columnSpan="1" GridPane.rowIndex="3" />
<Button fx:id="submitButton" text="Log in" GridPane.columnIndex="2" GridPane.columnSpan="2" GridPane.rowIndex="4" />
<Label fx:id="errorLabel" textFill="#c30808" GridPane.columnIndex="2" GridPane.columnSpan="2" GridPane.rowIndex="6" />
</children>
</GridPane>
The XML are both Different Files, but shown here as 1 file, they have the same attributes and all because it was for testing the scene switcher, but for some reason i can't get it to work!
Controller class:
package Data_Project;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.scene.control.*;
public class Controller{
Scene window;
#FXML Button submitButton;
#FXML TextField userField;
#FXML PasswordField passField;
#FXML Label errorLabel;
public void submitForm(ActionEvent actionEvent) {
authorizedUser user = new authorizedUser();
if(!user.checkCredentials(userField.getText(), passField.getText()))
{
errorLabel.setText("Invalid credentials");
}
}
public Button getSubmitButton() {
return submitButton;
}
}
Admin:
package Data_Project;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.scene.control.*;
public class Admin{
Scene window;
#FXML Button submitButton;
#FXML TextField userField;
#FXML PasswordField passField;
#FXML Label errorLabel;
public Button getSubmitButton() {
return submitButton;
}
}
The FXMLLoader parses the FXML file, creates an instance of the controller class (if one is specified) and injects any #FXML-annotated fields into that controller instance.
If you create your own controller instance, as you do with
Controller a1 = new Controller();
then the FXMLLoader knows nothing of that instance (how would it?) and so the #FXML-annotated fields are not initialized. Hence when you call
a1.getSubmitButton()
it returns null and so you get a NullPointerException.
To fix this, replace
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
and
Controller a1 = new Controller();
with
FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
Parent root = loader.load();
Controller a1 = loader.getController();
This gives you a reference to the controller instance that the FXMLLoader created, so it has its #FXML-annotated fields properly initialized.
Related
I'm working on a desktop application using javaFX, I'm using scene builder version 11 to create my interface that contains TextFields and ChoiceBox, my inputs are supposed to be set in Arabic. The problem is when I retrieve the text from the TextFiled or the ChoiceBox and print it in the console it shows characters like that "بننلنلن" and it also generates a problem to store the inputs in database, this is the SQLException:
java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'بننلنلن,نبلنبنلب,2021-06-30,أمر جزائي,تم التبليغ
When I tried another project without scene builder I got the Arabic outputs correctly, could scene bulider be the problem's origin?
here is my fxml file:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ChoiceBox?>
<?import javafx.scene.control.DatePicker?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<AnchorPane id="AnchorIns" prefHeight="529.0" prefWidth="523.0" stylesheets="#newinsc.css" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="newInsc.NewInscController">
<children>
<VBox fx:id="boxContainer" layoutX="139.0" layoutY="99.0" prefHeight="273.0" prefWidth="245.0">
<children>
<HBox alignment="CENTER_RIGHT" prefHeight="42.0" prefWidth="245.0">
<children>
<TextField fx:id="firstNameTxtField" alignment="CENTER_RIGHT" />
<Label alignment="CENTER_RIGHT" contentDisplay="RIGHT" prefHeight="17.0" prefWidth="71.0" stylesheets="#newinsc.css" text="الاسم" />
</children>
</HBox>
<HBox alignment="CENTER_RIGHT" prefHeight="42.0" prefWidth="245.0">
<children>
<TextField fx:id="lastNameTxtField" alignment="CENTER_RIGHT" />
<Label prefHeight="17.0" prefWidth="71.0" text="اللقب" />
</children>
</HBox>
<HBox alignment="CENTER_RIGHT" prefHeight="42.0" prefWidth="245.0">
<children>
<ChoiceBox fx:id="docTypeChoice" prefWidth="150.0" />
<Label prefHeight="17.0" prefWidth="71.0" text="نوع التبليغ" />
</children>
</HBox>
<HBox alignment="CENTER_RIGHT" prefHeight="42.0" prefWidth="245.0">
<children>
<DatePicker fx:id="dateField" prefHeight="25.0" prefWidth="149.0" />
<Label prefHeight="17.0" prefWidth="71.0" text="التاريخ" />
</children>
</HBox>
<HBox alignment="CENTER_RIGHT" prefHeight="42.0" prefWidth="245.0">
<children>
<ChoiceBox fx:id="tablighCase" prefWidth="150.0" />
<Label prefHeight="17.0" prefWidth="71.0" text="حالة التبليغ" />
</children>
</HBox>
<HBox alignment="CENTER" onMouseClicked="#cancelBtnClicked" prefHeight="46.0" prefWidth="245.0">
<children>
<Button fx:id="cancleBtn" mnemonicParsing="false" onMouseClicked="#cancelBtnClicked" text="الغاء">
<HBox.margin>
<Insets right="16.0" />
</HBox.margin>
</Button>
<Button fx:id="saveInscBtn" mnemonicParsing="false" onMouseClicked="#registerBtnClicked" text="تسجيل" />
</children>
</HBox>
</children>
</VBox>
</children>
</AnchorPane>
and this here is my controller file:
package newInsc;
import DB.dbConnection;
import java.net.URL;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.time.LocalDate;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.DatePicker;
import javafx.scene.control.TextField;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
/**
* FXML Controller class
*
* #author asus
*/
public class NewInscController implements Initializable {
#FXML
private ChoiceBox<String> docTypeChoice;
#FXML
private ChoiceBox<String> tablighCase;
#FXML
private DatePicker dateField;
#FXML
private Button saveInscBtn;
#FXML
private TextField firstNameTxtField;
#FXML
private TextField lastNameTxtField;
#FXML
private Button cancleBtn;
private Connection c;
private Statement s;
#FXML
private VBox boxContainer;
#Override
public void initialize(URL url, ResourceBundle rb) {
docTypeChoice.getItems().add("أمر جزائي");
docTypeChoice.getItems().add("حكم جزائي" );
docTypeChoice.getItems().add("تكليف بالحضور" );
docTypeChoice.getItems().add("قرار جزائي" );
tablighCase.getItems().add("تم التبليغ");
tablighCase.getItems().add("ترك اشعار");
tablighCase.getItems().add("عدم التمكن من التبليغ");
}
#FXML
private void cancelBtnClicked(MouseEvent event) {
Stage s = (Stage) cancleBtn.getScene().getWindow();
}
#FXML
private void registerBtnClicked(MouseEvent event) {
try {
String query;
String fname = firstNameTxtField.getText();
String lname = lastNameTxtField.getText();
LocalDate date = dateField.getValue();
String docType = docTypeChoice.getValue();
String state = tablighCase.getValue();
query = "insert into infotable values " + fname + "," + lname + "," + date+ ","+ docType+"," +state ;
dbConnection dbc = new dbConnection();
s = dbc.createConnection().createStatement();
s.execute(query);
s.close();
} catch (SQLException ex) {
Logger.getLogger(NewInscController.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
I could find a solution, before I run the program I first compile it with F9 key then I run it with a key combination SHIFT + F6
So I have to build this JavaFX application really fast, my code compiles yet the GUI doesn't start and I get exceptions. The problem starts as soon as the FileChooser code is implemented.
public class Main extends Application {
#FXML // fx:id="openButton"
private Button openButton; // Value injected by FXMLLoader
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("Plot.fxml"));
openButton.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent arg0) {
FileChooser fileChooser = new FileChooser();
FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("TXT files (*.txt)", "*.txt");
fileChooser.getExtensionFilters().add(extFilter);
File file = fileChooser.showOpenDialog(primaryStage);
System.out.println(file);
}
});
primaryStage.setTitle("Plotter");
primaryStage.setScene(new Scene(root, 1024 , 768));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
The FXML file is as such:
<BorderPane prefHeight="400.0" prefWidth="723.0" xmlns="http://javafx.com/javafx/null" xmlns:fx="http://javafx.com/fxml/1">
<top>
<HBox alignment="TOP_CENTER" prefHeight="50.0" spacing="10.0" BorderPane.alignment="CENTER">
<children>
<Button fx:id="openButton" mnemonicParsing="false" prefHeight="31.0" prefWidth="150.0" text="Open Dataset" />
<Button mnemonicParsing="false" prefHeight="31.0" prefWidth="150.0" text="Button" />
<Button mnemonicParsing="false" prefHeight="31.0" prefWidth="150.0" text="Button" />
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
<BorderPane.margin>
<Insets />
</BorderPane.margin>
</HBox>
</top>
</BorderPane>
I am completely novice to JavaFX. Any tips is appreciated. P.S. I am using the Gluon scene builder.
Thanks.
The Exceptions:
Exception in Application start method Exception in thread "main"
java.lang.RuntimeException: Exception in Application start method at
com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:917)
at
com.sun.javafx.application.LauncherImpl.lambda$launchApplication$155(LauncherImpl.java:182)
at java.lang.Thread.run(Thread.java:745) Caused by:
java.lang.NullPointerException at sample.Main.start(Main.java:29) at
com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$162(LauncherImpl.java:863)
at
com.sun.javafx.application.PlatformImpl.lambda$runAndWait$175(PlatformImpl.java:326)
at
com.sun.javafx.application.PlatformImpl.lambda$null$173(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method) at
com.sun.javafx.application.PlatformImpl.lambda$runLater$174(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$148(WinApplication.java:191)
... 1 more
Process finished with exit code 1
Answer is to separate the Controller and Application class, so that you don't end up with two instances of the Application class. Otherwise one instance of the Application class will not have some members initialized and will end up throwing null pointer exceptions.
package plot;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.stage.FileChooser;
import java.io.File;
public class Controller {
#FXML // fx:id="openButton"
private Button openButton; // Value injected by FXMLLoader
#FXML
public void open(ActionEvent e) {
FileChooser fileChooser = new FileChooser();
FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("TXT files (*.txt)", "*.txt");
fileChooser.getExtensionFilters().add(extFilter);
File file = fileChooser.showOpenDialog(openButton.getScene().getWindow());
System.out.println(file);
}
}
package plot;
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 stage) throws Exception{
FXMLLoader loader = new FXMLLoader(getClass().getResource("Plot.fxml"));
Parent root = loader.load();
stage.setTitle("Plotter");
stage.setScene(new Scene(root, 1024 , 768));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.Button?>
<?import javafx.geometry.Insets?>
<BorderPane prefHeight="400.0" prefWidth="723.0" xmlns="http://javafx.com/javafx/null"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="plot.Controller"
>
<top>
<HBox alignment="TOP_CENTER" prefHeight="50.0" spacing="10.0" BorderPane.alignment="CENTER">
<children>
<Button fx:id="openButton" mnemonicParsing="false" prefHeight="31.0" prefWidth="150.0" text="Open Dataset" onAction="#open"/>
<Button mnemonicParsing="false" prefHeight="31.0" prefWidth="150.0" text="Button"/>
<Button mnemonicParsing="false" prefHeight="31.0" prefWidth="150.0" text="Button"/>
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
</padding>
<BorderPane.margin>
<Insets/>
</BorderPane.margin>
</HBox>
</top>
</BorderPane>
See related (I advise the first link be studied to understand why this solution works).
Javafx - Can application class be the controller class
What is a NullPointerException, and how do I fix it?
How do i center this image horizontally? I've tried putting in a HBox like so, however it does not work...
<HBox alignment="CENTER">
<ImageView fitWidth="300" preserveRatio="true">
<image>
<Image url="#Logo.png"/>
</image>
</ImageView>
</HBox>
Here a little MCVE with a FXML file:
Application:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
/**
*
* #author Naoghuman
*/
public class CenterImageInHBox 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.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
FXML file:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.image.*?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="center.image.in.hbox.FXMLDocumentController">
<children>
<VBox layoutX="60.0" prefHeight="200.0" prefWidth="100.0" spacing="7.0" style="-fx-background-color: YELLOWGREEN;" AnchorPane.bottomAnchor="14.0" AnchorPane.leftAnchor="14.0" AnchorPane.rightAnchor="14.0" AnchorPane.topAnchor="14.0">
<children>
<ToolBar prefHeight="40.0" prefWidth="200.0">
<items>
<Button mnemonicParsing="false" onAction="#onActionLeft" text="Left" />
<Button mnemonicParsing="false" onAction="#onActionCenter" text="Center" />
<Button mnemonicParsing="false" onAction="#onActionRight" text="Right" />
</items>
</ToolBar>
<HBox fx:id="hbImage" alignment="TOP_CENTER" VBox.vgrow="ALWAYS">
<children>
<ImageView fx:id="ivImage" fitHeight="128.0" fitWidth="128.0" pickOnBounds="true" preserveRatio="true" />
</children>
</HBox>
</children>
</VBox>
</children>
</AnchorPane>
and the Controller:
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.geometry.Pos;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
/**
*
* #author Naoghuman
*/
public class FXMLDocumentController implements Initializable {
#FXML private HBox hbImage;
#FXML private ImageView ivImage;
#Override
public void initialize(URL url, ResourceBundle rb) {
Image img = new Image("http://icons.iconarchive.com/icons/evermor-design/galaxian/128/Movie-Clip-icon.png");
ivImage.setImage(img);
}
public void onActionCenter() {
hbImage.setAlignment(Pos.TOP_CENTER);
}
public void onActionLeft() {
hbImage.setAlignment(Pos.TOP_LEFT);
}
public void onActionRight() {
hbImage.setAlignment(Pos.TOP_RIGHT);
}
}
When you click on the buttons in the application then you will see that the image is aligned left, center or right depends from your the choosen action.
Well, my question is simple.
I am building an application with Java Fx, and I have a TabPane.
When I open a new Tab, that Tab gets it's content by a fxml file.
tab.setContent((Node)FXMLLoader.load(this.getClass().getResource("/main/textEditor.fxml")))
Fine, that works well, it always loads the same file on all Tabs, so, all textarea on all tabs have the same id.
The problem is, with java, I can only get the information of the textarea of the first Tab.
How can i edit specifically the textarea of one tab in particular?
An example of what i want to do :
Main
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Rectangle2D;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Screen;
import javafx.stage.Stage;
public class Main extends Application{
public static void main(String[] args) {
Application.launch(Main.class, args);
}
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("/tabPane/test.fxml"));
Screen screen = Screen.getPrimary();
Rectangle2D bounds = screen.getVisualBounds();
stage.setScene(new Scene(root));
stage.show();
}
}
test.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.net.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.layout.AnchorPane?>
<VBox prefHeight="400.0" prefWidth="640.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="tabPane.controller">
<children>
<MenuBar VBox.vgrow="NEVER">
<menus>
<Menu mnemonicParsing="false" onAction="#test" text="File">
<items>
<MenuItem fx:id="insert" mnemonicParsing="false" onAction="#test" text="Insert" />
</items>
</Menu>
</menus>
</MenuBar>
<AnchorPane maxHeight="-1.0" maxWidth="-1.0" prefHeight="-1.0" prefWidth="-1.0" VBox.vgrow="ALWAYS">
<children>
<Label alignment="CENTER" layoutX="155.0" layoutY="177.0" style="
" text="Drag components from Library here…" textAlignment="CENTER" textFill="#9f9f9f" wrapText="false">
<font>
<Font size="18.0" />
</font>
</Label>
<TabPane fx:id="tabPane" prefHeight="375.0" prefWidth="640.0" tabClosingPolicy="UNAVAILABLE">
<tabs>
<Tab text="Untitled Tab 1">
<content>
<TextArea fx:id="textarea" prefHeight="200.0" prefWidth="200.0" text="a" />
</content>
</Tab>
<Tab text="Untitled Tab 2">
<content>
<TextArea fx:id="textarea1" prefHeight="200.0" prefWidth="200.0" text="a" />
</content>
</Tab>
</tabs>
</TabPane>
</children>
</AnchorPane>
</children>
<stylesheets>
<URL value="#../../../../BasicApplicatio11n_css/BasicApplication.css" />
controller.java
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TextArea;
public class controller implements Initializable{
#FXML
private TextArea textarea;
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
}
public void test(ActionEvent event){
textarea.appendText("Text");
}
}
There are two tabs on this example, when the button is pressed, I want to add the text on the current selected tab.
There are a few different ways to do this. One way is to let the controller for the tab content expose the textProperty from the text area. Then in your "main controller", create a StringProperty field. When you create a new tab, just observe its selected property and update the string property field to point to the one from the current controller. Then you can easily load text into the "current" pane.
Here's a simple example of this:
EditorController:
import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
import javafx.scene.control.TextArea;
public class EditorController {
#FXML
private TextArea textArea ;
public StringProperty textProperty() {
return textArea.textProperty() ;
}
}
with textEditor.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.TextArea?>
<BorderPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="EditorController">
<center>
<TextArea fx:id="textArea"/>
</center>
</BorderPane>
And then the MainController could look like:
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.stage.FileChooser;
public class MainController {
#FXML
private TabPane tabPane ;
private StringProperty currentText ;
public void initialize() throws IOException {
// load an initial tab:
newTab();
}
#FXML
private void newTab() throws IOException {
Tab tab = new Tab("Untitled text");
FXMLLoader loader = new FXMLLoader(getClass().getResource("textEditor.fxml"));
tab.setContent(loader.load());
EditorController controller = loader.getController() ;
tab.selectedProperty().addListener((obs, wasSelected, isNowSelected) -> {
if (isNowSelected) {
currentText = controller.textProperty();
}
});
tabPane.getTabs().add(tab);
tabPane.getSelectionModel().select(tab);
}
#FXML
private void load() {
FileChooser chooser = new FileChooser();
File file = chooser.showOpenDialog(tabPane.getScene().getWindow());
if (file != null) {
Path path = file.toPath();
try {
currentText.set(String.join("\n", Files.readAllLines(path)));
} catch (IOException e) {
Alert alert = new Alert(AlertType.ERROR);
alert.setContentText("Unable to load file "+path);
alert.setTitle("Load error");
alert.showAndWait();
}
tabPane.getSelectionModel().getSelectedItem().setText(path.getFileName().toString());
}
}
}
with main.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.control.Button?>
<?import javafx.geometry.Insets?>
<BorderPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="MainController">
<center>
<TabPane fx:id="tabPane"/>
</center>
<bottom>
<HBox alignment="CENTER" spacing="5">
<padding>
<Insets top="5" right="5" left="5" bottom="5" />
</padding>
<Button text="Load..." onAction="#load" />
<Button text="New" onAction="#newTab"/>
</HBox>
</bottom>
</BorderPane>
For completeness, a simple application class:
import java.io.IOException;
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 IOException {
Parent root = FXMLLoader.load(getClass().getResource("main.fxml"));
Scene scene = new Scene(root, 800, 800);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
There are other approaches, e.g. you could just change the button's onAction property when the tab selection changes, etc.
I've been asked to write a conversion program in JavaFX, but i need to allow the user to set different options depending on the conversion direction.
In reaction to swapping the conversion direction, I need to show two different (unique) sets of controls for options relating to the current direction only.
In one direction I need to display two TextFields, in the other direction, a pair of RadioButtons. I could show both at the same time and just enable/disable when needed, but I'm trying for a less cluttered approach first.
I'm looking for a solution that has similar layout-switching functionality to Qt's StackedWidget that I've used in C++, so i can swap out the TextFields for the RadioButtons and vice versa depending on the conversion direction.
It's important to note that this window has many other options that are common to both directions, so it's only a small part that needs to change according to the conversion direction. Thus I'd prefer it if I could easily access the swapped controls from within the same controller.
I don't want tabs or page numbers as the user controls the direction elsewhere, so TabPane and Pagination are out, unless those undesirable pieces of functionality can be disabled.
I've heard that there's something called a CardLayout in another Java framework (it's in awt if i heard right) which would do the job I want, what's the JavaFX 8 equivalent? Or is there another solution that i should be using instead?
I'm using SceneBuilder so ideally something i can implement in that, but I can use pure code if need be.
You can use any Pane subclass (e.g. a StackPane) and call pane.getChildren().setAll(textFieldDisplay); or pane.getChildren().setAll(radioButtonDisplay); as needed. The different displays can be any kind of Node, but since they hold other controls they would typically also be some subclass of Pane. In the example below I use a GridPane for one and a VBox for the other. In a real application, you might define each one in its own FXML file and load them independently, etc.
Complete example (using FXML):
Main.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<VBox xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.MainController"
alignment="CENTER">
<padding>
<Insets top="20" left="20" right="20" bottom="20" />
</padding>
<CheckBox text="Show Text Fields" fx:id="showTextFields" VBox.vgrow="NEVER">
<VBox.margin>
<Insets top="10" left="10" right="10" bottom="10"/>
</VBox.margin>
</CheckBox>
<StackPane fx:id="display" VBox.vgrow="ALWAYS" />
<Button text="OK" onAction="#submit" VBox.vgrow="NEVER" />
</VBox>
MainController.java:
package application;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.control.CheckBox;
import javafx.scene.layout.StackPane;
public class MainController {
#FXML
private CheckBox showTextFields ;
#FXML
private StackPane display ;
private Node radioDisplay ;
private Node textFieldDisplay ;
private RadioButtonController radioButtonController ;
private TextFieldController textFieldController ;
public void initialize() throws Exception {
FXMLLoader radioDisplayLoader = new FXMLLoader(getClass().getResource("RadioDisplay.fxml"));
radioDisplay = radioDisplayLoader.load();
radioButtonController = radioDisplayLoader.getController();
FXMLLoader textFieldDisplayLoader = new FXMLLoader(getClass().getResource("TextFieldDisplay.fxml"));
textFieldDisplay = textFieldDisplayLoader.load();
textFieldController = textFieldDisplayLoader.getController();
showTextFields.selectedProperty().addListener((obs, wasSelected, isSelected) -> {
if (isSelected) {
display.getChildren().setAll(textFieldDisplay);
} else {
display.getChildren().setAll(radioDisplay);
}
});
display.getChildren().add(radioDisplay);
}
#FXML
private void submit() {
if (showTextFields.isSelected()) {
System.out.println("Value 1 is "+ textFieldController.getText1());
System.out.println("Value 2 is "+ textFieldController.getText2());
} else {
System.out.println("Chosen value is "+radioButtonController.getSelectedItem());
}
}
}
RadioDisplay.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.VBox?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.RadioButton?>
<VBox xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.RadioButtonController"
alignment="TOP_CENTER" spacing="10">
<padding>
<Insets top="10" left="10" right="10" bottom="10"/>
</padding>
<RadioButton text="Choice 1" selected="true" fx:id="choice1"/>
<RadioButton text="Choice 2" fx:id="choice2"/>
</VBox>
RadioButtonController.java:
package application;
import javafx.fxml.FXML;
import javafx.scene.control.RadioButton;
import javafx.scene.control.ToggleGroup;
public class RadioButtonController {
#FXML
private RadioButton choice1 ;
#FXML
private RadioButton choice2 ;
public void initialize() {
ToggleGroup toggleGroup = new ToggleGroup();
choice1.setToggleGroup(toggleGroup);
choice2.setToggleGroup(toggleGroup);
}
public String getSelectedItem() {
if (choice1.isSelected()) {
return "Choice 1";
} else if (choice2.isSelected()) {
return "Choice 2";
} else return "" ;
}
}
TextFieldDisplay.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<GridPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.TextFieldController"
hgap="10" vgap="10">
<columnConstraints>
<ColumnConstraints hgrow="NEVER" halignment="RIGHT"/>
<ColumnConstraints hgrow="SOMETIMES" />
</columnConstraints>
<Label text="Value 1:" GridPane.columnIndex="0" GridPane.rowIndex="0"/>
<Label text="Value 2:" GridPane.columnIndex="0" GridPane.rowIndex="1"/>
<TextField fx:id="textField1" GridPane.columnIndex="1" GridPane.rowIndex="0"/>
<TextField fx:id="textField2" GridPane.columnIndex="1" GridPane.rowIndex="1"/>
</GridPane>
TextFieldController.java:
package application;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;
public class TextFieldController {
#FXML
private TextField textField1 ;
#FXML
private TextField textField2 ;
public String getText1() {
return textField1.getText() ;
}
public String getText2() {
return textField2.getText();
}
}
Main.java:
package application;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.fxml.FXMLLoader;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
VBox root = (VBox)FXMLLoader.load(getClass().getResource("Main.fxml"));
Scene scene = new Scene(root,400,400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
All the FXML files are in the same package (application) as the .java files.
Update
If you prefer not to "modularize" it to this extent, you can put everything in a single FXML with a single controller. In that case, Main.fxml looks like
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.RadioButton?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<VBox xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.MainController"
alignment="CENTER">
<padding>
<Insets top="20" left="20" right="20" bottom="20" />
</padding>
<CheckBox text="Show Text Fields" fx:id="showTextFields" VBox.vgrow="NEVER">
<VBox.margin>
<Insets top="10" left="10" right="10" bottom="10"/>
</VBox.margin>
</CheckBox>
<StackPane fx:id="display" VBox.vgrow="ALWAYS">
<VBox fx:id="radioDisplay" alignment="TOP_CENTER" spacing="10">
<padding>
<Insets top="10" left="10" right="10" bottom="10" />
</padding>
<RadioButton text="Choice 1" selected="true" fx:id="choice1" />
<RadioButton text="Choice 2" fx:id="choice2" />
</VBox>
<fx:define>
<GridPane fx:id="textFieldDisplay" hgap="10" vgap="10">
<columnConstraints>
<ColumnConstraints hgrow="NEVER" halignment="RIGHT" />
<ColumnConstraints hgrow="SOMETIMES" />
</columnConstraints>
<Label text="Value 1:" GridPane.columnIndex="0"
GridPane.rowIndex="0" />
<Label text="Value 2:" GridPane.columnIndex="0"
GridPane.rowIndex="1" />
<TextField fx:id="textField1" GridPane.columnIndex="1"
GridPane.rowIndex="0" />
<TextField fx:id="textField2" GridPane.columnIndex="1"
GridPane.rowIndex="1" />
</GridPane>
</fx:define>
</StackPane>
<Button text="OK" onAction="#submit" VBox.vgrow="NEVER" />
</VBox>
and the corresponding controller is
package application;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.CheckBox;
import javafx.scene.control.RadioButton;
import javafx.scene.control.TextField;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.StackPane;
public class MainController {
#FXML
private CheckBox showTextFields ;
#FXML
private StackPane display ;
#FXML
private Node radioDisplay ;
#FXML
private Node textFieldDisplay ;
#FXML
private TextField textField1 ;
#FXML
private TextField textField2 ;
#FXML
private RadioButton choice1 ;
#FXML
private RadioButton choice2 ;
public void initialize() throws Exception {
showTextFields.selectedProperty().addListener((obs, wasSelected, isSelected) -> {
if (isSelected) {
display.getChildren().setAll(textFieldDisplay);
} else {
display.getChildren().setAll(radioDisplay);
}
});
ToggleGroup toggleGroup = new ToggleGroup();
choice1.setToggleGroup(toggleGroup);
choice2.setToggleGroup(toggleGroup);
}
#FXML
private void submit() {
if (showTextFields.isSelected()) {
System.out.println("Value 1 is "+ textField1.getText());
System.out.println("Value 2 is "+ textField2.getText());
} else {
String chosenValue ;
if (choice1.isSelected()) {
chosenValue = "Choice 1";
} else if (choice2.isSelected()) {
chosenValue = "Choice 2";
} else {
chosenValue
= "None";
}
System.out.println("Chosen value is "+chosenValue);
}
}
}
(and in this case, remove the other two FXML files and their controllers).