Close tab in another Controller - JavaFX - java

I'm designing a simple email client in Java. I'm using two FXML file as the view, and of course, 2 Java controller, each one for the right view.
My goal is to close the WriteView tab clicking on the button "delete" in the Writeview, but I have some problems, because I created the new tab in which I've load the WriteView in the MainVIewController, and now i don't have any id or kind of reference to close the tab unless the "x" button beside the tab name.
Here's the code:
MainView.FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.cell.*?>
<?import javafx.collections.*?>
<TabPane fx:id="root" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" tabClosingPolicy="ALL_TABS" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controller.MainViewController">
<tabs>
<Tab text="Mailbox" closable="false">
<content>
<BorderPane prefHeight="200.0" prefWidth="200.0">
<top>
<ToolBar prefHeight="40.0" prefWidth="200.0" BorderPane.alignment="CENTER">
<items>
<Button fx:id="update" mnemonicParsing="false" onAction="#updateButton" text="Update" />
<Button fx:id="write" mnemonicParsing="false" onAction="#writeButton" text="Write" />
<Button fx:id="reply" mnemonicParsing="false" onAction="#replyButton" text="Reply" />
<Button fx:id="replyToAll" mnemonicParsing="false" onAction="#replyToAllButton" text="Reply to All" />
<Button fx:id="forward" mnemonicParsing="false" onAction="#forwardButton" text="Forward" />
<Button fx:id="delete" mnemonicParsing="false" onAction="#deleteButton" text="Delete" />
</items>
</ToolBar>
</top>
<bottom>
<HBox prefHeight="19.0" prefWidth="600.0" BorderPane.alignment="CENTER">
<children>
<Label text="Client Status:" />
<Label fx:id="status" text="" />
</children>
</HBox>
</bottom>
<left>
<Accordion BorderPane.alignment="CENTER">
<panes>
<TitledPane fx:id="mailboxName" animated="false" text="Inbox: ">
<content>
<VBox prefHeight="200.0" prefWidth="100.0">
<children>
<Label fx:id="inbox" text="Inbox" />
<Label fx:id="drafts" text="Drafts" />
<Label fx:id="bin" text="Bin" />
</children>
</VBox>
</content>
</TitledPane>
</panes>
</Accordion>
</left>
<center>
<TableView fx:id="table" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
<columns>
<TableColumn prefWidth="247.0" text="Subject">
<cellValueFactory><PropertyValueFactory property="subject" /></cellValueFactory>
</TableColumn>
<TableColumn prefWidth="131.0" text="From">
<cellValueFactory><PropertyValueFactory property="receiver" /></cellValueFactory>
</TableColumn>
<TableColumn minWidth="3.0" prefWidth="118.0" text="Date">
<cellValueFactory><PropertyValueFactory property="date" /></cellValueFactory>
</TableColumn>
</columns>
<items>
<FXCollections fx:factory="observableArrayList">
</FXCollections>
</items>
</TableView>
</center>
</BorderPane>
</content>
</Tab>
</tabs>
</TabPane>
MainViewController.java:
package controller;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import model.Account;
import model.Email;
import java.io.IOException;
import java.net.URL;
import java.util.Observable;
import java.util.Observer;
import java.util.ResourceBundle;
public class MainViewController implements Initializable, Observer {
// utility https://stackoverflow.com/questions/40557492/mvc-with-javafx-and-fxml
// we will follow method 1
public MainViewController() {}
#FXML // MainView components
public TabPane root;
public Button update;
public Button write;
public Button reply;
public Button replyToAll;
public Button forward;
public Button delete;
public Label status;
public TitledPane mailboxName;
public Label inbox;
public Label drafts;
public Label bin;
public TableView<Email> table;
// BUTTONS ---------------------------------------------------------------------------------------------------------
/**
* On click on Update button do something
*/
#FXML
public void updateButton() {
update.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
String mess = "You clicked: " + e.getSource() + "!";
System.out.println(mess);
status.setText(mess);
}
});
}
/**
* On click on Write button do something
*/
#FXML
public void writeButton() {
write.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
String mess = "You clicked: " + e.getSource() + "!";
System.out.println(mess);
onWriteClick();
}
});
}
/**
* On click on Reply button do something
*/
#FXML
public void replyButton() {
reply.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
String mess = "You clicked: " + e.getSource() + "!";
System.out.println(mess);
}
});
}
/**
* On click on Reply To All button do something
*/
#FXML
public void replyToAllButton() {
replyToAll.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
String mess = "You clicked: " + e.getSource() + "!";
System.out.println(mess);
}
});
}
/**
* On click on Forward button do something
*/
#FXML
public void forwardButton() {
forward.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
String mess = "You clicked: " + e.getSource() + "!";
System.out.println(mess);
}
});
}
/**
* On click on Delete label do something
*/
#FXML
public void deleteButton() {
delete.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
String mess = "You clicked: " + e.getSource() + "!";
System.out.println(mess);
}
});
}
/**
* Sets the name of the account in the MainView
*/
#FXML
public void setMailboxName() {
}
// TODO: implementare lo stato nelle email, perche così possiamo discriminare se sono inbox, bin, drafts
/**
* On click on Inbox label do something
*/
#FXML
public void inboxLabel() {
}
/**
* On click on Drafts label do something
*/
#FXML
public void draftsLabel() {
}
/**
* On click on Bin label do something
*/
#FXML
public void binLabel() {
}
// INITIALIZING ----------------------------------------------------------------------------------------------------
/**
* It initialize all the event of the buttons
*/
private void initializeButtons(){
updateButton();
writeButton();
replyButton();
replyToAllButton();
forwardButton();
deleteButton();
}
/**
* It calls all the methods that initialize a category of components
*/
private void initializeAll() {
initializeButtons();
loadEmails();
}
// IMPLEMENTATIONS -------------------------------------------------------------------------------------------------
/**
* It initialize all the necessary for the GUI
* #param location: The location used to resolve relative paths for the root object, or null if the location is not known.
* #param resources: The resources used to localize the root object, or null if the root object was not localized.
*/
#Override
public void initialize(URL location, ResourceBundle resources) {
initializeAll();
System.out.println("GUI Loaded"); // DEBUG
}
/**
* Implementation of update method in Observer interface
* #param o: the observable object.
* #param arg: (optional) an argument passed to the notifyObservers method.
*/
#Override
public void update(Observable o, Object arg) {
}
// ON CLICK --------------------------------------------------------------------------------------------------------
/**
* It opens a new Tab with WriteView loaded. It is used to write a new email.
*/
private void onWriteClick(){
try{
Tab tab = new Tab("Write");
tab.setContent(FXMLLoader.load(getClass().getResource("/view/WriteView.fxml"))); // load the GUI for the Write tab
tab.setId("writeroot");
root.getTabs().add(tab); // Add the new tab beside the "Inbox" tab
root.getSelectionModel().select(tab); // Switch to Write tab
} catch (IOException e) {
e.printStackTrace();
}
}
// POPULATING MAIN VIEW --------------------------------------------------------------------------------------------
/**
* It populates the main view with all the email with a specific status
*/
private void loadEmails() {
}
} // end class
WriteView.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.text.*?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<VBox fx:id="root" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controller.WriteViewController">
<children>
<GridPane fx:id="table">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="296.0" minWidth="10.0" prefWidth="57.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="543.0" minWidth="10.0" prefWidth="543.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Label text="To:" />
<Label text="From:" GridPane.rowIndex="1" />
<Label text="Subject:" GridPane.rowIndex="2" />
<TextField fx:id="to" GridPane.columnIndex="1" />
<TextField fx:id="from" GridPane.columnIndex="1" GridPane.rowIndex="1" />
<TextField fx:id="subject" GridPane.columnIndex="1" GridPane.rowIndex="2" />
</children>
</GridPane>
<Pane prefHeight="272.0" prefWidth="600.0">
<children>
<TextArea fx:id="text" prefHeight="277.0" prefWidth="600.0" />
</children>
</Pane>
<ToolBar fx:id="toolbar" nodeOrientation="RIGHT_TO_LEFT" prefHeight="40.0" prefWidth="200.0">
<items>
<Button fx:id="send" mnemonicParsing="false" onAction="#sendButton" text="Send" />
<Button fx:id="saveAsDraft" mnemonicParsing="false" onAction="#saveButton" text="Save as draft" />
<Button fx:id="delete" mnemonicParsing="false" onAction="#deleteButton" text="Delete" />
</items>
</ToolBar>
</children>
</VBox>
WriteViewController.java:
package controller;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import model.Account;
import model.Email;
import java.net.URL;
import java.util.Observable;
import java.util.Observer;
import java.util.ResourceBundle;
public class WriteViewController implements Initializable, Observer{
public WriteViewController() {
}
#FXML
public VBox root;
public GridPane table;
public TextField to;
public TextField from;
public TextField subject;
public TextArea text;
public ToolBar toolbar;
public Button send;
public Button saveAsDraft;
public Button delete;
// BUTTONS ---------------------------------------------------------------------------------------------------------
/**
* On click on Update button do something
*/
#FXML
public void sendButton() {
send.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Account reciver = new Account(to.getText());
Account sender = new Account(from.getText());
Email toSend = new Email(sender, reciver, subject.getText(), text.getText());
toSend.writeEmail(reciver, subject.getText(), text.getText());
// Is not necessary to set the state, because, when a new Email is created, it has already the
// state of new (2). For further information, see Email constructor.
}
});
}
#FXML
public void saveButton() {
saveAsDraft.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Account reciver = new Account(to.getText());
Account sender = new Account(from.getText());
Email toSend = new Email(sender, reciver, subject.getText(), text.getText());
toSend.setState(0); // the email is a draft
}
});
}
#FXML
public void deleteButton() {
delete.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Account reciver = new Account(to.getText());
Account sender = new Account(from.getText());
Email toSend = new Email(sender, reciver, subject.getText(), text.getText());
// here is the problem, Goal: close this tab!
System.out.println(root.getParent()); // DEBUG
}
});
}
// INITIALIZING ----------------------------------------------------------------------------------------------------
/**
* It initialize all the event of the buttons
*/
private void initializeButtons(){
sendButton();
saveButton();
deleteButton();
}
/**
* It call all the methods that initialize a category of components
*/
private void initializeAll() {
initializeButtons();
}
// IMPLEMENTATIONS -------------------------------------------------------------------------------------------------
/**
* It initialize all the necessary for the GUI
* #param location: The location used to resolve relative paths for the root object, or null if the location is not known.
* #param resources: The resources used to localize the root object, or null if the root object was not localized.
*/
#Override
public void initialize(URL location, ResourceBundle resources) {
initializeAll();
}
/**
* Implementation of update method in Observer interface
* #param o: the observable object.
* #param arg: (optional) an argument passed to the notifyObservers method.
*/
#Override
public void update(Observable o, Object arg) {
}
}
Client.java:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
/**
*
* #author lorenzotabasso
*/
public class Client extends Application{
#Override
public void start(Stage primaryStage) throws IOException{
Parent root = FXMLLoader.load(getClass().getResource("view/MainView.fxml"));
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}

You could pass a event handler to the controller that is triggered when you click the delete button:
private Runnable tabCloseHandler;
public void setTabCloseHandler(Runnable handler) {
tabCloseHandler = handler;
}
#FXML
public void deleteButton(ActionEvent e) {
// you don't set the handler here; this method is invoked as handler
String mess = "You clicked: " + e.getSource() + "!";
System.out.println(mess);
if (handler != null) {
handler.run();
}
}
final Tab tab = new Tab("Write");
FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/WriteView.fxml"));
tab.setContent(loader.load());
loader.<WriteViewController>getController().setTabCloseHandler(() -> root.getTabs().remove(tab));
Alternatively you could iterate up towards the root of the scene and close the shown tab as soon as you find a TabPane
public static TabPane findEnclosingTabPane(Node n) {
while (n != null && !(n instanceof TabPane)) {
n = n.getParent();
}
return (TabPane) n;
}
TabPane tabPane = findEnclosingTabPane(root);
tabPane.getTabs().remove(tabPane.getSelectionModel().getSelectedItem());

Related

Making changes in another JavaFX Controller on the same Scene

This is what my view looks like:
Explanation
The entire window itself runs on one controller, called the CartWindowController, and the product list itself is a JavaFX Custom Control called CartItemComponent, which has it's own controller. Each item in the list, therefore has it's own instance of the controller as I programmatically make an instance and populate a VBox.
Problem
I cannot figure out how to notify the CartWindowController when the user clicks on the "X" button, which is handled by the "CartItemComponent" Controller. I would highly appreciate it if anyone could give me a heads up on how to tackle this problem.
Here's what my FXML looks like for the entire Window:
CartWindow.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.VBox?>
<AnchorPane id="AnchorPane" fx:id="parent" prefHeight="400.0" prefWidth="600.0" styleClass="pane" stylesheets="#../assets/userwindow.css" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ordermanagementsystem.controllers.CartWindowController">
<children>
<Button layoutX="25.0" layoutY="25.0" mnemonicParsing="false" onAction="#exitWindow" prefHeight="35.0" prefWidth="20.0" styleClass="back-button" />
<Label layoutX="262.0" layoutY="17.0" styleClass="heading" text="Cart" />
<ScrollPane hbarPolicy="NEVER" layoutY="97.0" prefHeight="315.0" prefWidth="600.0" styleClass="no-padding">
<content>
<AnchorPane prefWidth="600.0" styleClass="no-padding">
<children>
<VBox fx:id="productList" layoutX="25.0" prefWidth="350.0" />
<AnchorPane layoutX="425.0" layoutY="25.0" prefWidth="150.0" styleClass="no-padding">
<children>
<Label layoutX="18.0" styleClass="heading-sub" text="Summary" />
<Label layoutX="1.0" layoutY="55.0" text="Gross:" />
<Label fx:id="grossTotal" alignment="CENTER_RIGHT" layoutX="47.0" layoutY="55.0" prefHeight="17.0" prefWidth="103.0" text="RM0.00" />
<Label layoutX="1.0" layoutY="75.0" text="Packaging:" />
<Label fx:id="packagingTotal" alignment="CENTER_RIGHT" layoutX="73.0" layoutY="75.0" prefHeight="17.0" prefWidth="77.0" text="RM0.00" />
<Label layoutX="1.0" layoutY="95.0" text="Total:" />
<Label fx:id="total" alignment="CENTER_RIGHT" layoutX="40.0" layoutY="95.0" prefHeight="17.0" prefWidth="110.0" styleClass="green-text" text="RM0.00" />
<Button layoutY="125.0" mnemonicParsing="false" onAction="#checkout" prefHeight="39.0" prefWidth="150.0" styleClass="action-button" text="Check Out" />
</children>
</AnchorPane>
</children>
</AnchorPane>
</content>
</ScrollPane>
</children>
</AnchorPane>
CartWindowController.java
package ordermanagementsystem.controllers;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import ordermanagementsystem.cart.CartState;
import ordermanagementsystem.orders.models.OrderItem;
import ordermanagementsystem.viewcomponents.CartItemComponent;
public class CartWindowController extends ViewController implements Initializable {
#FXML
public Parent parent;
#FXML
private VBox productList;
#FXML
private Label grossTotal;
#FXML
private Label packagingTotal;
#FXML
private Label total;
private CartState cartState;
#FXML
private void exitWindow(ActionEvent event) {
try {
this.openPage(this.parent, "MainWindow.fxml");
} catch (IOException ex) {
ex.printStackTrace();
}
}
#FXML
private void checkout(ActionEvent event) {
}
#Override
public void initialize(URL url, ResourceBundle rb) {
this.cartState = CartState.getInstance();
for (OrderItem item : this.cartState.getItems()) {
this.productList.getChildren().add(new CartItemComponent(item));
}
}
}
CartItemComponent.java:
package ordermanagementsystem.viewcomponents;
import java.io.IOException;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import ordermanagementsystem.DialogBox;
import ordermanagementsystem.cart.CartState;
import ordermanagementsystem.orders.models.OrderItem;
public class CartItemComponent extends AnchorPane {
#FXML
private AnchorPane frame;
#FXML
private TextField quantity;
#FXML
private Label total;
#FXML
private Label productName;
private OrderItem item;
private CartState cartState;
public CartItemComponent(OrderItem item) {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/ordermanagementsystem/views/components/CartItemComponent.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException exception) {
exception.getStackTrace();
}
this.cartState = CartState.getInstance();
this.item = item;
this.setQuantity(1);
this.quantity.setEditable(false);
this.productName.setText(this.item.getProduct().getName());
}
private void setQuantity(int quantity) {
this.quantity.setText(String.valueOf(quantity));
this.item.setQuantity(quantity);
this.cartState.updateItem(this.item);
this.total.setText("RM" + String.format("%.2f", item.getProduct().getPrice() * quantity));
}
private int getQuantity() {
return Integer.parseInt(this.quantity.getText());
}
private void updateSummary() {
}
#FXML
private void add(ActionEvent event) {
int quantity = this.getQuantity();
if (quantity == 99) {
DialogBox.showValidationDialog("The quantity cannot be over 99.");
} else {
this.setQuantity(quantity + 1);
}
}
#FXML
private void substract(ActionEvent event) {
int quantity = this.getQuantity();
if (quantity == 1) {
DialogBox.showValidationDialog("The quantity cannot be below 1.");
} else {
this.setQuantity(quantity - 1);
}
}
}
Something you could try is to pass the CartWindowController into the CartItemComponent constructor, then when you need to notify the CartWindowController, you call a method or set a flag or trigger an event, your choice! All you would need to do is add a parameter of type CartWindowController to you CartItemComponent and just save the reference.

ListCell not show on ListView

At this time I learned how to make a program with javafx fxml application. I find out how to display listcell on listview. I use the code below. But from the code, it is not able to display listcell on the listview. When I run the program just show listview and listcell does not appear.
please help me.
Main.java
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("Main.fxml"));
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
Main.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1">
<children>
<ListView fx:id="listView" layoutX="67.0" layoutY="29.0" prefHeight="320.0" prefWidth="376.0" />
</children>
</AnchorPane>
Student.java
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package belajarlistview;
/**
*
* #author kuupie
*/
public class Student {
private static int studentIdAct = 0;
private int studentId;
private String name;
private GENDER gender;
enum GENDER {
MALE,
FEMALE
}
public Student(String name, GENDER gender) {
studentId = studentIdAct++;
this.name = name;
this.gender = gender;
}
public int getStudentId() {
return studentId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public GENDER getGender() {
return gender;
}
public void setGender(GENDER gender) {
this.gender = gender;
}
}
ListCell.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<GridPane fx:id="listCellDetail" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="39.0" prefWidth="421.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1">
<columnConstraints>
<ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Label fx:id="label1" alignment="CENTER" prefHeight="36.0" prefWidth="137.0" text="Label">
<GridPane.margin>
<Insets left="10.0" />
</GridPane.margin>
</Label>
<Label fx:id="label2" alignment="CENTER" prefHeight="36.0" prefWidth="137.0" text="Label" GridPane.columnIndex="2" />
<FontAwesomeIconView fx:id="fxIconGender" strokeLineCap="ROUND" strokeLineJoin="ROUND" GridPane.columnIndex="1" />
</children>
</GridPane>
StudentListViewCell.java
import java.io.IOException;
/**
* Created by Johannes on 23.05.16.
*
*/
public class StudentListViewCell extends ListCell<Student> {
#FXML
private Label label1;
#FXML
private Label label2;
#FXML
private FontAwesomeIconView fxIconGender;
#FXML
private GridPane gridPane;
private FXMLLoader mLLoader;
#Override
protected void updateItem(Student student, boolean empty) {
super.updateItem(student, empty);
mLLoader = new FXMLLoader(getClass().getResource("/fxml/ListCell.fxml"));
mLLoader.setController(this);
try {
mLLoader.load();
} catch (IOException e) {
e.printStackTrace();
}
label1.setText(String.valueOf(student.getStudentId()));
label2.setText(student.getName());
if(student.getGender().equals(Student.GENDER.MALE)) {
fxIconGender.setIcon(FontAwesomeIcon.MARS);
} else if(student.getGender().equals(Student.GENDER.FEMALE)) {
fxIconGender.setIcon(FontAwesomeIcon.VENUS);
} else {
fxIconGender.setIcon(FontAwesomeIcon.GENDERLESS);
}
setText(null);
setGraphic(gridPane);
}
}
Controller.java
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package belajarlistview;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.util.Callback;
import java.net.URL;
import java.util.ResourceBundle;
public class Controller implements Initializable {
#FXML
private ListView<Student> listView;
private final ObservableList<Student> studentObservableList;
public Controller() {
studentObservableList = FXCollections.observableArrayList();
//add some Students
studentObservableList.addAll(
new Student("John Doe", Student.GENDER.MALE),
new Student("Jane Doe", Student.GENDER.FEMALE),
new Student("Donte Dunigan", Student.GENDER.MALE),
new Student("Gavin Genna", Student.GENDER.MALE),
new Student("Darin Dear", Student.GENDER.MALE),
new Student("Pura Petty", Student.GENDER.FEMALE),
new Student("Herma Hines", Student.GENDER.FEMALE)
);
}
#Override
public void initialize(URL location, ResourceBundle resources) {
listView.setItems(studentObservableList);
listView.setCellFactory(studentListView -> new StudentListViewCell());
}
}
I tried alot of code to solve it, by adding
this code setGraphic(student == null ? null : gridPane); on StudentListViewCell.java and
adding
this code fx:controller="belajarlistview.Controller" on Main.fxml
But i have got some error like this :
error 1
error
please help me
Okay
I got the problem. The main problem on this code mLLoader = new FXMLLoader(getClass().getResource("/fxml/ListCell.fxml")); just change this one ("/fxml/ListCell.fxml") to ("ListCell.fxml") the program runs well. Thanks so much #fabian

Is it possible to use objects in an ArrayList to set labels and radio buttons in JavaFx and Scene Builder?

I am trying to create a multiple choice movie game where a movie appears in the label and the actor matched with that movie and 3 randoms are set as the radio buttons. Every example or tutorials I have found online show me how to use JavaFx and Scene Builder without implementing any classes or objects already written. I have a class called MovieSet that takes in an Arraylist of movies, and I do the same with the actors. However all the tutorials online never say if its possible to set the labels and radio buttons to these arraylist.
Is it possible to do this? and if so how would this be done?
I have used a for loop to iterate over the movieList, but it is telling me it cannot find symbol for movieList
public static void displayMovies(ArrayList<Movie> movieList) {
for (int x = 0; x < movieList.size(); x++) {
Movie movie = movieList.get(x);
System.out.printf("%s", movie.toString());
}
}
This is my controller. I do apologize. This is my first time using JavaFX and
public class FXMLDocumentController implements Initializable {
#FXML
private Label movielabel;
#FXML
private void handleButtonAction(ActionEvent event) {
}
#Override
public void initialize(URL url, ResourceBundle rb) {
MovieSet movie = new MovieSet();
this.movielabel.getLabelFor(MovieSet.displayMovies(movieList));
}
}
This sample app is a general way of handling a multiple choice question and answer app. Comments in code.
Main Class
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
/**
*
* #author blj0011
*/
public class MultipleChoiceGameExample 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);
}
}
Controller
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
/**
*
* #author blj0011
*/
public class FXMLDocumentController implements Initializable
{
#FXML
private Label lblQuestionNumber;
#FXML
private TextArea taQuestionDisplay;
#FXML
private Button btnAnswer1, btnAnswer2, btnAnswer3, btnAnswer4, btnNextQuestion;
List<Question> questions;
int currentQuestion = 0;
#Override
public void initialize(URL url, ResourceBundle rb)
{
//Get Questions from Database or what ever source you use
FakeDatabaseHandler fakeDatabaseHandler = new FakeDatabaseHandler();
questions = fakeDatabaseHandler.getQuestions();
//Shuffle the questions
Collections.shuffle(questions);
//Load first question
lblQuestionNumber.setText("Question 1 of " + questions.size());
loadQuestion(questions.get(currentQuestion));
}
#FXML
public void handleBtnNextQuestion(ActionEvent actionEvent)
{
currentQuestion++;
if (currentQuestion < questions.size()) {
lblQuestionNumber.setText("Question " + (currentQuestion + 1) + " of " + questions.size());
loadQuestion(questions.get(currentQuestion));
}
else {
Alert alert = new Alert(Alert.AlertType.WARNING);
alert.setTitle("Game Over Alert");
alert.setContentText("There are no more questions!");
alert.showAndWait();
}
}
public void loadQuestion(Question question)
{
taQuestionDisplay.setText(question.getQuestion());//Set the question
List<String> choices = question.getIncorrectAnswers();//Get the incorrect answers
choices.add(question.getCorrectAnswer());////Get the correct answer and add it.
Collections.shuffle(choices);//Randomize the choices
//Add buttons to List to make creating event handlers easier
List<Button> choicesButtons = new ArrayList();
choicesButtons.add(btnAnswer1);
choicesButtons.add(btnAnswer2);
choicesButtons.add(btnAnswer3);
choicesButtons.add(btnAnswer4);
//Set the choices to a Button
for (int i = 0; i < choices.size(); i++) {
choicesButtons.get(i).setText(choices.get(i));
}
//Create Action handlers for each button
for (Button button : choicesButtons) {
button.setOnAction(actionEvent -> {
//Check if button's text equals the correct answer
if (button.getText().equals(question.getCorrectAnswer())) {
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("Answer Alert");
alert.setHeaderText("Correct!");
alert.setContentText("You got the answer right!");
alert.showAndWait();
}
else {
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("Answer Alert");
alert.setHeaderText("Incorrect!");
alert.setContentText("You got the answer wrong!\nThe correct answer is " + question.getCorrectAnswer());
alert.showAndWait();
}
});
}
}
}
FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.VBox?>
<AnchorPane id="AnchorPane" prefHeight="476.0" prefWidth="668.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.141" fx:controller="multiplechoicegameexample.FXMLDocumentController">
<children>
<Label fx:id="label" layoutX="126" layoutY="120" minHeight="16" minWidth="69" />
<VBox alignment="TOP_CENTER" prefHeight="200.0" prefWidth="100.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<Label fx:id="lblQuestionNumber" alignment="CENTER" maxWidth="1.7976931348623157E308" text="Label" />
<TextArea fx:id="taQuestionDisplay" prefHeight="300.0" prefWidth="200.0">
<VBox.margin>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</VBox.margin>
</TextArea>
<GridPane hgap="5.0" maxWidth="400.0" vgap="5.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Button fx:id="btnAnswer1" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" mnemonicParsing="false" text="Button" />
<Button fx:id="btnAnswer2" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" mnemonicParsing="false" text="Button" GridPane.columnIndex="1" />
<Button fx:id="btnAnswer3" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" mnemonicParsing="false" text="Button" GridPane.rowIndex="1" />
<Button fx:id="btnAnswer4" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" mnemonicParsing="false" text="Button" GridPane.columnIndex="1" GridPane.rowIndex="1" />
</children>
</GridPane>
<Button fx:id="btnNextQuestion" onAction="#handleBtnNextQuestion" text="Next Question">
<VBox.margin>
<Insets top="20.0" />
</VBox.margin>
</Button>
</children>
</VBox>
</children>
</AnchorPane>
Question Class
import java.util.ArrayList;
import java.util.List;
/**
*
* #author blj0011
*/
public class Question
{
private String question;
private String correctAnswer;
private List<String> incorrectAnswers = new ArrayList();
public Question(String question, String correctAnswer, List<String> incorrectAnswers)
{
this.question = question;
this.correctAnswer = correctAnswer;
this.incorrectAnswers = incorrectAnswers;
}
public String getQuestion()
{
return question;
}
public void setQuestion(String question)
{
this.question = question;
}
public String getCorrectAnswer()
{
return correctAnswer;
}
public void setCorrectAnswer(String correctAnswer)
{
this.correctAnswer = correctAnswer;
}
public List<String> getIncorrectAnswers()
{
return incorrectAnswers;
}
public void setIncorrectAnswers(List<String> incorrectAnswers)
{
this.incorrectAnswers = incorrectAnswers;
}
}
Database Handler(You may get your data from a different source)
import java.util.ArrayList;
import java.util.List;
/**
*
* #author blj0011
*/
public class FakeDatabaseHandler
{
List<Question> questions = new ArrayList();
public FakeDatabaseHandler()
{
//connect to db!
//Simulate getting data from db!
List<String> incorrectAnswersQuestion1 = new ArrayList();
incorrectAnswersQuestion1.add("Pakistan");
incorrectAnswersQuestion1.add("Palau");
incorrectAnswersQuestion1.add("Panama");
questions.add(new Question("Which is the only American state to begin with the letter 'p'?", "Pennsylvania", incorrectAnswersQuestion1));
List<String> incorrectAnswersQuestion2 = new ArrayList();
incorrectAnswersQuestion2.add("Mississppi");
incorrectAnswersQuestion2.add("Nile");
incorrectAnswersQuestion2.add("Yangtze");
questions.add(new Question("What is the world's longest river?", "Amazon", incorrectAnswersQuestion2));
List<String> incorrectAnswersQuestion3 = new ArrayList();
incorrectAnswersQuestion3.add("6,000 miles");
incorrectAnswersQuestion3.add("10,000 miles");
incorrectAnswersQuestion3.add("12,000 miles");
questions.add(new Question("What is the diameter of Earth?", "8,000 miles", incorrectAnswersQuestion3));
}
public List<Question> getQuestions()
{
return questions;
}
}

How to simulate a queue and a stack JavaFX with pause, threads(task, service)

I been searching in YouTube, Stack-Overflow and fxexperience, even oracle documentation but I still don't get it.
There's not similar example :(
The problem is how to do a stack and queue simulator.
Generate 10 random numbers. Done.
Show the numbers in a table. Done.
Use the 10 random numbers to simulate a stack and a queue. I don't now how to comunicate the service with the TextField.
Pause the simulation. or Stop.
-The program needs a pause method. I don't know how to pause a thead. Perhaps with wait() and notify(). I don't know.
I have used label.textProperty.bind(service.progressProperty()). this works but when i try to bind a variable instead
the method updateProgress(i,n) throws a exception.
Maybe I need to use 2 Tasks.
Main class:
package simulation;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("/simulation/simulation.fxml"));
primaryStage.setTitle("JavaFX and concurrency, Stack and Queue");
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Controller Class:
package simulation;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Worker;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import java.net.URL;
import java.util.ResourceBundle;
public class SimulationCt implements Initializable {
#FXML private TableView table;
#FXML private TableColumn i_tc;
#FXML private TableColumn random_tc;
#FXML private TextField stack_start;
#FXML private TextField stack_1;
#FXML private TextField stack_2;
#FXML private TextField stack_3;
#FXML private TextField stack_4;
#FXML private TextField stack_5;
#FXML private TextField stack_final;
#FXML private TextField queue_start;
#FXML private TextField queue_1;
#FXML private TextField queue_2;
#FXML private TextField queue_3;
#FXML private TextField queue_4;
#FXML private TextField queue_5;
#FXML private TextField queue_final;
#FXML private Button new_b;
#FXML private Button play_pause_b;
#FXML private Button stop_b;
#FXML private ProgressBar progress_bar;
private ObservableList<RandomNumber> numberList = FXCollections.observableArrayList();
private CalculateService backProcess;
#FXML
private void createNew () {
disableNew(true);
generateRandom();
backProcess = new CalculateService();
progress_bar.progressProperty().bind(backProcess.progressProperty());
Platform.runLater(() -> {
backProcess.start();
});
}
#FXML
private void playPause () {
if(backProcess.getState().equals(Worker.State.RUNNING)) {
System.out.println("stoping...");
backProcess.cancel();
} else if (backProcess.getState().equals(Worker.State.CANCELLED)) {
System.out.println("restarting...");
backProcess.restart();
}
}
#FXML
private void stop () {
if(backProcess.getState().equals(Worker.State.RUNNING)) {
System.out.println("stoping...");
backProcess.cancel();
} else if (backProcess.getState().equals(Worker.State.CANCELLED)) {
System.out.println("already stoped...");
}
clearItems();
disableNew(false);
}
// cleans the list and the progress bar.
private void clearItems () {
progress_bar.progressProperty().unbind();
progress_bar.progressProperty().set(0.0);
numberList.clear();
}
private void disableNew (boolean b) {
new_b.setDisable(b);
play_pause_b.setDisable(!b);
stop_b.setDisable(!b);
}
// generates random numbers to fill the table, these numbers are the ones for the stack and the queue.
private void generateRandom () {
for (int i = 1; i < 11; i++) {
int rnd = (int)(Math.random() * (200 - 0 + 1)) + 0;
numberList.add(new RandomNumber(i, rnd ));
}
}
private void startTable () {
i_tc.setCellValueFactory( new PropertyValueFactory("i"));
random_tc.setCellValueFactory( new PropertyValueFactory("number"));
table.setItems(numberList);
}
#Override
public void initialize(URL url, ResourceBundle rb) {
disableNew(false);
startTable();
}
}
FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ProgressBar?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.VBox?>
<AnchorPane id="AnchorPane" prefHeight="300.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="simulation.SimulationCt">
<children>
<TableView fx:id="table" layoutX="8.0" layoutY="10.0" prefHeight="282.0" prefWidth="162.0">
<columns>
<TableColumn fx:id="i_tc" prefWidth="28.0" text="i" />
<TableColumn fx:id="random_tc" prefWidth="122.0" text="Random Number" />
</columns>
</TableView>
<GridPane layoutX="303.0">
<columnConstraints>
<ColumnConstraints fillWidth="false" minWidth="10.0" prefWidth="50.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" />
<ColumnConstraints fillWidth="false" halignment="RIGHT" hgrow="SOMETIMES" minWidth="10.0" prefWidth="50.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Label text="Stack" />
<VBox GridPane.rowIndex="1" GridPane.valignment="TOP">
<children>
<TextField fx:id="stack_start" prefHeight="25.0" prefWidth="25.0" />
<Label text="new" />
</children>
</VBox>
<VBox GridPane.columnIndex="1" GridPane.rowIndex="1">
<children>
<TextField fx:id="stack_1" prefHeight="25.0" prefWidth="25.0" />
<TextField fx:id="stack_2" prefHeight="25.0" prefWidth="25.0" />
<TextField fx:id="stack_3" prefHeight="25.0" prefWidth="25.0" />
<TextField fx:id="stack_4" prefHeight="25.0" prefWidth="25.0" />
<TextField fx:id="stack_5" prefHeight="25.0" prefWidth="25.0" />
</children>
</VBox>
<VBox GridPane.columnIndex="2" GridPane.rowIndex="1" GridPane.valignment="TOP">
<children>
<TextField fx:id="stack_final" prefHeight="25.0" prefWidth="25.0" />
<Label text="last" />
</children>
</VBox>
</children>
</GridPane>
<GridPane layoutX="193.0" layoutY="155.0">
<columnConstraints>
<ColumnConstraints fillWidth="false" hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" />
<ColumnConstraints fillWidth="false" hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Label text="Queue" GridPane.columnIndex="1" />
<VBox GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="TOP">
<children>
<TextField fx:id="queue_start" prefHeight="25.0" prefWidth="25.0" />
<Label text="new" />
</children>
</VBox>
<HBox spacing="5.0" GridPane.columnIndex="1" GridPane.rowIndex="1">
<children>
<TextField fx:id="queue_1" prefHeight="25.0" prefWidth="25.0" />
<TextField fx:id="queue_2" prefHeight="25.0" prefWidth="25.0" />
<TextField fx:id="queue_3" prefHeight="25.0" prefWidth="25.0" />
<TextField fx:id="queue_4" prefHeight="25.0" prefWidth="25.0" />
<TextField fx:id="queue_5" prefHeight="25.0" prefWidth="25.0" />
</children>
</HBox>
<VBox GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="TOP">
<children>
<TextField fx:id="queue_final" prefHeight="25.0" prefWidth="25.0" />
<Label text="last" />
</children>
</VBox>
</children>
</GridPane>
<Button fx:id="new_b" onAction="#createNew" layoutX="266.0" layoutY="243.0" mnemonicParsing="false" text="New" />
<Button fx:id="play_pause_b" onAction="#playPause" layoutX="326.0" layoutY="243.0" mnemonicParsing="false" text="Play / Pause" />
<Button fx:id="stop_b" onAction="#stop" layoutX="428.0" layoutY="243.0" mnemonicParsing="false" text="Stop" />
<ProgressBar fx:id="progress_bar" layoutX="266.0" layoutY="277.0" prefWidth="200.0" progress="0.0" />
</children>
</AnchorPane>
DataHelper:
package simulation;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
public class RandomNumber {
private IntegerProperty i;
private IntegerProperty number;
public RandomNumber(int i, int number) {
this.i = new SimpleIntegerProperty(i);
this.number = new SimpleIntegerProperty(number);
}
public int getI() {
return i.get();
}
public IntegerProperty iProperty() {
return i;
}
public void setI(int i) {
this.i.set(i);
}
public int getNumber() {
return number.get();
}
public IntegerProperty numberProperty() {
return number;
}
public void setNumber(int number) {
this.number.set(number);
}
}
Service class:
package simulation;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
public class CalculateService extends Service {
int n = 20; // this does the trick to simulate the pause.
int j = 0; // even if the task is canceled the last value is saved here.
#Override
protected Task createTask() {
return new Task() {
#Override protected Void call() throws Exception {
int a;
int b;
int iterations;
for (iterations = j; iterations <= n; iterations++) {
j = iterations;
if (isCancelled()) {
updateMessage("Cancelled");
break;
}
updateProgress(iterations, n);
System.out.println("number: " + j);
//Block the thread for a short time, but be sure
//to check the InterruptedException for cancellation
try {
Thread.sleep(100);
} catch (InterruptedException interrupted) {
if (isCancelled()) {
updateMessage("Cancelled");
break;
}
}
}
return null;
}
};
}
}
There's a lot of code in the question, and I don't think you need it all to address the concepts you're actually asking about. So I'll just give a high-level answer here. If you want to edit your question to something much simpler that addresses the actual issue, then I can make this specific to that example.
I would probably try to do this without threads at all, but using the animation API. For example, you could use a Timeline, with the following basic outline:
public class Controller {
// #FXML-annotated UI elements...
// Other state....
private Timeline timeline ;
#FXML
public void initialize() {
timeline = new Timeline(new KeyFrame(Duration.seconds(100)), e -> {
if (moreStepsToDo()) {
doNextStep();
} else {
stopSimulation();
}
});
timeline.setCycleCount(Animation.INDEFINITE);
}
private boolean moreStepsToDo() {
// return true if there are more steps in the simulation,
// false otherwise
}
private void doNextStep() {
// do next step in the simulation
}
#FXML
private void stopSimulation() {
timeline.stop();
}
#FXML
private void pauseSimulation() {
timeline.pause();
}
#FXML
private void playSimulation() {
timeline.play();
}
#FXML
private void resetSimulation() {
timeline.jumpTo(Duration.ZERO);
}
}
The nice thing about this solution is that everything is single-threaded: the event handler for the key frame is executed on the FX Application Thread, which is the same thread that executes the event handlers. This means there is no need to worry about synchronizing data across threads. The predefined methods in the animation API pause(), play(), and stop() provide exactly the functionality you're looking for; you just have to update the application state appropriately.
Here's a simple complete example that uses this approach (it just moves a bunch of rectangles, one at a time, from one vbox to another).
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class SimplePausableAnimation extends Application {
private VBox left;
private VBox right;
private Timeline timeline;
private Button pausePlay;
#Override
public void start(Stage primaryStage) {
left = new VBox(10);
left.setMinWidth(200);
right = new VBox(10);
right.setMinWidth(200);
HBox hbox = new HBox(10, left, right);
pausePlay = new Button();
Button reset = new Button("Reset");
reset.setOnAction(e -> reset());
reset();
BorderPane root = new BorderPane(hbox);
HBox buttons = new HBox(5, pausePlay, reset);
buttons.setAlignment(Pos.CENTER);
root.setBottom(buttons);
Scene scene = new Scene(root, 600, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
private void reset() {
if (timeline != null) {
timeline.stop();
}
left.getChildren().clear();
right.getChildren().clear();
for (int i = 0; i < 5; i++) {
left.getChildren().add(new Rectangle(100, 100, Color.CORNFLOWERBLUE));
}
timeline = new Timeline(new KeyFrame(Duration.seconds(1), e -> {
if (moreStepsToDo()) {
doNextStep();
} else {
timeline.stop();
}
}));
timeline.setCycleCount(Animation.INDEFINITE);
pausePlay.disableProperty().bind(Bindings.createBooleanBinding(() -> {
if (left.getChildren().isEmpty()) {
return true;
}
return false;
}, left.getChildren()));
pausePlay.textProperty().bind(Bindings.createStringBinding(() -> {
if (timeline.getStatus() == Animation.Status.RUNNING) {
return "Pause";
}
return "Play";
}, timeline.statusProperty()));
pausePlay.setOnAction(e -> {
if (timeline.getStatus() == Animation.Status.RUNNING) {
timeline.pause();
} else {
timeline.play();
}
});
}
private boolean moreStepsToDo() {
return !left.getChildren().isEmpty();
}
private void doNextStep() {
int n = left.getChildren().size();
Node node = left.getChildren().remove(n - 1);
right.getChildren().add(node);
}
public static void main(String[] args) {
launch(args);
}
}
If you do want to do this with threads, one approach to pausing a thread is to use a Semaphore with a single permit. This generally looks something like this:
Semaphore pauser = new Semaphore(1);
Thread simulationThread = new Thread(() -> {
try {
while (! Thread.currentThread().isInterrupted()) {
pauser.acquire();
// do simulation step
pauser.release();
Thread.sleep(100);
}
} catch (InterruptedException exc) {
// ignore and exit thread...
}
});
(Obviously the same idiom will work in your Task, which is executed on a background thread.)
Then calling pauser.acquire(); from the controller will pause the simulation (because the simulation thread will not be able to acquire the permit), and calling pauser.release() while it is paused will let it run again.

JavaFx java.lang.NullPointerException with scene builder 2.0

I'm trying to make a program to Log in or register a person. But I am getting the Exception.(InvocationTargetException and the NullPointerException)
Error Stack Trace
Exception in Application start method
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:363)
at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:303)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:767)
Caused by: 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$48/1099983479.run(Unknown Source)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.NullPointerException
at gui.StartSchermController.<init>(StartSchermController.java:81)
at StartUp.start(StartUp.java:27)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$153(LauncherImpl.java:821)
at com.sun.javafx.application.LauncherImpl$$Lambda$51/93724165.run(Unknown Source)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$166(PlatformImpl.java:323)
at com.sun.javafx.application.PlatformImpl$$Lambda$45/128893786.run(Unknown Source)
at com.sun.javafx.application.PlatformImpl.lambda$null$164(PlatformImpl.java:292)
at com.sun.javafx.application.PlatformImpl$$Lambda$47/2063964656.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$165(PlatformImpl.java:291)
at com.sun.javafx.application.PlatformImpl$$Lambda$46/1108411398.run(Unknown Source)
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$141(WinApplication.java:102)
at com.sun.glass.ui.win.WinApplication$$Lambda$37/1147985808.run(Unknown Source)
... 1 more
Exception running application StartUp
Java Result: 1
BUILD SUCCESSFUL (total time: 1 second)
I have problem with this line.
chTaal.setItems(FXCollections.observableArrayList(dc.keuzeTaal()));
StartSchermController.java
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package gui;
import domein.DomeinController;
import java.io.IOException;
import java.util.ArrayList;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.SplitPane;
import javafx.scene.control.TextField;
/**
* FXML Controller class
*
* #author Eigenaar
*/
public class StartSchermController extends SplitPane {
#FXML
private TextField txfMeldID;
#FXML
private TextField txfMeldWw;
#FXML
private TextField txfRegNaam;
#FXML
private TextField txfRegVNaam;
#FXML
private TextField txfRegWw2;
#FXML
private TextField txfRegWw;
#FXML
private TextField txfRegID;
#FXML
private Label lblMeldAan;
#FXML
private Label lblRegistreer;
#FXML
private Label lblMeldId;
#FXML
private Label lblMeldWw;
#FXML
private Label lblRegNaam,lblRegVNaam,lblRegId,lblRegWw,lblRegWw2;
#FXML
private Button btnMeldAan,btnRegistreer;
private final DomeinController dc;
#FXML
private ComboBox cmbTaal;
#FXML
private TextField txfStartExc;
/**
* Initializes the controller class.
* #param dc
*/
public StartSchermController(DomeinController dc) {
this.dc=dc;
FXMLLoader loader=new FXMLLoader(getClass().getResource("StartScherm.fxml"));
loader.setRoot(this);
loader.setController(this);
try {
loader.load();
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
cmbTaal.setItems(FXCollections.observableArrayList(dc.keuzeTaal()));
}
public int geefKeuze(){
int s = cmbTaal.getSelectionModel().getSelectedIndex();
return s;
}
#FXML
private void cmbTaalOnAction(ActionEvent event) {
int keuzeTaal = cmbTaal.getSelectionModel().getSelectedIndex();
System.out.println(keuzeTaal);
updateTaal(dc.setTaal(keuzeTaal));
this.geefKeuze();
}
private void updateTaal(ArrayList<String> s) {
lblMeldAan.setText(s.get(0));
this.lblMeldId.setText(s.get(1));
this.lblMeldWw.setText(s.get(2));
this.lblRegistreer.setText(s.get(3));
this.lblRegNaam.setText(s.get(4));
this.lblRegVNaam.setText(s.get(5));
this.lblRegId.setText(s.get(6));
this.lblRegWw.setText(s.get(7));
this.lblRegWw2.setText(s.get(8));
}
#FXML
private void btnMeldAanOnAction(ActionEvent event){
String id= this.txfMeldID.getText();
String ww= this.txfMeldWw.getText();
try{
dc.meldAan(id, ww);
}catch(Exception e){
txfStartExc.setText(e.getMessage());
this.txfMeldID.setText("");
this.txfMeldWw.setText("");
}
this.txfMeldID.setText("");
this.txfMeldWw.setText("");
}
#FXML
private void btnRegistreerOnAction(ActionEvent event){
String n=this.txfRegNaam.getText();
String vn=this.txfRegVNaam.getText();
String id= this.txfRegID.getText();
String ww= this.txfRegWw.getText();
String ww2=this.txfRegWw2.getText();
dc.maakSpelerAan(n, vn, id, ww);
}
}
this is the code from my fxml file generated by JavaFX Scene Builder 2.0
StartScherm.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.text.*?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<fx:root dividerPositions="0.17839195979899497" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" orientation="VERTICAL" prefHeight="400.0" prefWidth="600.0" type="SplitPane" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="gui.StartSchermController">
<items>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="100.0" prefWidth="160.0">
<children>
<Label layoutX="352.0" text="SOKOBAN" textFill="#d7300f">
<font>
<Font name="Colonna MT" size="48.0" />
</font>
</Label>
<ComboBox fx:id="cmbTaal" layoutX="46.0" layoutY="18.0" onAction="#cmbTaalOnAction" prefWidth="150.0" />
</children></AnchorPane>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="257.0" prefWidth="598.0">
<children>
<SplitPane dividerPositions="0.481421647819063" layoutY="-8.0" prefHeight="400.0" prefWidth="621.0">
<items>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="346.0" prefWidth="295.0">
<children>
<Label fx:id="lblMeldAan" layoutX="12.0" layoutY="1.0" prefHeight="27.0" prefWidth="106.0">
<font>
<Font size="18.0" />
</font>
</Label>
<Label fx:id="lblMeldId" layoutX="14.0" layoutY="42.0" prefHeight="17.0" prefWidth="74.0" />
<TextField fx:id="txfMeldID" layoutX="97.0" layoutY="38.0" />
<Label fx:id="lblMeldWw" layoutX="14.0" layoutY="83.0" prefHeight="17.0" prefWidth="74.0" />
<TextField fx:id="txfMeldWw" layoutX="97.0" layoutY="79.0" />
<TextField fx:id="txfStartExc" layoutX="26.0" layoutY="184.0" prefHeight="116.0" prefWidth="244.0" />
<Button fx:id="btnMeldAan" layoutX="97.0" layoutY="129.0" mnemonicParsing="false" onAction="#btnMeldAanOnAction" text="Meld aan" />
</children>
</AnchorPane>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
<children>
<Label fx:id="lblRegistreer" layoutX="14.0" layoutY="1.0" prefHeight="27.0" prefWidth="106.0">
<font>
<Font size="18.0" />
</font>
</Label>
<Label fx:id="lblRegNaam" layoutX="8.0" layoutY="41.0" prefHeight="17.0" prefWidth="113.0" text="" />
<Label fx:id="lblRegWw" layoutX="8.0" layoutY="148.0" prefHeight="17.0" prefWidth="113.0" text="" />
<Label fx:id="lblRegId" layoutX="8.0" layoutY="112.0" prefHeight="17.0" prefWidth="113.0" text="" />
<Label fx:id="lblRegVNaam" layoutX="8.0" layoutY="76.0" prefHeight="17.0" prefWidth="113.0" text="" />
<Label fx:id="lblRegWw2" layoutX="8.0" layoutY="185.0" text="" />
<TextField fx:id="txfRegNaam" layoutX="128.0" layoutY="37.0" />
<TextField fx:id="txfRegVNaam" layoutX="128.0" layoutY="72.0" />
<TextField fx:id="txfRegWw2" layoutX="128.0" layoutY="181.0" />
<TextField fx:id="txfRegWw" layoutX="128.0" layoutY="144.0" />
<TextField fx:id="txfRegID" layoutX="128.0" layoutY="108.0" />
<Button fx:id="btnRegistreer" layoutX="128.0" layoutY="226.0" mnemonicParsing="false" onAction="#btnRegistreerOnAction" text="Registreer" />
</children>
</AnchorPane>
</items>
</SplitPane>
</children></AnchorPane>
</items>
</fx:root>
DomeinController.java
package domein;
import java.util.ArrayList;
import java.util.Locale;
import java.util.ResourceBundle;
public class DomeinController {
private final SpelerRepository spelerRepository;
private Speler speler;
// private Spel spel;
public DomeinController() {
spelerRepository = new SpelerRepository();
}
/*
public void registreer(String naam, String voornaam, String email, LocalDate geboortedatum, String wachtwoord, String wachtwoordBevestiging) {
if (!wachtwoord.equals(wachtwoordBevestiging)) {
throw new EmailException();
}*/
/*
Speler nieuweSpeler = new Speler(naam, voornaam, gebruikersID, wachtwoord);
setSpeler(nieuweSpeler); // ONTBREEKT!!
spelerRepository.voegToe(nieuweSpeler);
}*/
/** UC_1 meldAan DOING */
public void meldAan(String gebruikersID, String wachtwoord) {
Speler gevondenSpeler = spelerRepository.meldAan(gebruikersID, wachtwoord);
if (gevondenSpeler != null) {
setSpeler(gevondenSpeler);
}
}
/** UC_1 meldAan KNOWING */
public String[] geefDetailsSpeler() {
return speler.geefDetailsSpeler();
/* if (speler == null) {
return null;
}
String[] spelerS = new String[3];
spelerS[0] = speler.getVoornaam();
spelerS[1] = speler.getNaam();
return spelerS;*/
}
private void setSpeler(Speler speler){
this.speler = speler;
}
public void maakSpelerAan(String naam, String voornaam, String gebruikersID, String wachtwoord) {
Speler s=new Speler(naam,voornaam,gebruikersID,wachtwoord,false);
spelerRepository.voegToe(s);
}
public ArrayList<String> keuzeTaal(){
ArrayList<String> taal = new ArrayList<>();
taal.add("Nederlands");
taal.add("Français");
taal.add("English");
return taal;
}
public ArrayList<String> setTaal(int i){
ResourceBundle taal=setResourceBundle(i);
ArrayList<String> s= new ArrayList<>();
s.add(taal.getString("meldAan"));
s.add(taal.getString("userID"));
s.add(taal.getString("wachtwoord"));
s.add(taal.getString("registreer"));
s.add(taal.getString("naam"));
s.add(taal.getString("voornaam"));
s.add(taal.getString("userID"));
s.add(taal.getString("wachtwoord"));
s.add(taal.getString("herhalingWachtwoord"));
return s;
}
public static ResourceBundle setResourceBundle(int taalKeuze) {
Locale locale=null;
ResourceBundle taal;
if (taalKeuze==1) {
locale = Locale.FRENCH;
} else if (taalKeuze==2) {
locale = Locale.ENGLISH;
} else if(taalKeuze==0) {
locale = new Locale("nl");
}
return ResourceBundle.getBundle("taal\\LabelsBundle", locale);
}
;
}
/*
private void setSpel(Spel spel) {
this.spel = spel;
}
*/
Main Class StartUp.java
import domein.DomeinController;
import gui.StartSchermController;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.scene.Scene;
import javafx.stage.Stage;
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
/**
*
* #author Eigenaar
*/
#SuppressWarnings("unchecked")
public class StartUp extends Application
{
#Override
#SuppressWarnings("unchecked")
public void start(Stage stage)
{
DomeinController controller = new DomeinController();
StartSchermController root = new StartSchermController(controller);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.setTitle("Startscherm Sokoban");
stage.show();
}
public static void main(String[] args)
{
launch(args);
}
}
Thank you in advance. I hope somebody can help me.
I don't think your FXML is loading. Add a System.exit(1); to the catch block after you call loader.load(); to check.
If you use loader.setController(...), you should not have a fx:controller attribute in the FXML file. Remove that attribute, and it should fix the problem.

Categories