I have a desktop app that contains:
Main Class: that load the first fxml file -> SideBar.fxml
SideBar.fxml: contains BorderPane -> at the left of it, i create 2 buttons:
- the fist button: load sample fxml file
- the second button: load secondFxml file
sample.fxml: contains a tableView and a Button
secondFxml.fxml: contains a label
Controller: a class that control sample.fxml -> load random double values to a tableView
the issue is :
when i press the button (fill Table) in Pane 1 : it load the data to the tableView, untill now everything is going well
when i switch to the second pane and i return to the first pane the center border pane is reloaded again so the data of the tableView disappeared
what i want is when i return to the first pane the the table view stay as it was first
i try to hide the borderpane center but it doesn't work for me
i screenShot the issue:
Main:
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("SideBar.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 700, 500));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
SideBarController:
public class SideBarController implements Initializable {
#FXML BorderPane borderPane;
public void openPane1(ActionEvent event) throws Exception {
loadScene("Sample.fxml");
}
public void openPane2(ActionEvent event) throws Exception {
loadScene("secondFxml.fxml");
}
private void loadScene(String sc) throws IOException {
Parent root = FXMLLoader.load(getClass().getResource(sc));
borderPane.setCenter(root);
}
#Override
public void initialize(URL location, ResourceBundle resources) { }
}
Controller:
public class Controller implements Initializable {
double[][] data = new double[5][5];
Random random = new Random();
ObservableList<double[]> observableLists = FXCollections.observableArrayList();
#FXML
TableView<double []> tableView = new TableView<>(observableLists);
#FXML
public void fillTable(ActionEvent event) throws IOException {
//Random Values
for (int i = 0; i <data.length ; i++) {
for (int j = 0; j <data[0].length ; j++) {
data[i][j]= random.nextDouble();
}
}
//Add data to ObservableLists
for (int i = 0; i <data.length ; i++) {
observableLists.add(data[i]);
}
//Create Columns
for (int i = 0; i <data[0].length ; i++) {
TableColumn<double[], Double> column= null;
column = new TableColumn<>("column "+i);
int finalI = i;
column.setCellValueFactory(param -> new ReadOnlyObjectWrapper<>(param.getValue()[finalI]));
tableView.getColumns().add(column);
}
// Fill TableView
tableView.setItems(observableLists);
}
#Override
public void initialize(URL location, ResourceBundle resources) {
}
}
SideBar.fxml
<BorderPane fx:id="borderPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.SideBarController">
<left>
<VBox prefHeight="400.0" prefWidth="173.0" style="-fx-background-color: black;" BorderPane.alignment="CENTER">
<children>
<Button mnemonicParsing="false" onAction="#openPane1" prefHeight="25.0" prefWidth="177.0" style="-fx-background-color: blue; -fx-border-color: white;" text="Pane 1" textFill="WHITE">
<VBox.margin>
<Insets top="50.0" />
</VBox.margin>
<font>
<Font name="System Bold" size="17.0" />
</font>
</Button>
<Button mnemonicParsing="false" onAction="#openPane2" prefHeight="25.0" prefWidth="176.0" style="-fx-background-color: blue; -fx-border-color: white;" text="Pane 2" textFill="WHITE">
<VBox.margin>
<Insets top="10.0" />
</VBox.margin>
<font>
<Font name="System Bold" size="17.0" />
</font>
</Button>
</children>
</VBox>
</left>
<center>
<Pane prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
<children>
<Label layoutX="163.0" layoutY="152.0" prefHeight="68.0" prefWidth="131.0" text="Home">
<font>
<Font size="46.0" />
</font>
</Label>
</children>
</Pane>
</center>
</BorderPane>
Sample.fxml
<Pane prefHeight="395.0" prefWidth="597.0" style="-fx-background-color: white;" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
<children>
<TableView fx:id="tableView" layoutX="77.0" layoutY="47.0" prefHeight="266.0" prefWidth="461.0" />
<Button layoutX="257.0" layoutY="329.0" mnemonicParsing="false" onAction="#fillTable" text="fill Table" />
</children>
</Pane>
SecondFxml.fxml
<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
<children>
<Label layoutX="232.0" layoutY="153.0" text="Pane 2">
<font>
<Font size="46.0" />
</font>
</Label>
</children>
</Pane>
Do not reload from fxml when button is clicked. Do it once in initialize:
public class SideBarController implements Initializable {
#FXML BorderPane borderPane;
private Parent sample, secondFxml;
public void openPane1(ActionEvent event) throws Exception {
borderPane.setCenter(sample);
}
public void openPane2(ActionEvent event) throws Exception {
borderPane.setCenter(secondFxml);
}
private Parent loadScene(String sc) throws IOException {
return FXMLLoader.load(getClass().getResource(sc));
}
#Override
public void initialize(URL location, ResourceBundle resources) {
try {
sample = loadScene("Sample.fxml");
secondFxml = loadScene("secondFxml.fxml");
} catch (IOException ex) {
ex.printStackTrace();
};
}
}
I'm a tad confused with JavaFX at the minute. Basically, when I run my code, I can only click on a button on the side bar of the application once, then it will swap the center pane for the one that I want displayed. After that though, it appears that the ActionEvent does not trigger... I've tried reattaching them after handling but it doesn't work, and I have no idea whats wrong.
I've spent 2 days trying to crack this, and I'm sure it's something so annoyingly simple.
Controller:
#FXML private Button fooButton, barButton;
#FXML private Pane fooPane, barPane;
#FXML private BorderPane mainWindow;
#FXML private TabPane tabPane;
#FXML private VBox buttonBar;
#FXML private AnchorPane centerAP;
private HashMap<Button, Pane> buttonsPaneHMap = new HashMap<>(); //storing the data in a HashMap to create a link between buttons and their panes.
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
putNodesInHashmap();
assertControlsExist();
mainWindow.setCenter(welcomePane);
setOnActions(buttonsPaneHMap);
}
public final void handleButton(ActionEvent event) throws IOException {
Node newCenter = new AnchorPane();
if (event.getSource() == fooButton){
newCenter = FXMLLoader.load(getClass().getResource("/FXML/fooPane.fxml"));
}
if (event.getSource() == barButton){
newCenter = FXMLLoader.load(getClass().getResource("/FXML/barPane.fxml"));
}
try{
this.mainWindow.setCenter(newCenter);
}
catch (NullPointerException e){
e.printStackTrace();
}
}
public final void setOnActions(HashMap<Button, Pane> hMap){
for (Button button : hMap.keySet()){
((ButtonBase) button).setOnAction(arg0 -> {
try {
handleButton(arg0);
}
catch (Exception e) {
e.printStackTrace();
}
});
}
}
public final void putNodesInHashMap(){
buttonsPaneHMap.put(fooButton, fooPane);
buttonsPaneHMap.put(barButton, barPane);
}
FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import java.net.URL?>
<?import javafx.scene.Cursor?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Tab?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<BorderPane fx:id="mainWindow" prefHeight="461.0" prefWidth="760.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="guiControllers.MainController">
<top>
<Pane id="body" prefHeight="96.0" prefWidth="658.0" style="-fx-background-color: #243242; -fx-border-color: #0E141B; -fx-border-radius: 3;" stylesheets="#application.css" BorderPane.alignment="CENTER">
<children>
<Label layoutX="103.0" layoutY="25.0" prefHeight="48.0" prefWidth="394.0" text="Title Here" textFill="WHITE">
<font>
<Font name="Calibri Bold" size="41.0" />
</font>
</Label>
<ImageView fitHeight="55.0" fitWidth="61.0" layoutX="25.0" layoutY="20.0" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="#../Res/mhlogo.png" />
</image>
</ImageView>
</children>
</Pane>
</top>
<left>
<VBox id="buttonBar" fx:id="buttonBar" alignment="TOP_CENTER" prefHeight="365.0" prefWidth="168.0" style="-fx-background-color: #2E4055; -fx-border-radius: 3; -fx-border-color: #0E141B;" BorderPane.alignment="CENTER">
<children>
<Pane prefHeight="31.0" prefWidth="98.0">
<children>
<Pane layoutX="-1.0" layoutY="-2.0" prefHeight="33.0" prefWidth="169.0" style="-fx-background-color: #565656; -fx-border-color: #000000; -fx-border-radius: 20; -fx-background-radius: 20;">
<children>
<ImageView fitHeight="19.0" fitWidth="18.0" layoutX="7.0" layoutY="7.0" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="#../Res/magnifying-glass.png" />
</image>
</ImageView>
<TextField layoutX="29.0" layoutY="2.0" prefHeight="0.0" prefWidth="134.0" style="-fx-border-radius: 1; -fx-border-color: #111111; -fx-border-width: 2; -fx-background-color: #FFFFFF; -fx-background-radius: 20; -fx-border-radius: 20;" styleClass="stop-color-leaking" stylesheets="#../cSS/application.css" />
</children>
</Pane>
</children>
</Pane>
<Button id="fooButton" fx:id="fooButton" mnemonicParsing="false" onAction="#handleButton" prefHeight="31.0" prefWidth="171.0" style="-fx-background-color: #CDCDCD; -fx-border-color: #0E141B; -fx-border-radius: 3;" text="foo" />
<Button id="barButton" fx:id="barButton" mnemonicParsing="false" onAction="#handleButton" prefHeight="31.0" prefWidth="202.0" style="-fx-background-color: #CDCDCD; -fx-border-color: #0E141B; -fx-border-radius: 3;" text="bar" />
<children>
<ImageView id="settingsButton" fitHeight="38.0" fitWidth="48.0" layoutX="64.0" layoutY="130.0" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="#../Res/settings.png" />
</image>
</ImageView>
</children>
</AnchorPane>
</children>
</VBox>
</left>
<right>
<TabPane id="tabPane" fx:id="tabPane" focusTraversable="false" prefHeight="365.0" prefWidth="166.0" rotateGraphic="true" style="-fx-background-color: # #414760;" styleClass="tab-header-background" tabClosingPolicy="UNAVAILABLE" BorderPane.alignment="CENTER">
<tabs>
<Tab fx:id="notesTab" text="Notes">
<content>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="336.0" prefWidth="216.0" style="-fx-border-color: #414760; -fx-background-radius: 3;" styleClass="tab-header-background" stylesheets="#../application/CSS/application.css" />
</content>
</Tab>
<Tab fx:id="diagramTab" closable="false" text="Diagram" />
</tabs>
<cursor>
<Cursor fx:constant="DEFAULT" />
</cursor>
<stylesheets>
<URL value="#application.css" />
<URL value="#../application/CSS/application.css" />
</stylesheets>
</TabPane>
</right>
<center>
<AnchorPane fx:id="centerAP" style="-fx-background-color: #414760;" BorderPane.alignment="CENTER">
<children>
<VBox fx:id="welcomePane" prefHeight="304.0" prefWidth="391.0" style="-fx-background-color: #414760;">
<children>
<Pane fx:id="welcomePane" prefHeight="313.0" prefWidth="428.0" style="-fx-background-color: #414760;">
<children>
<ImageView fitHeight="183.0" fitWidth="296.0" layoutX="14.0" layoutY="65.0" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="#../Res/welcomepane.png" />
</image>
</ImageView>
<Label layoutX="141.0" layoutY="14.0" text="Welcome" textFill="WHITE">
<font>
<Font name="Calibri Bold" size="33.0" />
</font>
</Label>
<Label layoutX="82.0" layoutY="53.0" prefHeight="68.0" prefWidth="346.0" text="To start, please select an" textFill="WHITE" textOverrun="CLIP">
<font>
<Font name="Calibri" size="24.0" />
</font>
</Label>
<Label layoutX="82.0" layoutY="80.0" prefHeight="68.0" prefWidth="346.0" text="option from the left." textFill="WHITE" textOverrun="CLIP">
<font>
<Font name="Calibri" size="24.0" />
</font>
</Label>
</children>
</Pane>
</children>
</VBox>
</children>
</AnchorPane>
</center>
</BorderPane>
As far as I can tell all objects are injected correctly from the FXML, but once the center panel has switched, the side buttons no longer function (Though I can click any initially and it will load.
Footnote: The code above is slightly cut down and changed for readability.
I took an idea from Android's playbook.
If you know how to get to the node's parent and you know the node's fx:id, you can use this approach.
The full code loads different panes into the center of a Scene depending on which button is press. The code below is a sample that show how one pane is loaded. You can get any node using this idea if you know the node's parent, the node's fx:id and the node's type for casting.
Controller code
private void showSetupAccountScreen()
{
try
{
spCenterDisplay.getChildren().remove(0);//remove old display
BorderPane root = FXMLLoader.load(getClass().getResource("SubSetupAccount.fxml"));
spCenterDisplay.getChildren().add(root);//add new display
GridPane tempDisplay = (GridPane)root.getChildren().get(1);//get Parent of the nodes I will be using in this controller
loadQWERTYKeyboard();
TextField tfFirstName = (TextField)findNodeByID("tfFirstName", tempDisplay.getChildren());
TextField tfLastName = (TextField)findNodeByID("tfLastName", tempDisplay.getChildren());
TextField tfStreetAddress = (TextField)findNodeByID("tfStreetAddress", tempDisplay.getChildren());
TextField tfCity = (TextField)findNodeByID("tfCity", tempDisplay.getChildren());
TextField tfState = (TextField)findNodeByID("tfState", tempDisplay.getChildren());
TextField tfZip = (TextField)findNodeByID("tfZip", tempDisplay.getChildren());
TextField tfInitialDepositChecking = (TextField)findNodeByID("tfInitialDepositChecking", tempDisplay.getChildren());
TextField tfInitialDepositSavings = (TextField)findNodeByID("tfInitialDepositSavings", tempDisplay.getChildren());
ChoiceBox cbChecking = (ChoiceBox)findNodeByID("cbChecking", tempDisplay.getChildren());
cbChecking.getItems().addAll("No", "Yes");
cbChecking.setValue("No");
ChoiceBox cbSavings = (ChoiceBox)findNodeByID("cbSavings", tempDisplay.getChildren());
cbSavings.getItems().addAll("No", "Yes");
cbSavings.setValue("No");
if(true)//come back and check to make sure all info is in textfields
{
btnLeftOne.setOnAction((event) -> {
boolean createChecking = cbChecking.getValue().equals("Yes");
boolean createSavings = cbSavings.getValue().equals("Yes");
dbh.createNewAccount(tfFirstName.getText(), tfLastName.getText(), tfStreetAddress.getText(), tfCity.getText(),
tfState.getText(), tfZip.getText(), createChecking, Double.parseDouble(tfInitialDepositChecking.getText()),
createSavings, Double.parseDouble(tfInitialDepositSavings.getText()));
});
}
else
{
//create Alert
}
btnRightOne.setOnAction((event) -> {
cancelAccountCreation();
});
btnLeftTwo.setOnAction(null);
btnLeftThree.setOnAction(null);
btnLeftFour.setOnAction(null);
btnRightTwo.setOnAction(null);
btnRightThree.setOnAction(null);
btnRightFour.setOnAction(null);
}
catch (IOException ex)
{
Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex);
}
}
private void loadQWERTYKeyboard()
{
try
{
AnchorPane keyboardRoot = FXMLLoader.load(getClass().getResource("KeyboardQWERTY.fxml"));
System.out.println(keyboardRoot.getId());
spBottomDisplay.getChildren().add(keyboardRoot);
GridPane tempKeyboard = (GridPane)keyboardRoot.getChildren().get(0);
tempKeyboard.getChildren().stream().filter((tempNode)
-> (tempNode instanceof Button)).map((
tempNode) -> (Button) tempNode).forEachOrdered((tempButton) -> {
buttons.put(tempButton.getText().toLowerCase(), tempButton);
});
apMain.setOnKeyPressed((event) -> {
Button tempButton = buttons.get(event.getText());
if (tempButton != null) {
tempButton.arm();
tempButton.setStyle("-fx-background-color: blue");
}
else if (event.getCode().equals(KeyCode.ENTER)) {
tempButton = buttons.get("enter");
tempButton.arm();
tempButton.setStyle("-fx-background-color: blue");
}
else if (event.getCode().equals(KeyCode.BACK_SPACE)) {
tempButton = buttons.get("backspace");
tempButton.arm();
tempButton.setStyle("-fx-background-color: blue");
}
else if (event.getCode().equals(KeyCode.SPACE)) {
tempButton = buttons.get("space");
tempButton.arm();
tempButton.setStyle("-fx-background-color: blue");
}
});
apMain.setOnKeyReleased((event) -> {
System.out.println();
Button tempButton = buttons.get(event.getText());
System.out.println("Released key text: " + event.getText());
System.out.println("Released key code: " + event.getCode());
if (tempButton != null) {
tempButton.disarm();
tempButton.setStyle("");
}
else if (event.getCode().equals(KeyCode.ENTER)) {
tempButton = buttons.get("enter");
tempButton.disarm();
tempButton.setStyle("");
}
else if (event.getCode().equals(KeyCode.BACK_SPACE)) {
tempButton = buttons.get("backspace");
tempButton.disarm();
tempButton.setStyle("");
}
else if (event.getCode().equals(KeyCode.SPACE)) {
tempButton = buttons.get("space");
tempButton.disarm();
tempButton.setStyle("");
}
});
}
catch (IOException ex)
{
Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex);
}
}
private Node findNodeByID(String id, ObservableList<Node> observableList)
{
for(Node node : observableList)
{
if(node.getId().equals(id))
{
System.out.println("node found!");
return node;
}
else
{
System.out.println("node not found yet!");
}
}
return null;
}
In this snippet of code I use two different approaches. In the loadQWERTYKeyboard methods is one approach. In the findNodeByID is the second approach. The full code is found here. Working but project not complete.
This answer is similar to the code you posted. This answer uses the same two ideas talked about in the original answer.
Main
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
/**
*
* #author blj0011
*/
public class JavaFXApplication75 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
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane id="AnchorPane" prefHeight="449.0" prefWidth="564.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="javafxapplication75.FXMLDocumentController">
<children>
<Button fx:id="btnMainBar" layoutX="76.0" layoutY="391.0" onAction="#handleButtonAction" text="Load Bar" />
<Label fx:id="label" layoutX="126" layoutY="120" minHeight="16" minWidth="69" />
<Button fx:id="btnMainFoo" layoutX="419.0" layoutY="391.0" mnemonicParsing="false" onAction="#handleButtonAction" text="Load Foo" />
<AnchorPane fx:id="apMain" layoutX="161.0" layoutY="88.0" prefHeight="200.0" prefWidth="200.0" AnchorPane.bottomAnchor="100.0" AnchorPane.leftAnchor="50.0" AnchorPane.rightAnchor="50.0" AnchorPane.topAnchor="25.0" />
</children>
</AnchorPane>
Controller
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane;
/**
*
* #author blj0011
*/
public class FXMLDocumentController implements Initializable
{
#FXML AnchorPane apMain;//This pane will be used to display the other two panes depending on which button is pressed.
//Foo Pane children nodes
Button btnFooOne, btnFooTwo;
TextField tfFooOne, tfFooTwo;
Label lblFoo;
//Bar Pane children nodes
Button btnBar;
TextField tfBar;
Label lblBar;
#FXML
private void handleButtonAction(ActionEvent event)
{
if(((Button)event.getSource()).getId().equals("btnMainBar"))
{
loadBarPane();
}
else if(((Button)event.getSource()).getId().equals("btnMainFoo"))
{
loadFooPane();
}
}
#Override
public void initialize(URL url, ResourceBundle rb)
{
// TODO
}
//This approach uses the findNodeByID method.
private void loadFooPane()
{
try
{
if(apMain.getChildren().size() > 0)
{
apMain.getChildren().remove(0);//if a node is loaded into apMain, remove the node.
}
Pane root = FXMLLoader.load(getClass().getResource("Foo.fxml"));
apMain.getChildren().add(root);//Add Foo Pane to apMain
//Get Foo Pane's children nodes
tfFooOne = (TextField)findNodeByID("tfFooOne", root.getChildren());
tfFooTwo = (TextField)findNodeByID("tfFooTwo", root.getChildren());
btnFooOne = (Button)findNodeByID("btnFooOne", root.getChildren());
btnFooTwo = (Button)findNodeByID("btnFooTwo", root.getChildren());
lblFoo = (Label)findNodeByID("lblFoo", root.getChildren());
//Set Listeners
tfFooOne.textProperty().addListener((obserValue, oldValue, newValue) -> {
lblFoo.setText("new value: " + newValue);
});
tfFooTwo.textProperty().addListener((obserValue, oldValue, newValue) -> {
lblFoo.setText("new value: " + newValue);
});
btnFooOne.setOnAction((event) -> {lblFoo.setText("You pressed btnFooOne");});
btnFooTwo.setOnAction((event) -> {lblFoo.setText("You pressed btnFooTwo");});
}
catch (IOException ex)
{
Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex);
}
}
private void loadBarPane()
{
try
{
//This way is more complicate if you have more than one of the same type of node
if(apMain.getChildren().size() > 0)
{
apMain.getChildren().remove(0);//if a node is loaded into apMain, remove the node.
}
Pane root = FXMLLoader.load(getClass().getResource("Bar.fxml"));
apMain.getChildren().add(root);//Add Foo Pane to apMain
//Get Bar Pane's children nodes
for(Node node : root.getChildren())
{
if(node instanceof Button)
{
btnBar = (Button)node;
}
else if(node instanceof TextField)
{
tfBar = (TextField)node;
}
else if(node instanceof Label)
{
lblBar = (Label)node;
}
}
//Set listeners
tfBar.textProperty().addListener((obserValue, oldValue, newValue) -> {
lblBar.setText("new value: " + newValue);
});
btnBar.setOnAction((event) -> {lblBar.setText("You pressed btnFooOne");});
}
catch (IOException ex)
{
Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex);
}
}
private Node findNodeByID(String id, ObservableList<Node> observableList)
{
for(Node node : observableList)
{
if(node.getId().equals(id))
{
System.out.println("node found!");
return node;
}
else
{
System.out.println("node not found yet!");
}
}
return null;
}
}
Foo FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.Pane?>
<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="324.0" prefWidth="464.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1">
<children>
<Button fx:id="btnFooOne" layoutX="84.0" layoutY="209.0" mnemonicParsing="false" text="Button" />
<Button fx:id="btnFooTwo" layoutX="329.0" layoutY="209.0" mnemonicParsing="false" text="Button" />
<TextField fx:id="tfFooOne" layoutX="36.0" layoutY="162.0" />
<TextField fx:id="tfFooTwo" layoutX="281.0" layoutY="162.0" />
<Label fx:id="lblFoo" layoutX="165.0" layoutY="39.0" text="You just loaded Foo Pane" />
</children>
</Pane>
Bar FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.Pane?>
<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="324.0" prefWidth="464.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1">
<children>
<Button fx:id="btnBar" layoutX="206.0" layoutY="186.0" mnemonicParsing="false" text="Button" />
<TextField fx:id="tfBar" layoutX="158.0" layoutY="130.0" />
<Label fx:id="lblBar" layoutX="160.0" layoutY="65.0" text="You just loaded Bar Pane" />
</children>
</Pane>
I am trying to make a GUI for a application and I have run into a problem when trying to change items after the fxmlloader loaded a file in.
At the start I thought it was that I wanted to edit the items before they were initialised (the rendering of the file was still going and i tried to edit the items before that was done). But then I tried to make a timer that every second (for testing purposes such a long time) checked the state of the object and if the object wasn't null it would do something.
However I noticed that the items never stop being null and thus I can't edit them. Anyone has a idea how to change this? I get this problem with Text and GridPane objects so far but not with buttons :/
The controller (the part that matters):
protected void sceneSetter(String filename) {
//loads the FXMLFile of the right sceneand makes the scene 1280x720 (720p)
try {
root = FXMLLoader.load(getClass().getResource(filename));
} catch (IOException e) {
System.out.println("RIP scene setting");
}
Scene scene = new Scene(root, 1280, 720);
//sets the right scene and displays it
stage.setScene(scene);
stage.show();
if(filename.equals("/InterfaceUserChange.fxml")){
final Timer timer = new Timer(1000, null);
timer.addActionListener(new ActionListener() {
#Override
public void actionPerformed(java.awt.event.ActionEvent e) {
System.out.println("tried it");
if(firstLoginContainer != null){
TextField field = new TextField() {
#Override public void replaceText(int start, int end, String text) {
// If the replaced text would end up being invalid, then simply
// ignore this call!
if (!text.matches("[0-9]*")) {
super.replaceText(start, end, text);
}
}
#Override public void replaceSelection(String text) {
if (!text.matches("[0-9]*")) {
super.replaceSelection(text);
}
}
};
firstLoginContainer.add(field , 1 , 1);
timer.stop();
}
}
});
if(firstLoginContainer == null) {
timer.start();
}
System.out.println("textfield added?");
}
The fxml file (its in a borderpane):
<left>
<VBox fx:id="firstLoginPaneContainer">
<HBox styleClass="profilePane">
<VBox styleClass="pictureDummy">
<ImageView>
<Image url="#testpica200px.jpg"/>
</ImageView>
</VBox>
<GridPane styleClass="firstLoginContainer"
alignment="TOP_LEFT" hgap="10" vgap="10">
<padding>
<Insets top="25" right="25" bottom="10" left="25"/>
</padding>
<Text text="Name: "
GridPane.rowIndex="0" GridPane.columnIndex="0"/>
<TextField fx:id="changeName"
GridPane.rowIndex="0" GridPane.columnIndex="1"/>
<Text text="Age: "
GridPane.rowIndex="1" GridPane.columnIndex="0"/>
<Text text="Study: "
GridPane.rowIndex="2" GridPane.columnIndex="0"/>
<Text text="Courses: "
GridPane.rowIndex="3" GridPane.columnIndex="0"/>
<Text text="Preferences: " fx:id="selfPreferences"
GridPane.rowIndex="4" GridPane.columnIndex="0"/>
<Text text="E-mail: " fx:id="changeEmail"
GridPane.rowIndex="5" GridPane.columnIndex="0"/>
<Text text="Phone number: " fx:id="selfPhonenumber"
GridPane.rowIndex="6" GridPane.columnIndex="0"/>
</GridPane>
</HBox>
</VBox>
</left>
I am trying to change the data of the StackedAreaChart dynamically. So I have created an fxml file and a controller for it.
fig.fxml
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="495.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.gollahalli.test.controller">
<children>
<StackedAreaChart fx:id="graph" layoutX="24.0" layoutY="95.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0">
<xAxis>
<NumberAxis side="BOTTOM" />
</xAxis>
<yAxis>
<NumberAxis side="LEFT" />
</yAxis>
</StackedAreaChart>
<TextField fx:id="number" layoutX="57.0" layoutY="39.0" />
<Button fx:id="button" layoutX="330.0" layoutY="39.0" mnemonicParsing="false" text="Button" />
</children>
</AnchorPane>
Controller.java
public class controller {
#FXML
private Button button;
#FXML
private TextField number;
#FXML
private StackedAreaChart<Number, Number> graph;
public void initialize(){
XYChart.Series<Number, Number> series = new XYChart.Series<Number, Number>();
button.setOnAction(event -> {
int number1 = Integer.parseInt(number.getText());
System.out.println(number1);
for (int i = 0; i < number1; i++) {
series.getData().addAll(new XYChart.Data(i, i));
}
graph.getData().add(series);
});
}
}
When I enter a number say 100, I am able to get the graph correctly, when I change the data say 101 and click on the button I get an error as Exception in thread "JavaFX Application Thread" java.lang.IllegalArgumentException: Duplicate series added.
I do know that to overcome this I would have to use ObservableList, but I am not sure how to use it.
Now the question is, how should I change/refresh the data every time I click on the button?
The exception message is clear. To avoid it add series only once:
public void initialize(){
XYChart.Series<Number, Number> series = new XYChart.Series<Number, Number>();
// add series only once at init
graph.getData().add(series);
button.setOnAction(event -> {
int number1 = Integer.parseInt(number.getText());
System.out.println(number1);
// clear current data
series.getData().clear();
// add new data
for (int i = 0; i < number1; i++) {
series.getData().add(new XYChart.Data(i, i));
}
});
}
I'm writing a custom control which displays an error icon and a message in a tooltip if the validation in a form fails. My version without the custom control looks like this:
<HBox>
<TextField fx:id="name"></TextField>
<Label fx:id="error" focusTraversable="false" visible="false">
<graphic>
<ImageView fitHeight="24.0" fitWidth="24.0" pickOnBounds="true" preserveRatio="true"/>
</graphic>
<tooltip>
<Tooltip fx:id="errorTooltip"/>
</tooltip>
</Label>
</HBox>
The result is this:
My efforts to create a custom control lead to this:
<fx:root type="javafx.scene.layout.HBox" xmlns:fx="http://javafx.com/fxml">
<children/>
<Label fx:id="error" focusTraversable="false" visible="false">
<graphic>
<ImageView fitHeight="24.0" fitWidth="24.0" pickOnBounds="true" preserveRatio="true"/>
</graphic>
<tooltip>
<Tooltip fx:id="errorToolTip"/>
</tooltip>
</Label>
</fx:root>
This is the code behind the fxml:
package control;
[imports omitted for brevity]
#DefaultProperty(value = "children")
public final class ValidatedControl extends HBox implements Initializable {
#FXML
private Label error;
#FXML
private Tooltip errorToolTip;
private StringProperty errorToolTipProperty;
public ValidatedControl() {
final FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("ValidatedControl.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}
public void setErrorToolTip(final String errorToolTip) {
this.getErrorToolTipProperty().setValue(errorToolTip);
}
public String getErrorToolTip() {
return this.getErrorToolTipProperty().getValueSafe();
}
#Override
public void initialize(final URL location, final ResourceBundle resources) {
this.errorToolTip.textProperty().bind(this.getErrorToolTipProperty());
this.error.visibleProperty().bind(this.getErrorToolTipProperty().isNotEmpty());
}
public StringProperty getErrorToolTipProperty() {
if (this.errorToolTipProperty == null) {
this.errorToolTipProperty = new SimpleStringProperty();
}
return this.errorToolTipProperty;
}
}
I can use the control in fxml but the child component I add is always the last child which means the error icon is displayed to its left.
My control is used like this:
<ValidatedControl>
<TextField>
</TextField>
</ValidatedControl>
How do I get it to display the icon on the right side?
Now I do understand your problem. This might not fix your problem when you add your ValidatedControl in FXML, but when you do it programmatically try this:
ValidatedControl vc = new ValidatedControl();
TextField textField = new TextField();
vc.getChildren().add(textField);
textField.toBack();
Another way would be to go ItachiUchiha's way and add a Pane in your FXML as first child. But instead of overwriting the getChildren() method, write a new Method addNode(Node n) and add the node to the Pane.
forget about my first answer ;)