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>
Related
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 have a TableView in my fxml. Within the controller this table is mapped with the #FXML annotation.
#FXML
TableView<BankMovement> table;
I also set, in fxml document, for table element
onMouseReleased = "#handleRowSelect"
finally always in the controller I have the following method
#FXML
private void handleRowSelect(MouseEvent event){
Document row = table.getSelectionModel().getSelectedItem();
if (row == null) return;
if(row != temp){
temp = row;
lastClickTime = new Date();
} else if(row == temp) {
Date now = new Date();
long diff = now.getTime() - lastClickTime.getTime();
if (diff < 300){
System.out.println("Edit dialog");
System.out.print(row.getDescription());
} else {
lastClickTime = new Date();
System.out.print(row.getFile());
}
}
}
here instead is where to bind the data to table
#Override
public void initializeTable() {
table.setItems(subList());
colId.setCellValueFactory(new PropertyValueFactory<>("idDocument"));
colIdCategory.setCellValueFactory(d -> new SimpleStringProperty(d.getValue().getCategory().getName()));
colIdCategoryChild.setCellValueFactory(d -> new SimpleStringProperty(d.getValue().getCategoryChild().getName()));
colIdDocument.setCellFactory(getFileCellFactory());
colIdDocument.setCellValueFactory(new PropertyValueFactory<>("file"));
colIdDescription.setCellValueFactory(new PropertyValueFactory<>("description"));
colIdDate.setCellValueFactory(d -> new SimpleStringProperty(Utils.localTimeToItalianDate(d.getValue().getDocumentDate())));
}
Even if I click on the table I never enter the handleRowSelect method.
I have the impression that when I click on the row the click on the cell is managed and not on the row. As if the event were not propagated.
This is fxml
<AnchorPane minWidth="-Infinity" prefHeight="900.0" prefWidth="1510.0" styleClass="background-white" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="MyController">
<children>
<VBox prefHeight="886.0" prefWidth="1465.0" style="-fx-min-width: 100%;" AnchorPane.bottomAnchor="4.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="330.0" AnchorPane.topAnchor="10.0">
<children>
<TitledPane animated="false" minHeight="-Infinity" minWidth="-Infinity" prefHeight="880.0" prefWidth="1465.0" text="%app.pane.document">
<content>
<VBox minWidth="-Infinity" prefHeight="815.0" prefWidth="1474.0">
<children>
<TableView onMouseReleased="#handleRowSelect" fx:id="table" minHeight="-Infinity" minWidth="-Infinity" prefHeight="746.0" prefWidth="1450.0">
.......
<padding>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
</padding>
</TableView>
<Pagination fx:id="pagination" prefHeight="73.0" prefWidth="1450.0" />
</children>
</VBox>
</content>
</TitledPane>
</children>
</VBox>
</children>
</AnchorPane>
I have an desktop application based on javafx.The only requirement is that it has to run continuously for 5-6 days.I am in testing phase.
UI component used in this:
6 Gauges (Medusa)
Line chart that is stacked on JFXDrawer and is controlled by a hamburger-button.
Out of 6 gauges 3 gauges and line chart has to be updated on a period of a sec. For this i have used a thread which update this component per second.
I have tested this application for almost 3 time:
For Three days
For 26 hours(In which graph is working fine and values are updating in the gauges but the gauge needle is stuck at a particular value)
For 24 hours now the gauge needle is moving but gauge value is stuck and the Line chart drawer is not open only the hamburger-icon is changing.
There is no Exception in log and the UI is not hanging, Just Ui components are not responsive.
I did profiling but everything seems OK.I have read this post also but in his case the ui is hanging and i have no issue regarding this, all the button are clicky and i am able to change the screens with not issue.
After reloading the Screen All gets back to normal.
sample code i've used to update the component.
Controller
public class testGaugeController implements Initializable {
// Component
private static final int MAX_DATA_POINTS = 1000;
private int xSeriesData = 0;
private final XYChart.Series<Number, Number> series1 = new XYChart.Series<>();
private ExecutorService executor;
private final ConcurrentLinkedQueue<Number> dataQ1 = new ConcurrentLinkedQueue<>();
private NumberAxis xAxis,yAxis;
LineChart<Number, Number> lineChart;
DatabaseHandler dh = new DatabaseHandler();
Connection connect = dh.MakeConnection();
#FXML
private JFXDrawer drawer;
#FXML
private JFXHamburger burger;
#FXML
private Gauge Gauge;
/**
* Initializes the controller class.
*
* #param url
* #param rb
*/
public void initialize(URL url, ResourceBundle rb) throws IOException {
initializeRecorder();
start_recording();
}
private void initializeRecorder() throws IOException {
try {
xAxis = new NumberAxis(0, MAX_DATA_POINTS, MAX_DATA_POINTS / 100);
xAxis.setForceZeroInRange(false);
xAxis.setAutoRanging(true);
xAxis.setTickLabelsVisible(true);
xAxis.setTickMarkVisible(true);
xAxis.setMinorTickVisible(true);
yAxis = new NumberAxis();
// Create a LineChart
lineChart = new LineChart<Number, Number>(xAxis, yAxis) {
// Override to remove symbols on each data point
#Override
protected void dataItemAdded(XYChart.Series<Number, Number> series, int itemIndex, XYChart.Data<Number, Number> item) {
}
};
lineChart.setAnimated(false);
lineChart.setTitle("");
lineChart.setHorizontalGridLinesVisible(true);
series1.setName("Test Value");
lineChart.getData().addAll(series1);
drawer.setSidePane(lineChart);
drawer.setOverLayVisible(false);
} catch (Exception e) {
}
HamburgerBackArrowBasicTransition burgermove = new HamburgerBackArrowBasicTransition(burger);
burgermove.setRate(-1);
burger.addEventHandler(MouseEvent.MOUSE_PRESSED, (evt) -> {
burgermove.setRate(burgermove.getRate() * -1);
burgermove.play();
if (drawer.isShown()) {
drawer.close();
} else {
drawer.open();
}
});
}
int count_executer_status = 0;
boolean initial_start_trend = true;
private void start_recording() {
if (initial_start_trend) {
initial_start_trend = false;
} else {
xSeriesData = 0;
System.out.println("Clearing dataQue");
dataQ1.clear();
series1.getData().clear();
}
xAxis.setLowerBound(0);
count_executer_status++;
System.out.println("Cleared dataQue");
executor = Executors.newCachedThreadPool((Runnable r) -> {
Thread thread = new Thread(r);
thread.setDaemon(true);
return thread;
});
count_executer_status = 0;
AddToQueue addToQueue = new AddToQueue();
executor.execute(addToQueue);
//-- Prepare Timeline
prepareTimeline();
}
private class AddToQueue implements Runnable {
String query = "SELECT test_value FROM test_data_reader ORDER BY test_data_reader_id DESC LIMIT 1";
ResultSet rs;
#Override
public void run() {
try {
// add a item of random data to queue.
rs = dh.getData(query, connect);
if (rs.next()) {
double test_value = Double.parseDouble(rs.getString("test_value"));
dataQ1.add(test_value);
String Record_data = "INSERT INTO `test_data_record` (`test_value`, `date_time`) VALUES( '" + rs.getString("test_value") + "', NOW());";
dh.execute(Record_data, connect);
Platform.runLater(() -> {
Gauge.setValue(test_value);
});
}
xaxis_count++;
Thread.sleep(1000);
executor.execute(this);
} catch (Exception ex) {
}
}
}}
//-- Timeline gets called in the JavaFX Main thread
private void prepareTimeline() {
// Every frame to take any data from queue and add to chart
new AnimationTimer() {
#Override
public void handle(long now) {
addDataToSeries();
}
}.start();
}
int xaxis_count = 0;
private void addDataToSeries() {
try {
for (int i = 0; i < 20; i++) {
//-- add 20 numbers to the plot+
if (dataQ1.isEmpty()) {
break;
}
series1.getData().add(new XYChart.Data<>(xaxis_count, dataQ1.remove()));
}
if (series1.getData().size() > MAX_DATA_POINTS) {
series1.getData().remove(0, series1.getData().size() - MAX_DATA_POINTS);
}
// update
xAxis.setLowerBound(xSeriesData - MAX_DATA_POINTS);
xAxis.setUpperBound(xSeriesData - 1);
} catch (Exception e) {
}
}
FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import com.jfoenix.controls.JFXDrawer?>
<?import com.jfoenix.controls.JFXHamburger?>
<?import eu.hansolo.medusa.Gauge?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="e913_300mt.testGaugeController">
<children>
<VBox prefHeight="400.0" prefWidth="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<HBox fx:id="sectionHeader" minHeight="-Infinity" prefHeight="50.0" prefWidth="600.0" style="-fx-background-color: #2F333E;">
<children>
<VBox alignment="CENTER" layoutX="10.0" layoutY="10.0" HBox.hgrow="ALWAYS" />
<VBox alignment="CENTER" HBox.hgrow="ALWAYS" />
<VBox alignment="CENTER" HBox.hgrow="ALWAYS" />
<VBox alignment="CENTER" HBox.hgrow="ALWAYS" />
<VBox alignment="CENTER" HBox.hgrow="ALWAYS">
<children>
<JFXHamburger fx:id="burger" />
</children>
</VBox>
</children>
</HBox>
<HBox prefHeight="100.0" prefWidth="200.0" VBox.vgrow="ALWAYS">
<children>
<Gauge fx:id="gauge" autoScale="false" borderPaint="#0099ff" borderWidth="3.0" decimals="0" foregroundPaint="#0000000b" highlightSections="true" innerShadowEnabled="true" knobType="METAL" lcdDesign="RED" lcdFont="LCD" majorTickMarkType="PILL" majorTickSpace="250.0" markersVisible="true" maxValue="3000.0" mediumTickMarkType="TRAPEZOID" mediumTickMarksVisible="false" minorTickSpace="50.0" needleSize="THIN" needleType="VARIOMETER" shadowsEnabled="true" threshold="300.0" title="Test Gauge" unit="unit" HBox.hgrow="ALWAYS">
<HBox.margin>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
</HBox.margin>
</Gauge>
</children>
</HBox>
</children>
</VBox>
<JFXDrawer fx:id="drawer" defaultDrawerSize="600.0" direction="RIGHT" layoutY="56.0" prefHeight="344.0" prefWidth="600.0" />
</children>
</AnchorPane>
Libraries
jfoenix-8.0.1-for-java8.jar Github
fontawesomefx-8.9.jar bitbucket
Medusa-8.0.jar jar-download
For testing purpose i am only updating one gauge.And system Configuration is :
OS : Ubuntu 18.04 lts
Processor : Intel core i5-8400
Ram : 8Gb
Any Idea on this.
I'm currently working on an application where I use a TableView including RadioButtons in one TableCell. For that I created an own RadioButtonCell class. I also have a "Add new Row"-Button to let the user add some additional rows. After clicking the "Add" button a third time, I get more RadioButtonCells than rows. I don't find the mistake in my code.
Here a screenshot after clicking the Button a third time:
RadioButtonCell:
import java.util.EnumSet;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Pos;
import javafx.scene.control.RadioButton;
import javafx.scene.control.TableCell;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.HBox;
public class RadioButtonCell<S,T extends Enum<T>> extends TableCell<S,T>{
private EnumSet<T> enumeration;
public RadioButtonCell(EnumSet<T> enumeration) {
this.enumeration = enumeration;
}
#Override
protected void updateItem(T item, boolean empty)
{
super.updateItem(item, empty);
if (!empty)
{
// GUI
HBox hb = new HBox(7);
hb.setAlignment(Pos.CENTER);
final ToggleGroup group = new ToggleGroup();
// create a radio button for each 'element' of the enumeration
for (Enum<T> enumElement : enumeration) {
RadioButton radioButton = new RadioButton(enumElement.toString());
radioButton.setUserData(enumElement);
radioButton.setToggleGroup(group);
hb.getChildren().add(radioButton);
if (enumElement.equals(item)) {
radioButton.setSelected(true);
}
}
// issue events on change of the selected radio button
group.selectedToggleProperty().addListener(new ChangeListener<Toggle>() {
#SuppressWarnings("unchecked")
#Override
public void changed(ObservableValue<? extends Toggle> observable,
Toggle oldValue, Toggle newValue) {
getTableView().edit(getIndex(), getTableColumn());
RadioButtonCell.this.commitEdit((T) newValue.getUserData());
}
});
setGraphic(hb);
}
}
}
Model:
public class Points {
private final SimpleObjectProperty<Participation> participation = new SimpleObjectProperty<Participation>(); // radio buttons
public static enum Participation {
NONE, HALO, BORDER;
public String toString() {
return super.toString().toLowerCase();
};
}
/**
* Constructor.
* #param <Participation>
*/
public Points(Participation p) {
this.participation.setValue(p);
public void setParticipation(Participation p){
participation.set(p);
}
public Participation getParticipation(){
return participation.get();
}
public SimpleObjectProperty<Participation> ParticipationProperty() {
return participation;
}
}
FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.SplitPane?>
<?import javafx.scene.control.Tab?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<VBox prefHeight="900.0" prefWidth="1250.0" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1" fx:controller="address.view.DataOverviewController">
<children>
<SplitPane fx:id="splitPaneVertical" prefHeight="776.0" prefWidth="1200.0" VBox.vgrow="ALWAYS">
<items>
<TabPane fx:id="tabPane" stylesheets="#Theme.css">
<tabs>
<Tab text="Points">
<content>
<SplitPane fx:id="splitPaneHorizontal" dividerPositions="0.949874686716792" orientation="VERTICAL" stylesheets="#Theme.css">
<items>
<TableView fx:id="pointsDataTable">
<columns>
<TableColumn fx:id="pointsBackgroundColumn" prefWidth="200.0" resizable="false" text="Background" />
</columns>
</TableView>
<AnchorPane SplitPane.resizableWithParent="false">
<children>
<HBox fx:id="pointsHBoxButton" alignment="CENTER" prefHeight="35.0" prefWidth="1016.0" spacing="20.0" stylesheets="#Theme.css" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<Button fx:id="pointsAddButton" mnemonicParsing="false" onAction="#handleAddPoints" text="Add" />
</children>
</HBox>
</children>
</AnchorPane>
</items>
</SplitPane>
</content>
</Tab>
</tabs>
</TabPane>
</items>
</SplitPane>
Controller:
public class DataOverviewController {
#FXML
private TableView<Points> pointsDataTable;
#FXML
private TableColumn<Points, Participation> pointsBackgroundColumn;
#FXML
private Button pointsButtonAdd;
public DataOverviewController() {
}
#FXML
private void initialize() {
// select multipe rows, make rows editable
pointsDataTable.setEditable(true);
pointsDataTable.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
linesDataTable.setEditable(true);
pointsBackgroundColumn.setCellFactory((param) -> new RadioButtonCell<Points, Participation>(EnumSet.allOf(Participation.class)));
pointsBackgroundColumn.setCellValueFactory(new PropertyValueFactory<Points, Participation>("Participation"));
pointsBackgroundColumn.setOnEditCommit(
new EventHandler<CellEditEvent<Points, Participation>>() {
#Override
public void handle(CellEditEvent<Points, Participation> t) {
((Points) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setParticipation(t.getNewValue());
}
}
);
public void setMainConfig(MainConfig mainConfig) {
// Add observable list data to the table
pointsDataTable.setItems(mainConfig.getTableDataPoints());
}
#FXML
private void handleAddPoints() {
// create new record and add it to the tableview
Points dataPoints = new Points(0, "Points", "TabFileName.tab",Participation.NONE, false, false, 18, "new");
pointsDataTable.getItems().add(dataPoints);
}
}
}
I reduced my code to the important parts. Maybe someone can help?
Thanks.
Items can be added as well as removed from TableCells. This means you need to handle the case where the TableCell becomes empty by undoing any changes done to the cell when a item was added. Otherwise there may be TableCells that look as if they contain a item although they are actually empty.
A updateItem method should look like this:
#Override
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
// modifications to restore the empty state
} else {
// customized way of displaying the item
}
}
Furthermore I do not recommend recreating the Nodes for displaying the item in the cell every time the item is changed, since this means you'll have a lot of unnecessary node creations which is the thing TableView is trying to avoid by reusing cells. Store them in fields and keep them for later use instead.
im building a stop watch with a digital and analog clock but having trouble with printing the digital clock value mainly because im unsure how to pass the string to the FXML.
why is it that when i try to us id="digitalText" for the textfield in the fxml the program fails but if i were to write it using a text="00:00" it will work properly(just with a text with 00:00 though so no updating the digital clock of course). i would have hoped giving it the id would print the value inside it.
the controller
package tds7xbstopwatchfxml;
import java.awt.TextField;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.util.Duration;
public class FXMLDocumentController implements Initializable {
#FXML
private ImageView clockFace;
#FXML
private ImageView hand;
#FXML
private TextField digitalText;
String digital;
Double rotation = 0.0;
Integer ms = 00;
Integer mm = 00;
Timeline timeline = new Timeline(
new KeyFrame(Duration.millis(1000), (ActionEvent actionEvent) -> {
updateTimer();
updateDigital();
})
);
#FXML
private void stopButton(ActionEvent event) {
timeline.stop();
}
#FXML
private void startButton(ActionEvent event) {
timeline.play();
}
#FXML
private void resetButton(ActionEvent event) {
rotation = 0.0;
ms = 0;
mm = 0;
hand.setRotate(0);
digital="00:00";
timeline.stop();
}
public void updateTimer() {
ms += 1000;
if (ms >= 60000) {
ms = 00;
mm++;
}
rotation = (ms / 60000.0) * 360.0;
hand.setRotate(rotation);
}
public void updateDigital(){
if(ms/1000<10 && mm<10){
digital=("0" + String.valueOf(mm) + ":" + "0" + String.valueOf(ms/1000));
}
if(ms/1000>=10 && mm <10){
digital=("0" + String.valueOf(mm) + ":" + "" + String.valueOf(ms/1000));
}
if(mm>=10 && ms/1000<10){
digital=("" + String.valueOf(mm) + ":" + "0" + String.valueOf(ms/1000));
}
if(mm>=10 && ms/1000>=10){
digital=("" + String.valueOf(mm) + ":" + "" + String.valueOf(ms/1000));
}
}
#Override
public void initialize(URL url, ResourceBundle rb) {
clockFace.setImage(new Image(this.getClass().getResourceAsStream("clockface.png")));
hand.setImage(new Image(this.getClass().getResourceAsStream("hand.png")));
timeline.setCycleCount(Animation.INDEFINITE);
digitalText.setText(digital);
}
}
i know there are better ways to implement the code for the digital clock but mainly i want to know about passing a changing string to be printed. the digitalText.setText(digital); will cause it to crash
and the fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.text.*?>
<?import javafx.scene.image.*?>
<?import java.lang.*?>
<?import java.net.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane id="AnchorPane" prefHeight="600.0" prefWidth="600.0" styleClass="mainFxmlClass" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="tds7xbstopwatchfxml.FXMLDocumentController">
<stylesheets>
<URL value="#stopwatchui.css" />
</stylesheets>
<children>
<StackPane layoutY="94.0" prefHeight="150.0" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="175.0">
<children>
<ImageView fx:id="clockFace" fitHeight="400.0" fitWidth="400.0" pickOnBounds="true" preserveRatio="true" />
<ImageView fx:id="hand" fitHeight="400.0" fitWidth="400.0" pickOnBounds="true" preserveRatio="true" />
</children>
</StackPane>
<TextField fx:id="digitalText" alignment="CENTER" editable="false" layoutX="204.0" layoutY="123.0" prefHeight="43.0" prefWidth="194.0">
<font>
<Font name="Arial" size="23.0" />
</font>
</TextField>
<Button fx:id="stopbutton" layoutX="280.0" layoutY="75.0" onAction="#stopButton" text="Stop" />
<Button fx:id="startbutton" layoutX="204.0" layoutY="75.0" onAction="#startButton" text="Start" />
<Button fx:id="resetbutton" layoutX="353.0" layoutY="75.0" onAction="#resetButton" text="Reset" />
</children>
</AnchorPane>
You imported the Swing TextField, not the JavaFX text field
Change
import java.awt.TextField
to
import java.scene.control.TextField;