I am new to JavaFX. I programmed a tableview with a list of persons with their address. Everything worked fine. Then I wanted to write it in the MVC or even better the MVP Design pattern.
I am working with FXML and scenebuilder to layout the scenes.
Now with the MVC I have the problem that I get a NUllpointerException when i.e. I wanna close the confirm dialog box.
And I also can't change the labeltext for the confirmdialog window scene.
I think I know what the problem is but don't know how to solve this properly. The controller gets instantiated twice and therefore the first values of the variables form the fxml are NUll.
So this is how I wrote my little app:
MiniTest.java - my main App
package AddressBook;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class MiniTest extends Application {
private static Stage window;
#Override
public void start(Stage primaryStage) throws Exception {
window = primaryStage;
window.setTitle("Adresslist of Clients");
Model model = new Model();
try {
FXMLLoader mainViewloader = new FXMLLoader(getClass().getResource("View.fxml"));
Parent root = (Parent) mainViewloader.load();
MainController mainController = mainViewloader.<MainController>getController();
window.setScene(new Scene(root));
window.show();
} catch (Exception e) {
e.printStackTrace();
}
}
public static Stage getPrimaryStage() {
return window;
}
public static void main(String[] args) {
launch(args);
}
}
Model.java - this is my model, not sure if the content has to be in here. For the next step I wanna collect the data from a mysql db and save it to the db. For now I have the persons like this.
package AddressBook;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
public class Model {
public ObservableList<Person> getPerson() {
ObservableList<Person> personList = FXCollections.observableArrayList();
Address addressPerson1 = new Address("Jaguarweg 12", "23454", "Bensheim");
Address addressPerson2 = new Address("Friedrich-Ebert-Str. 134", "82635", "Berlin");
Address addressPerson3 = new Address("Adam-Opel-Str. 1", "92364", "Bamberg");
Address addressPerson4 = new Address("Power-Shell-Pfad 21", "10083", "Hamburg");
Address addressPerson5 = new Address("Schwertstr. 76", "749236", "Stuttgart");
Address addressPerson6 = new Address("Hans-Jacob-Weg 4", "66396", "Wiesbaden");
Address addressPerson7 = new Address("Georg-Lucas-Str. 110", "53421", "Wien");
Address addressPerson8 = new Address("Andalusienweg 17", "723612", "Ostfildern");
Address addressPerson9 = new Address("Mercedes-Benz-Str. 9", "883621", "Wolfsburg");
Address addressPerson10 = new Address("Heinrich-Schwein-Str. 43", "134923", "Frankfurt");
Address addressPerson11 = new Address("Engel-Teufel-Str. 66", "083273", "Hildesheim");
personList.add(new Person("Georg Sorresto", addressPerson1));
personList.add(new Person("Flynn Bozzen", addressPerson2));
personList.add(new Person("Bill Klang", addressPerson3));
personList.add(new Person("Wilhelm Busch", addressPerson4));
personList.add(new Person("Gertrud Raven", addressPerson5));
personList.add(new Person("Markus Berg", addressPerson6));
personList.add(new Person("Juergen Schmidt", addressPerson7));
personList.add(new Person("Fritz Titz", addressPerson8));
personList.add(new Person("Bodo Bambino", addressPerson9));
personList.add(new Person("Ortrun Giner", addressPerson10));
personList.add(new Person("Jakob Huber", addressPerson11));
return personList;
}
}
Person.java - my Person class
package AddressBook;
import javafx.beans.property.SimpleStringProperty;
public class Person {
private SimpleStringProperty name;
private Address address;
public Person() {
this.name = new SimpleStringProperty("");
this.address = new Address("", "", "");
}
public Person(String name, Address address) {
this.name = new SimpleStringProperty(name);
;
this.address = address;
}
public String getName() {
return name.get();
}
public void setName(String name2) {
name.set(name2);
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
In MainController.java - for certain things like closing the window or for deleting a person I call a method like this as my own confirmdialogwindow with corresponding String for the Label.
boolean answer = confirmDialogBoxController.display("Exiting Window", "Are you sure you want to close the window?");
if (answer) {
MiniTest.getPrimaryStage().close();
}
and here is the full MainController.java - this is the mainController for the mainView(View.fxml)
package AddressBook;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import javafx.util.Callback;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
public class MainController implements Initializable {
#FXML
private MenuItem exitMenu;
#FXML
private TableView<Person> personTable;
#FXML
private TextField zipInput;
#FXML
private TextField cityInput;
#FXML
private TextField streetInput;
#FXML
private TextField nameInput;
#FXML
private TableColumn<Person, String> cityColumn;
#FXML
private TableColumn<Person, String> zipColumn;
#FXML
private TableColumn<Person, String> streetColumn;
#FXML
private TableColumn<Person, String> nameColumn;
#FXML
private Button addButton;
#FXML
private Button deleteButton;
#FXML
private SimpleStringProperty message;
private Stage window;
private ConfirmDialogBoxController confirmDialogBoxController;
private AlertEmptyNameController alertEmptyNameController;
private Model model;
public MainController(){
model = new Model();
System.out.println("+1 MainController()");
}
public MainController(Model model){
this.model = model;
System.out.println("+1 MainController(model)");
}
#Override
public void initialize(URL location, ResourceBundle resources) {
Callback<TableColumn<Person, String>, TableCell<Person, String>> cellFactory = (TableColumn<Person, String> p) -> new MainController.EditingCell();
nameColumn.setCellValueFactory(new PropertyValueFactory<>("name"));
nameColumn.setCellFactory(cellFactory);
nameColumn.setOnEditCommit(
(
TableColumn.CellEditEvent<Person, String> t) ->
{
((Person) t.getTableView().getItems().get(t.getTablePosition().getRow())).setName(t.getNewValue());
}
);
streetColumn.setCellValueFactory(person -> new
SimpleStringProperty(person.getValue().
getAddress().
getStreet()));
streetColumn.setCellFactory(cellFactory);
streetColumn.setOnEditCommit(
(
TableColumn.CellEditEvent<Person, String> t) ->
{
((Person) t.getTableView().getItems().get(t.getTablePosition().getRow())).getAddress().setStreet(t.getNewValue());
});
zipColumn.setCellValueFactory(person -> new
SimpleStringProperty(person.getValue().
getAddress().
getZip()));
zipColumn.setCellFactory(cellFactory);
zipColumn.setOnEditCommit(
(
TableColumn.CellEditEvent<Person, String> t) ->
{
((Person) t.getTableView().getItems().get(t.getTablePosition().getRow())).getAddress().setZip(t.getNewValue());
});
cityColumn.setCellValueFactory(person -> new
SimpleStringProperty(person.getValue().
getAddress().
getCity()));
cityColumn.setCellFactory(cellFactory);
cityColumn.setOnEditCommit(
(
TableColumn.CellEditEvent<Person, String> t) ->
{
((Person) t.getTableView().getItems().get(t.getTablePosition().getRow())).getAddress().setCity(t.getNewValue());
});
personTable.setItems(model.getPerson());
personTable.setEditable(true);
MiniTest.getPrimaryStage().setOnCloseRequest(e -> {
try {
e.consume();
closeWindow(e);
} catch (IOException ex) {
ex.printStackTrace();
}
});
}
public SimpleStringProperty getMessageProperty(){
return message;
}
public void setMessage(String message) {
this.message.setValue(message);
}
public String getMessage() {
return this.message.getValue();
}
#FXML
public void closeWindow(WindowEvent e) throws IOException {
boolean answer = confirmDialogBoxController.display("Exiting Window", "Are you sure you want to close the window?");
if (answer) {
MiniTest.getPrimaryStage().close();
}
}
#FXML
public void closeWindowFromMenu(ActionEvent e) throws IOException {
boolean answer = confirmDialogBoxController.display("Exiting Window", "Are you sure you want to close the window?");
if (answer) {
MiniTest.getPrimaryStage().close();
}
}
public Stage getPrimaryStage() {
return window;
}
#FXML
public void addNewPerson() throws IOException {
alertEmptyNameController = new AlertEmptyNameController();
Person person = new Person();
person.setName(nameInput.getText());
if(nameInput.getText().isEmpty()){
alertEmptyNameController.alertEmptyName();
}else{
person.getAddress().setStreet(streetInput.getText());
person.getAddress().setZip(zipInput.getText());
person.getAddress().setCity(cityInput.getText());
personTable.getItems().add(person);
}
nameInput.clear();
streetInput.clear();
zipInput.clear();
cityInput.clear();
}
#FXML
public void deletePerson() throws IOException {
boolean answer = confirmDialogBoxController.display("Deleting Entry", "Are you sure you want to delete this entry?");
if (answer) {
ObservableList<Person> personSelected, allPersons;
allPersons = personTable.getItems();
personSelected = personTable.getSelectionModel().getSelectedItems();
personSelected.forEach(allPersons::remove);
}
}
static class EditingCell extends TableCell<Person, String> {
private TextField textField;
public EditingCell() {
}
#Override
public void startEdit() {
if (!isEmpty()) {
super.startEdit();
createTextField();
setText(null);
setGraphic(textField);
textField.selectAll();
}
}
#Override
public void cancelEdit() {
super.cancelEdit();
setText((String) getItem());
setGraphic(null);
}
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}
setText(null);
setGraphic(textField);
} else {
setText(getString());
setGraphic(null);
}
}
}
private void createTextField() {
textField = new TextField(getString());
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
textField.focusedProperty().addListener((ObservableValue<? extends Boolean> arg0,
Boolean arg1, Boolean arg2) -> {
if (!arg2) {
commitEdit(textField.getText());
}
});
}
private String getString() {
return getItem() == null ? "" : getItem();
}
}
}
View.fxml - this is the main View
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<BorderPane fx:id="borderPane" stylesheets="#MyStyle.css" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="AddressBook.MainController">
<bottom>
<HBox fx:id="bottomLayout" alignment="CENTER" style="-fx-background-color: #292929;" BorderPane.alignment="CENTER">
<BorderPane.margin>
<Insets />
</BorderPane.margin>
<children>
<TextField fx:id="nameInput" promptText="Name" style="-fx-background-radius: 0;">
<HBox.margin>
<Insets bottom="8.0" left="8.0" right="5.0" top="10.0" />
</HBox.margin>
</TextField>
<TextField fx:id="streetInput" layoutX="15.0" layoutY="15.0" promptText="Street" style="-fx-background-radius: 0;">
<HBox.margin>
<Insets bottom="8.0" left="5.0" right="5.0" top="10.0" />
</HBox.margin>
</TextField>
<TextField fx:id="zipInput" layoutX="163.0" layoutY="15.0" promptText="Zip" style="-fx-background-radius: 0;">
<HBox.margin>
<Insets bottom="8.0" left="5.0" right="5.0" top="10.0" />
</HBox.margin>
</TextField>
<TextField fx:id="cityInput" layoutX="312.0" layoutY="15.0" promptText="City" style="-fx-background-radius: 0;">
<HBox.margin>
<Insets bottom="8.0" left="5.0" right="5.0" top="10.0" />
</HBox.margin>
</TextField>
<Button fx:id="addButton" mnemonicParsing="false" onAction="#addNewPerson" style="-fx-background-color: #5ebcff; -fx-background-radius: 0; -fx-text-fill: white;" text="Add">
<HBox.margin>
<Insets bottom="8.0" left="5.0" right="5.0" top="10.0" />
</HBox.margin>
</Button>
<Button fx:id="deleteButton" layoutX="474.0" layoutY="15.0" mnemonicParsing="false" onAction="#deletePerson" style="-fx-background-radius: 0; -fx-background-color: #5ebcff; -fx-text-fill: white;" text="Delete">
<HBox.margin>
<Insets bottom="8.0" left="5.0" right="8.0" top="10.0" />
</HBox.margin>
</Button>
</children>
</HBox>
</bottom>
<center>
<TableView fx:id="personTable" editable="true" style="-fx-background-color: #5e5e5e;" stylesheets="#MyStyle.css" BorderPane.alignment="CENTER">
<columns>
<TableColumn fx:id="nameColumn" prefWidth="152.0" text="Name" />
<TableColumn fx:id="streetColumn" prefWidth="196.0" text="Street" />
<TableColumn fx:id="zipColumn" prefWidth="88.0" text="Zip" />
<TableColumn fx:id="cityColumn" prefWidth="163.0" text="City" />
</columns>
</TableView>
</center>
<top>
<HBox fx:id="menuLayout" style="-fx-background-color: #292929;" stylesheets="#MyStyle.css" BorderPane.alignment="CENTER">
<children>
<MenuBar fx:id="menuBar" stylesheets="#MyStyle.css">
<menus>
<Menu fx:id="fileMenu" mnemonicParsing="false" text="File">
<items>
<MenuItem fx:id="exitMenu" mnemonicParsing="false" onAction="#closeWindowFromMenu" text="Exit" />
</items>
</Menu>
</menus>
</MenuBar>
</children>
</HBox>
</top>
</BorderPane>
ConfirmDialogBoxController.java - this is the controller for the confirm dialog box for closing window with the "X" or in the menu File->Exit and for asking the user if he really wants to delete the entry in the tableview
package AddressBook;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
public class ConfirmDialogBoxController implements Initializable {
private static Boolean answer;
#FXML
private Button yesButton;
#FXML
private Button noButton;
#FXML
private Label confirmLabel;
#FXML
private Stage stage;
#FXML
private Model model;
public ConfirmDialogBoxController(){
System.out.println("+1 ConfirmDialogBoxController() object instantiated");
}
public ConfirmDialogBoxController(Model model){
this.model = model;
System.out.println("+1 ConfirmDialogBoxController(model) object instantiated");
}
public boolean display(String title, String message) throws IOException {
FXMLLoader confirmViewLoader = new FXMLLoader(getClass().getResource("ConfirmDialogBoxView.fxml"));
confirmViewLoader.load();
ConfirmDialogBoxController confirmDialogBoxController = confirmViewLoader.getController();
confirmDialogBoxController.confirmLabel.setText(message);
Parent root = confirmViewLoader.getRoot();
Stage stage = new Stage();
stage.setTitle(title);
stage.setScene(new Scene(root));
stage.initModality(Modality.APPLICATION_MODAL);
System.out.println("MessageVariable in DisplayMethod is:"+message);
stage.showAndWait();
return answer;
}
public Label getLabel(){
return confirmLabel;
}
#FXML
public boolean yesButtonClicked(ActionEvent actionEvent) {
answer = true;
stage = (Stage) yesButton.getScene().getWindow();
stage.close();
return answer;
}
#FXML
public boolean noButtonClicked(ActionEvent actionEvent) {
answer = false;
Stage confirmWindow = (Stage) noButton.getScene().getWindow();
confirmWindow.close();
return answer;
}
#Override
public void initialize(URL location, ResourceBundle resources) {
// confirmLabel.textProperty().bindBidirectional(messageProperty);
// System.out.println("messageProperty.getValue(): "+ messageProperty.getValue());
// System.out.println("DEBUG: message is = " + messageProperty.get());
}
}
ConfirmDialogBoxView.fxml - this is the ConfirmDialogBox View
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane prefWidth="450.0" stylesheets="#MyStyle.css" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="AddressBook.ConfirmDialogBoxController">
<children>
<VBox fx:id="vBox" alignment="CENTER" spacing="20.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<Label fx:id="confirmLabel" textAlignment="CENTER" />
<HBox fx:id="hBox" alignment="CENTER" spacing="10.0">
<children>
<Button fx:id="yesButton" alignment="CENTER" contentDisplay="CENTER" mnemonicParsing="false" onAction="#yesButtonClicked" text="Yes" textAlignment="CENTER">
<HBox.margin>
<Insets />
</HBox.margin>
</Button>
<Button fx:id="noButton" alignment="CENTER" contentDisplay="CENTER" layoutX="10.0" layoutY="10.0" mnemonicParsing="false" onAction="#noButtonClicked" text="No" textAlignment="CENTER">
<HBox.margin>
<Insets />
</HBox.margin>
</Button>
</children>
<VBox.margin>
<Insets />
</VBox.margin>
</HBox>
</children>
<padding>
<Insets bottom="50.0" top="30.0" />
</padding>
</VBox>
</children>
</AnchorPane>
AlertEmptyNameController.java - this is the controller for the view when the name field is empty and you click on add person
package AddressBook;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.io.IOException;
public class AlertEmptyNameController {
#FXML
private Button okButton;
#FXML
private Label emptyNameLabel;
private Stage alertEmptyNamestage;
private Model model;
private AlertEmptyNameController alertEmptyNameController;
public AlertEmptyNameController() {
System.out.println("+1 AlertEmptyNameControlle() object instantiated");
}
public AlertEmptyNameController(Model model){
this.model = model;
System.out.println("+1 AlertEmptyNameController(model) object instantiated");
}
public void alertEmptyName() throws IOException {
FXMLLoader alertEmptyNameLoader = new FXMLLoader(getClass().getResource("AlertEmptyNameView.fxml"));
alertEmptyNameLoader.load();
AlertEmptyNameController alertEmptyNameController = alertEmptyNameLoader.getController();
Parent root = alertEmptyNameLoader.getRoot();
alertEmptyNamestage = new Stage();
alertEmptyNamestage.setScene(new Scene(root));
alertEmptyNamestage.setTitle("The Namefield is empty!");
alertEmptyNamestage.initModality(Modality.APPLICATION_MODAL);
alertEmptyNamestage.showAndWait();
}
#FXML
public boolean okButtonClicked(ActionEvent actionEvent) {
boolean answer = true;
alertEmptyNamestage = (Stage) okButton.getScene().getWindow();
alertEmptyNamestage.close();
return answer;
}
}
AlertEmptyNameView.fxml - the view for AlertEmptyName
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?>
<AnchorPane prefHeight="250.0" prefWidth="450.0" stylesheets="#MyStyle.css" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="AddressBook.AlertEmptyNameController">
<children>
<VBox fx:id="vBox" alignment="CENTER" spacing="20.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<Label fx:id="emptyNameLabel" text="The Namefield cannot be empty!" textAlignment="CENTER" />
<HBox fx:id="hBox" alignment="CENTER" spacing="10.0">
<children>
<Button fx:id="okButton" alignment="CENTER" contentDisplay="CENTER" mnemonicParsing="false" onAction="#okButtonClicked" text="Ok" textAlignment="CENTER">
<HBox.margin>
<Insets />
</HBox.margin>
</Button>
</children>
<VBox.margin>
<Insets />
</VBox.margin>
</HBox>
</children>
<padding>
<Insets bottom="50.0" top="30.0" />
</padding>
</VBox>
</children>
</AnchorPane>
And this is for example the NullPointerException when I wanna close the window:
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
at AddressBook.MainController.closeWindow(MainController.java:163)
at AddressBook.MainController.lambda$initialize$8(MainController.java:137)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
at javafx.event.Event.fireEvent(Event.java:198)
at com.sun.javafx.stage.WindowPeerListener.closing(WindowPeerListener.java:88)
at com.sun.javafx.tk.quantum.GlassWindowEventHandler.run(GlassWindowEventHandler.java:122)
at com.sun.javafx.tk.quantum.GlassWindowEventHandler.run(GlassWindowEventHandler.java:40)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.quantum.GlassWindowEventHandler.lambda$handleWindowEvent$3(GlassWindowEventHandler.java:151)
at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:410)
at com.sun.javafx.tk.quantum.GlassWindowEventHandler.handleWindowEvent(GlassWindowEventHandler.java:149)
at com.sun.glass.ui.Window.handleWindowEvent(Window.java:1273)
at com.sun.glass.ui.Window.notifyClose(Window.java:1177)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$4(WinApplication.java:186)
at java.lang.Thread.run(Thread.java:748)
I hope that someone can help me.
Cheers
I deleted now the display() method in confirmDialogBoxController.java and put it in mainController.java
...
public boolean display(String title, String message) throws IOException {
FXMLLoader confirmViewLoader = new FXMLLoader(getClass().getResource("ConfirmDialogBoxView.fxml"));
confirmViewLoader.load();
ConfirmDialogBoxController confirmDialogBoxController = confirmViewLoader.getController();
confirmDialogBoxController.getLabel().setText(message);
Parent root = confirmViewLoader.getRoot();
Stage stage = new Stage();
stage.setTitle(title);
stage.setScene(new Scene(root));
stage.initModality(Modality.APPLICATION_MODAL);
System.out.println("MessageVariable in DisplayMethod is:"+message);
stage.showAndWait();
return answer;
}
#FXML
public void closeWindow(WindowEvent e) throws IOException {
boolean answer = display("Exiting Window", "Are you sure you want to close the window?");
if (answer) {
MiniTest.getPrimaryStage().close();
}
}
...
Your confirmDialogBoxController in the main controller is null: it is never initialized. The way you have set this up is problematic: the ConfirmDialogBoxController is only initialized when the corresponding FXML is loaded (common to any FXML-controller pair), but the code for loading the FXML is in the ConfirmDialogBoxController class, so you can't load the FXML until you have a controller instance.
It's not really the responsibility of the ConfirmDialogBoxController to display the UI defined by its FXML. You should move the display() method to your MainController class:
public class MainController implements Initializable {
// Existing code omitted...
// updated close method:
#FXML
public void closeWindow(WindowEvent e) throws IOException {
boolean answer = display("Exiting Window", "Are you sure you want to close the window?");
if (answer) {
MiniTest.getPrimaryStage().close();
}
}
public boolean displayConfirmDialog(String title, String message) throws IOException {
FXMLLoader confirmViewLoader = new FXMLLoader(getClass().getResource("ConfirmDialogBoxView.fxml"));
confirmViewLoader.load();
ConfirmDialogBoxController confirmDialogBoxController = confirmViewLoader.getController();
confirmDialogBoxController.setConfirmMessage(message);
Parent root = confirmViewLoader.getRoot();
Stage stage = new Stage();
stage.setTitle(title);
stage.setScene(new Scene(root));
stage.initModality(Modality.APPLICATION_MODAL);
System.out.println("MessageVariable in DisplayMethod is:"+message);
stage.showAndWait();
// Get the user response (true = Yes button clicked) from controller:
return confirmDialogBoxController.getResponse() ;
}
}
And then update the ConfirmDialogBoxController:
public class ConfirmDialogBoxController implements Initializable {
// existing code omitted...
// This should not be static, and should be a primitive type:
// private static Boolean answer ;
private boolean answer ;
// this method removed:
/*
public boolean display(String title, String message) throws IOException {
FXMLLoader confirmViewLoader = new FXMLLoader(getClass().getResource("ConfirmDialogBoxView.fxml"));
confirmViewLoader.load();
ConfirmDialogBoxController confirmDialogBoxController = confirmViewLoader.getController();
confirmDialogBoxController.confirmLabel.setText(message);
Parent root = confirmViewLoader.getRoot();
Stage stage = new Stage();
stage.setTitle(title);
stage.setScene(new Scene(root));
stage.initModality(Modality.APPLICATION_MODAL);
System.out.println("MessageVariable in DisplayMethod is:"+message);
stage.showAndWait();
return answer;
}
*/
// This added for convenience:
public void setConfirmMessage(String message) {
confirmLabel.setText(message);
}
// This added to return the response (answer):
public boolean getResponse() {
return answer ;
}
// Note also your event handlers should be void:
#FXML
public void yesButtonClicked(ActionEvent actionEvent) {
answer = true;
stage = (Stage) yesButton.getScene().getWindow();
stage.close();
// return answer;
}
#FXML
public void noButtonClicked(ActionEvent actionEvent) {
answer = false;
Stage confirmWindow = (Stage) noButton.getScene().getWindow();
confirmWindow.close();
// return answer;
}
}
You probably want a similar refactoring for your other dialog.
Related
In my application, I want to apply the behavior of a ToggleGroup to a group of TitledPanes. To do so, I implemented this:
ToggleAdapter.java
package sample;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableMap;
import javafx.scene.control.TitledPane;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleGroup;
public class ToggleAdapter implements Toggle {
final private TitledPane titledPane;
final private ObjectProperty<ToggleGroup> toggleGroupProperty = new SimpleObjectProperty<>();
private ToggleAdapter(TitledPane titledPane) {
this.titledPane = titledPane;
}
#Override
public ToggleGroup getToggleGroup() {
return toggleGroupProperty.get();
}
#Override
public void setToggleGroup(ToggleGroup toggleGroup) {
toggleGroupProperty.set(toggleGroup);
}
#Override
public ObjectProperty<ToggleGroup> toggleGroupProperty() {
return toggleGroupProperty;
}
#Override
public boolean isSelected() {
return titledPane.isExpanded();
}
#Override
public void setSelected(boolean selected) {
titledPane.setExpanded(selected);
}
#Override
public BooleanProperty selectedProperty() {
return titledPane.expandedProperty();
}
#Override
public Object getUserData() {
return titledPane.getUserData();
}
#Override
public void setUserData(Object value) {
titledPane.setUserData(value);
}
#Override
public ObservableMap<Object, Object> getProperties() {
return FXCollections.emptyObservableMap();
}
public static Toggle asToggle(final TitledPane titledPane) {
return new ToggleAdapter(titledPane);
}
}
sample.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.TitledPane?>
<?import javafx.scene.layout.VBox?>
<VBox spacing="7.0" xmlns="http://javafx.com/javafx/15.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
<children>
<TitledPane fx:id="titledPane1" text="Title 1">
<content>
<TextArea minHeight="-Infinity" prefHeight="125.0" prefRowCount="1" wrapText="true" />
</content>
</TitledPane>
<TextField />
<TitledPane fx:id="titledPane2" expanded="false" text="Title 2">
<content>
<TextArea minHeight="-Infinity" prefHeight="125.0" prefRowCount="1" wrapText="true" />
</content>
</TitledPane>
<TitledPane fx:id="titledPane3" expanded="false" text="Title 3">
<content>
<TextArea minHeight="-Infinity" prefHeight="125.0" prefRowCount="1" wrapText="true" />
</content>
</TitledPane>
</children>
<padding>
<Insets bottom="14.0" left="14.0" right="14.0" top="14.0" />
</padding>
</VBox>
Controller.java
package sample;
import javafx.fxml.FXML;
import javafx.scene.control.TitledPane;
import javafx.scene.control.ToggleGroup;
public class Controller {
#FXML private TitledPane titledPane1;
#FXML private TitledPane titledPane2;
#FXML private TitledPane titledPane3;
#FXML
private void initialize() {
final var toggleGroup = new ToggleGroup();
final var toggle1 = ToggleAdapter.asToggle(titledPane1);
toggle1.setToggleGroup(toggleGroup);
final var toggle2 = ToggleAdapter.asToggle(titledPane2);
toggle2.setToggleGroup(toggleGroup);
final var toggle3 = ToggleAdapter.asToggle(titledPane3);
toggle3.setToggleGroup(toggleGroup);
toggleGroup.selectToggle(toggle1);
}
}
Main.java
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Hello World");
final Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
My naive approach doesn't work, but I have no idea why not. Any ideas?
EDIT: I am aware of Acordion, but this would not be suitable because I cannot place all three TitledPanes in the same parent container.
First, you're reinventing the wheel. Don't do that; there's already a control, Accordion, that implements exactly what you're trying to do here.
All you need is:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Accordion?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.control.TitledPane?>
<Accordion expandedPane="${titledPane1}" xmlns="http://javafx.com/javafx/15.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
<panes>
<TitledPane fx:id="titledPane1" text="Title 1" expanded="true">
<content>
<TextArea minHeight="-Infinity" prefHeight="125.0" prefRowCount="1" wrapText="true" />
</content>
</TitledPane>
<TitledPane fx:id="titledPane2" expanded="false" text="Title 2">
<content>
<TextArea minHeight="-Infinity" prefHeight="125.0" prefRowCount="1" wrapText="true" />
</content>
</TitledPane>
<TitledPane fx:id="titledPane3" expanded="false" text="Title 3">
<content>
<TextArea minHeight="-Infinity" prefHeight="125.0" prefRowCount="1" wrapText="true" />
</content>
</TitledPane>
</panes>
<padding>
<Insets bottom="14.0" left="14.0" right="14.0" top="14.0" />
</padding>
</Accordion>
and
public class Controller {
#FXML private Accordion accordion ;
#FXML private TitledPane titledPane1;
#FXML private TitledPane titledPane2;
#FXML private TitledPane titledPane3;
#FXML
private void initialize() {
accordion.setExpandedPane(titledPane1);
}
}
The ToggleAdapter is not required.
The reason your code doesn't work is that you're assuming, I think, that the ToggleGroup observes the selected state of each of its toggles and updates the other toggle's state when one is selected. This isn't the case; it's actually the responsibility of the toggle implementation to maintain single selection in its toggle group, if it so desires. You could do this by adding a listener to the selected state in the ToggleAdapter (but again, to emphasize, it's always wrong to reinvent functionality defined in the standard API).
private ToggleAdapter(TitledPane titledPane) {
this.titledPane = titledPane;
selectedProperty().addListener(obs -> {
ToggleGroup tg = getToggleGroup();
if (tg != null) {
if (isSelected()) {
tg.selectToggle(this);
} else if (tg.getSelectedToggle() == this) {
tg.selectToggle(null);
}
}
});
}
Changing the implementation of ToggleAdapter to this actually solves the problem:
package sample;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ObjectPropertyBase;
import javafx.collections.FXCollections;
import javafx.collections.ObservableMap;
import javafx.scene.control.TitledPane;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleGroup;
import java.util.Optional;
public class ToggleAdapter implements Toggle {
final private TitledPane titledPane;
final private ObjectProperty<ToggleGroup> toggleGroupProperty = new ObjectPropertyBase<>() {
private ToggleGroup old;
#Override
protected void invalidated() {
if (old != null) {
old.getToggles().remove(ToggleAdapter.this);
}
old = get();
if (get() != null && get().getToggles().contains(ToggleAdapter.this) == false) {
get().getToggles().add(ToggleAdapter.this);
}
}
#Override
public Object getBean() {
return ToggleAdapter.this;
}
#Override
public String getName() {
return "toggleGroup";
}
};
#Override
public ToggleGroup getToggleGroup() {
return toggleGroupProperty.get();
}
#Override
public void setToggleGroup(ToggleGroup toggleGroup) {
toggleGroupProperty.set(toggleGroup);
}
#Override
public ObjectProperty<ToggleGroup> toggleGroupProperty() {
return toggleGroupProperty;
}
#Override
public boolean isSelected() {
return titledPane.isExpanded();
}
#Override
public void setSelected(boolean selected) {
titledPane.setExpanded(selected);
}
#Override
public BooleanProperty selectedProperty() {
return titledPane.expandedProperty();
}
#Override
public Object getUserData() {
return titledPane.getUserData();
}
#Override
public void setUserData(Object value) {
titledPane.setUserData(value);
}
#Override
public ObservableMap<Object, Object> getProperties() {
return FXCollections.emptyObservableMap();
}
public static Toggle asToggle(final TitledPane titledPane) {
return new ToggleAdapter(titledPane);
}
public ToggleAdapter(TitledPane titledPane) {
this.titledPane = titledPane;
selectedProperty().addListener(obs -> {
Optional.ofNullable(getToggleGroup()).ifPresent(toggleGroup -> {
if (isSelected()) {
toggleGroup.selectToggle(this);
} else if (toggleGroup.getSelectedToggle() == this) {
toggleGroup.selectToggle(null);
}
});
});
}
}
Thank you, +James_D for the idea on how to change the implementation.
I have a pdf reader developped using Apache PdfBox, my problem is that i have a blurry image after zooming , this problem is only with PDF even if the resolution is very good of the pdf file.
this code working good with png or jpg files, but the problem still with pdf files, i'am really confused, i search on google but i found some solutions that are not complete.
Controller
package application;
import java.nio.file.Paths;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.fxml.FXML;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.ScrollPane.ScrollBarPolicy;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
public class Controller {
String imagePath = "C:\\Users\\piratack007\\Desktop\\1.jpg";
private ImageView imageView = new ImageView();
String path="C:\\Users\\piratack007\\Desktop\\file.pdf";
private PdfModel model= new PdfModel(Paths.get(path));
private ScrollPane scrollPane = new ScrollPane();
private DoubleProperty zoom = new SimpleDoubleProperty(1.1);
private PageDimensions currentPageDimensions ;
#FXML private VBox vbox;
String cssLayout = "-fx-border-color: red;\n" +
"-fx-border-insets: 5;\n" +
"-fx-border-width: 3;\n" +
"-fx-border-style: dashed;\n";
String scrollCssLayout= "-fx-border-color: green;\n" +
"-fx-border-insets: 5;\n" +
"-fx-border-width: 3;\n" +
"-fx-border-style: dashed;\n"+
//Ne pas afficher le petit trait gris
"-fx-background-color:transparent";
public void initialize() {
afficheImage();
}
void afficheImage() {
/*try {
imageView = new ImageView(new Image(new FileInputStream(imagePath)));
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}*/
//loading the first page
imageView = new ImageView(model.getImage(0));
System.out.print("1-imageView.getFitHeight(): "+imageView.getImage().getHeight()+"\n");
System.out.print("imageView.getFitWidth(): "+imageView.getImage().getWidth()+"\n");
currentPageDimensions = new PageDimensions(imageView.getImage().getWidth(), imageView.getImage().getHeight());
zoom.addListener(new InvalidationListener() {
#Override
public void invalidated(Observable arg0) {
//My problem is in this part of code
int width = (int) (imageView.getImage().getWidth() * zoom.get());
int height = (int) (imageView.getImage().getHeight() * zoom.get());
imageView.setFitWidth(width);
System.out.print("Largeur: "+ (width) +"px\n");
imageView.setFitHeight(height);
System.out.print("Hauteur: "+ height +"px\n");
//==================================================
}
});
imageView.preserveRatioProperty().set(true);
scrollPane.setPannable(true);
scrollPane.setStyle(scrollCssLayout);
scrollPane.setVbarPolicy(ScrollBarPolicy.AS_NEEDED);
scrollPane.setHbarPolicy(ScrollBarPolicy.AS_NEEDED);
scrollPane.setContent(imageView);
vbox.setStyle(cssLayout);
vbox.getChildren().add(scrollPane);
}
#FXML private void zoomIn() {
zoom.set(zoom.get()*1.1);
// System.out.print("zoom.get(): "+zoom.get()*100 +"%\n");
}
#FXML private void zoomOut() {
zoom.set(zoom.get()/1.1);
// System.out.print("zoom.get(): "+zoom.get()*100 +"%\n");
}
#FXML private void zoomFit() {
}
#FXML private void zoomWidth() {
}
private class PageDimensions {
private double width ;
private double height ;
PageDimensions(double width, double height) {
this.width = width ;
this.height = height ;
}
#Override
public String toString() {
return String.format("[%.1f, %.1f]", width, height);
}
}
}
PdfModel
package application;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Path;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.image.Image;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.rendering.PDFRenderer;
/**
* #author toru
*/
class PdfModel {
//private static final Logger logger = Logger.getLogger(PdfModel.class.getName());
private PDDocument document;
private PDFRenderer renderer;
Path chemin;
PdfModel() {
}
PdfModel(Path path) {
try {
chemin=path;
document = PDDocument.load(path.toFile());
renderer = new PDFRenderer(document);
} catch (IOException ex) {
throw new UncheckedIOException("PDDocument thorws IOException file=" + path, ex);
}
}
int nombreDePages() {
return document.getPages().getCount();
}
PDPage getPage (int num) {
PDPage page = document.getPage(num);
return page;
}
void fermer() {
try {
document.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
void setPath(Path pPays)
{
chemin = pPays;
System.out.println("On est dans la page pdfmodel");
System.out.println("Path: "+pPays);
}
Image getImage(int pageNumber) {
BufferedImage pageImage;
try {
pageImage = renderer.renderImage(pageNumber);
} catch (IOException ex) {
throw new UncheckedIOException("PDFRenderer throws IOException", ex);
}
return SwingFXUtils.toFXImage(pageImage, null);
}
}
ui.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="10.0" minWidth="10.0" prefHeight="400.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.Controller">
<bottom>
<HBox prefHeight="50.0" prefWidth="400.0" BorderPane.alignment="CENTER">
<children>
<Button mnemonicParsing="false" onAction="#zoomIn" text="+">
<HBox.margin>
<Insets left="5.0" top="5.0" />
</HBox.margin>
</Button>
<Button mnemonicParsing="false" onAction="#zoomOut" text="-">
<HBox.margin>
<Insets left="5.0" top="5.0" />
</HBox.margin>
</Button>
<Button mnemonicParsing="false" onAction="#zoomFit" text="ZoomFit">
<HBox.margin>
<Insets left="5.0" top="5.0" />
</HBox.margin>
</Button>
<Button mnemonicParsing="false" onAction="#zoomWidth" text="ZoomWidth">
<HBox.margin>
<Insets left="5.0" top="5.0" />
</HBox.margin>
</Button>
</children>
</HBox>
</bottom>
<center>
<VBox fx:id="vbox" prefHeight="350.0" prefWidth="400.0" BorderPane.alignment="CENTER" />
</center>
</BorderPane>
Main.java
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
/**
*#author toru
*/
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
final Parent parent = FXMLLoader.load(getClass().getResource("ui.fxml"));
primaryStage.setTitle("Zoom ImageView Demo ");
primaryStage.setScene(new Scene(parent,800, 600));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Best regards
Use the two-parameter renderImage(page, scale) method. 1 is the default scale and is 72 dpi, 4 is 288 dpi which is usually pretty good. So I suggest you start with scale 4, and in a later step you increase when zooming (note that rendering may become slower with higher resolutions).
So a code example for your code would be
pageImage = renderer.renderImage(pageNumber, 4);
I've seen bunch of posts connected with my problem but it seems like nothing works for me. I'm writing simple window app in Java FX and FXML. I have a Main class which has field with days names. I've got also two controller classes - Controller (which is like more important controller) and ShowDays. Each controller has their own FXML and they are responsible for switching scenes and working with user (and with Model = Main). In Controller class user can add a new day to our days names or remove last day. In ShowDays class user can print on window names of days. It's all connected with clicking buttons.
I know that I can organize my program easier and I can print names of days in Controller class and remove all ShowDays class but it's only a part of project I want to create and I just want to understand how real programmers would have done this kind of things.
This is my code:
public class Main extends Application {
private ArrayList<String> daysNames;
public Main() {
daysNames = new ArrayList<>();
}
public void addDay() {
String[] days = {"Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"};
System.out.println("I'm adding day!" + daysNames.size());
if (getDaysNames().size() < 7)
getDaysNames().add(days[getDaysNames().size()]);
else
System.out.println("Can't add more days!");
}
public void removeDay() {
if (!getDaysNames().isEmpty())
getDaysNames().remove(getDaysNames().get(getDaysNames().size()-1));
}
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
}
//setters,getters...
}
Controller class:
public class Controller implements Initializable {
private Main myMain;
#FXML
private ShowDays showDays;
public Controller() {
myMain=new Main();
showDays = new ShowDays(myMain);
}
public Controller(Main main) {
this.myMain = main;
showDays = new ShowDays(myMain);
}
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
}
public void addDay() {
myMain.addDay();
}
public void removeDay() {
myMain.removeDay();
}
public void GOTOshowDays(ActionEvent event) throws IOException {
Parent showDaysParent = FXMLLoader.load(getClass().getResource("ShowDays.fxml"));
Scene showDaysScene = new Scene(showDaysParent);
Stage window = (Stage) ((Node)event.getSource()).getScene().getWindow();
window.setScene(showDaysScene);
window.show();
}
}
ShowDays class:
public class ShowDays implements Initializable {
#FXML
private Text text;
private Main myMain;
public ShowDays() {
myMain = new Main();
}
public ShowDays(Main main) {
myMain = main;
}
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
}
public void BackToController(ActionEvent event) throws IOException {
Parent controllerParent = FXMLLoader.load(getClass().getResource("sample.fxml"));
Scene controllerScene = new Scene(controllerParent);
Stage window = (Stage) ((Node)event.getSource()).getScene().getWindow();
window.setScene(controllerScene);
window.show();
}
public void showDaysInWindow(ActionEvent event) throws IOException {
String allText = "";
for (String day : myMain.getDaysNames())
allText += day + " ";
text.setText(allText);
}
}
and the FXML files:
sample.fxml
<HBox prefHeight="100.0" prefWidth="483.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
<children>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
<children>
<Button layoutX="-8.0" layoutY="48.0" mnemonicParsing="false" onAction="#GOTOshowDays" text="GOTO show days" />
</children>
</AnchorPane>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0" />
<Button mnemonicParsing="false" onAction="#addDay" text="add day" />
<Button mnemonicParsing="false" onAction="#removeDay" text="remove day" />
ShowDays.fxml
<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.ShowDays">
<center>
<Text fx:id="text" strokeType="OUTSIDE" strokeWidth="0.0" BorderPane.alignment="CENTER" />
</center>
<top>
<Button mnemonicParsing="false" onAction="#showDaysInWindow" text="Show days" BorderPane.alignment="CENTER" />
</top>
<bottom>
<Button mnemonicParsing="false" onAction="#BackToController" text="Go back" BorderPane.alignment="CENTER" />
</bottom>
</BorderPane>
I'm not sure if ShowDays user actions works as expected (it's all about that...) but Controller do. The problem is when I add a few days in Controller scene and then switch scene to ShowDays scene, it seems like I lose my Main instance... When I have one controller class, my code cope with working all time on the same instance of Main and adding/removing days works as expected but I can't cope with connecting multiple controllers to model and them each other... I've spend so much time trying to fix this and I don't really understand all of the tips which I found on the internet, so I ask you.
You should use Ideas from #James_D answer here. Your problem is how to pass a model from one Controller to the next. In this example, the model is a List<String>. The model is passed to the initial Controller named FXMLDocumentController. From that point, the model is passed between FXMLDocumentController and ShowDayscontroller. The showDaysInWindow method, shows the current model info in a Text node. addDay adds a day from the array days if the model is missing a day. removeDay removes a day from the model.
Main
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
/**
*
* #author blj0011
*/
public class JavaFXApplication320 extends Application
{
#Override
public void start(Stage stage)
{
try {
String[] days = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};
List<String> daysNames = new ArrayList();
for (int i = 0; i < days.length; i++) {
daysNames.add(days[i]);
}
//Load the FXML. Pass the model to initModel
FXMLLoader loader = new FXMLLoader(getClass().getResource("FXMLDocument.fxml"));
loader.load();
FXMLDocumentController fXMLDocumentController = loader.getController();
fXMLDocumentController.initModel(daysNames);
Scene scene = new Scene(loader.getRoot());
stage.setScene(scene);
stage.show();
}
catch (IOException ex) {
ex.printStackTrace();
}
}
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
}
FXMLDocumentController
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.stage.Stage;
/**
*
* #author blj0011
*/
public class FXMLDocumentController implements Initializable
{
List<String> model;
#FXML
private void goToShowDays(ActionEvent event)
{
try {
FXMLLoader showDaysLoader = new FXMLLoader(getClass().getResource("ShowDays.fxml"));
showDaysLoader.load();
ShowDaysController showDaysController = showDaysLoader.getController();
showDaysController.initModel(this.model);
Stage stage = (Stage) ((Button) event.getSource()).getScene().getWindow();
stage.setScene(new Scene(showDaysLoader.getRoot()));
}
catch (IOException ex) {
ex.printStackTrace();
}
}
#FXML
private void addDay(ActionEvent event)
{
String[] days = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};
List<String> tempDays = new ArrayList(Arrays.asList(days));
if (!this.model.containsAll(tempDays)) {
tempDays.removeAll(this.model);
if (tempDays.size() > 0) {
this.model.add(tempDays.get(0));
}
}
}
#FXML
private void removeDay(ActionEvent event)
{
if (this.model.size() > 0) {
Random random = new Random();
this.model.remove(random.nextInt(this.model.size()));
}
}
#Override
public void initialize(URL url, ResourceBundle rb)
{
// TODO
}
public void initModel(List<String> model)
{
// ensure model is only set once:
if (this.model != null) {
throw new IllegalStateException("Model can only be initialized once");
}
this.model = model;
this.model.forEach(System.out::println);
}
}
FXMLDocument
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<HBox prefHeight="100.0" prefWidth="483.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="javafxapplication320.FXMLDocumentController">
<children>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
<children>
<Button layoutX="-8.0" layoutY="48.0" mnemonicParsing="false" onAction="#goToShowDays" text="GOTO show days" />
</children>
</AnchorPane>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0" />
<Button mnemonicParsing="false" onAction="#addDay" text="add day" />
<Button mnemonicParsing="false" onAction="#removeDay" text="remove day" />
</children>
</HBox>
ShowDaysController
import java.io.IOException;
import java.net.URL;
import java.util.List;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.text.Text;
import javafx.stage.Stage;
/**
* FXML Controller class
*
* #author blj0011
*/
public class ShowDaysController implements Initializable
{
#FXML
Text text;
List<String> model;
#FXML
private void showDaysInWindow(ActionEvent event)
{
text.setText(String.join(" ", this.model));
}
#FXML
private void backToController(ActionEvent event)
{
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("FXMLDocument.fxml"));
loader.load();
FXMLDocumentController fXMLDocumentController = loader.getController();
fXMLDocumentController.initModel(this.model);
Stage stage = (Stage) ((Button) event.getSource()).getScene().getWindow();
stage.setScene(new Scene(loader.getRoot()));
}
catch (IOException ex) {
ex.printStackTrace();
}
}
/**
* Initializes the controller class.
*
* #param url
* #param rb
*/
#Override
public void initialize(URL url, ResourceBundle rb)
{
// TODO
}
public void initModel(List<String> model)
{
// ensure model is only set once:
if (this.model != null) {
throw new IllegalStateException("Model can only be initialized once");
}
this.model = model;
this.model.forEach(System.out::println);
}
}
ShowDays FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<BorderPane 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" fx:controller="javafxapplication320.ShowDaysController">
<center>
<Text fx:id="text" strokeType="OUTSIDE" strokeWidth="0.0" BorderPane.alignment="CENTER" />
</center>
<top>
<Button mnemonicParsing="false" onAction="#showDaysInWindow" text="Show days" BorderPane.alignment="CENTER" />
</top>
<bottom>
<Button mnemonicParsing="false" onAction="#backToController" text="Go back" BorderPane.alignment="CENTER" />
</bottom>
</BorderPane>
This question already has answers here:
The table cells are empty in my tableview. JavaFX + Scenebuilder
(2 answers)
Closed 4 years ago.
In my application Some of the columns in my table are populating like they should be the columns labeled Name Priority and BurstTime have the correct values being added but for some reason the other two ProcessID and State do not update with the values I am adding to my ArrayList.
It looks like my code should be working can anybody see something I have missed?
Here is my controller
package application;
import java.net.URL;
import java.util.ResourceBundle;
import java.io.File;
import java.util.ArrayList;
import java.util.Scanner;
import java.util.StringTokenizer;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
public class Controller implements Initializable {
private ArrayList<String> buf = new ArrayList<>();
protected ArrayList<PCB> array = new ArrayList<>();
protected ArrayList<Process> arrayP = new ArrayList<>();
ObservableList<Process> processData = FXCollections.observableArrayList();
#FXML
private Button SubmitButton;
#FXML
private Button LoadButton;
#FXML
private TextArea textArea;
#FXML
private TextField inputBox;
#FXML
private TableView<Process> ProcessTable;
#FXML
private TableColumn<Process, String> processIDP;
#FXML
private TableColumn<Process, String> processTypeP;
#FXML
private TableColumn<Process, String> priorityCodeP;
#FXML
private TableColumn<Process, String> burstTimeP;
#FXML
private TableColumn<Process, String> StatusCodeP;
#Override
public void initialize(URL url, ResourceBundle rb) {
processIDP.setCellValueFactory(new PropertyValueFactory<Process, String>("processIDP"));
processTypeP.setCellValueFactory(new PropertyValueFactory<Process, String>("processTypeP"));
priorityCodeP.setCellValueFactory(new PropertyValueFactory<Process, String>("priorityCodeP"));
burstTimeP.setCellValueFactory(new PropertyValueFactory<Process, String>("burstTimeP"));
StatusCodeP.setCellValueFactory(new PropertyValueFactory<Process, String>("StatusCodeP"));
Process p1 = new Process();
p1.setprocessIDP("22");
p1.setProcessTypeP ("Apname");
p1.setPriorityCodeP("1");
p1.setBurstTimeP ("13");
p1.setstatusCodeP("Tada");
arrayP.add(p1);
ProcessTable.getItems().addAll(arrayP.get(0));
ProcessTable.setItems(FXCollections.observableArrayList(arrayP));
Process p2 = new Process();
p2.setprocessIDP("24");
p2.setProcessTypeP ("Bpname");
p2.setBurstTimeP ("15");
p2.setPriorityCodeP("2");
arrayP.add(p2);
ProcessTable.getItems().addAll(arrayP.get(1));
// edit existing cell ?
arrayP.get(1).setPriorityCodeP("8");
arrayP.get(1).setstatusCodeP("This");
arrayP.get(1).setprocessIDP("TEST");
}
public ObservableList<Process> getProcessData() {
return processData;
}
#FXML
private TextField LoadProgram;
#FXML
private void handleButtonAction() {
textArea.appendText(inputBox.getText() + "\n");
StringTokenizer st1 = new StringTokenizer(inputBox.getText(), " ");
switch(st1.nextToken()) {
// case "proc": proc(); break;
case "mem": textArea.appendText("Memory: " + String.valueOf(Memory.getUsedMemory()) + "/" + String.valueOf(Memory.getTotalMemory()) + "\n"); break;
// case "exe": exe(); break;
// case "reset": reset(); break;
case "load": buf.add(inputBox.getText()) ;
// edit existing cell ?
arrayP.get(1).setPriorityCodeP("9");
ProcessTable.refresh();
break;
case "exit": System.exit(0); break;
case "clear": textArea.clear(); break;
default: break;
}
}
#FXML
private void handleLoadAction() {
File infile = new File("files/" + LoadProgram.getText() + ".txt");
if (infile.exists() == true ) {
textArea.appendText("Loading " + LoadProgram.getText() + "\n");
}
//call to read data here
else {
textArea.appendText("No Program named " + LoadProgram.getText() + " found \n");
}}
public class textLine {
private String infile;
private String cmd, value;
private Scanner input;
public void parseFile(String filename) {
this.infile = "files/" + filename + ".txt";
parseFile();
}
public void addbuf(String textline) {
buf.add(textline);
}
private void parseFile() {
buf.clear();
try {
File file = new File(infile);
if (file.exists() == true)
input = new Scanner(file);
while (input.hasNext()) {
buf.add(input.next());
}
} catch (Exception e) {
e.printStackTrace();
}
input.close();
}
}
}
Here is my class for Procsess
package application;
public class Process {
String processTypeP = "";
String priorityCodeP = "0";
int lineCodeP = 0;
String burstTimeP = "0";
String processIDP = "0";
String StatusCodeP = "0";
public Process (){}
public String getProcessTypeP() {
return processTypeP;
}
public void setProcessTypeP(String processTypeP) {
this.processTypeP = processTypeP;
}
public String getPriorityCodeP() {
return priorityCodeP;
}
public void setPriorityCodeP(String priorityCodeP) {
this.priorityCodeP = priorityCodeP;
}
public int getLineCodeP() {
return lineCodeP;
}
public void setLineCodeP(int lineCodeP) {
this.lineCodeP = lineCodeP;
}
public String getBurstTimeP() {
return burstTimeP;
}
public void setBurstTimeP(String burstTimeP) {
this.burstTimeP = burstTimeP;
}
public String getprocessIDP() {
return processIDP;
}
public void setprocessIDP(String processIDP) {
this.processIDP = processIDP;
}
public String getstatusCodeP() {
return StatusCodeP;
}
public void setstatusCodeP(String StatusCodeP) {
this.StatusCodeP = StatusCodeP;
}
}
Here is my main application
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
FXMLLoader loader = new FXMLLoader(Main.class.getResource("/Main.fxml"));
// Parent root = FXMLLoader.load(getClass().getResource("/Main.fxml"));
AnchorPane root = (AnchorPane) loader.load(Main.class.getResource("/application/Main.fxml"));
Scene scene = new Scene(root);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
and my fxml file
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.Controller">
<children>
<TabPane layoutX="4.0" layoutY="4.0" prefHeight="700.0" prefWidth="900.0" tabClosingPolicy="UNAVAILABLE">
<tabs>
<Tab text="Processes">
<content>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">
<children>
<TextField fx:id="inputBox" layoutX="14.0" layoutY="54.0" />
<Button fx:id="SubmitButton" layoutX="109.0" layoutY="94.0" mnemonicParsing="false" onAction="#handleButtonAction" text="Submit" />
<TextArea fx:id="textArea" layoutX="197.0" layoutY="14.0" prefHeight="105.0" prefWidth="493.0" />
<TableView fx:id="ProcessTable" layoutX="36.0" layoutY="157.0" prefHeight="430.0" prefWidth="654.0">
<columns>
<TableColumn fx:id="processIDP" prefWidth="75.0" text="ProccessID" />
<TableColumn fx:id="processTypeP" prefWidth="101.0" text="Name" />
<TableColumn fx:id="priorityCodeP" prefWidth="94.0" text="Priority" />
<TableColumn fx:id="StatusCodeP" prefWidth="119.0" text="State" />
<TableColumn fx:id="burstTimeP" prefWidth="100.0" text="BurstTime" />
</columns>
</TableView>
<TextField fx:id="LoadProgram" layoutX="712.0" layoutY="492.0" />
<Button fx:id="LoadProgramButton" layoutX="725.0" layoutY="531.0" mnemonicParsing="false" onAction="#handleLoadAction" text="Load External Program" />
</children></AnchorPane>
</content>
</Tab>
<Tab text="Scheduler">
<content>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0" />
</content>
</Tab>
<Tab text="Memory">
<content>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0" />
</content>
</Tab>
</tabs>
</TabPane>
</children>
</AnchorPane>
Try renaming your getter methods from getprocessIDP and getstatusCodeP to getProcessIDP and getStatusCodeP respectively (note the capital 'S' and 'P'). To avoid problems like this in the future it's a good habit to generate getters, setters and constructors with the IDE instead of doing it manually.
i'm new to JavaFX and i want to print some BigDecimal and Bean in TableColumn in my TableView.
The issue is that i get a NullPointerExceptionexception when i try to setItems of the TableView.
Just to be clear i'm using the fxml with scenario builder for the graphic (panel, tableView, etc.) and the java class for link the bean's data to the tableView. I'm using the ObservableList for get the change, and the Bean has the correct getter that return ObjectProperty<BigDecimal> or ObjectProperty<ContoBean>.
I don't know if what i'm using for setCellValueFactory is correct, i've tryed to follow the tutorial here, but in this tutorial the TableColumn are set only as String. I've read some example for do the the setCellValueFactory, but as far as i can see the best procedure is to avoid using PropertyValueFactory and trying to stick to lambda expressions (more safe and keep the data type intact using a cellFactory, correct me if i'm wrong).
My question are:
What's the best practies for use setCellValueFactory of TableColumn with BigDecimal and Bean?
The best way is to create the TableColumns with scenario builder and link them with the controller or create only the TableView, in scenario builder, and create each single TableColumn in the java controller?
If i'm using a Bean that contains other Beans, how the TableColumn will represent the information? Which variable will be used for view the information, all of the Bean's variable or only a single one?
MovimentoOverview.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="900.0" prefWidth="1400.0" stylesheets="#DarkTheme.css" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="it.evil.money.view.MovimentoOverviewController">
<children>
<SplitPane dividerPositions="0.12857142857142856" prefHeight="300.0" prefWidth="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<items>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
<children>
<TreeView layoutX="-9.0" layoutY="14.0" prefHeight="891.0" prefWidth="200.0" AnchorPane.bottomAnchor="5.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0" AnchorPane.topAnchor="5.0" />
</children>
</AnchorPane>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0" styleClass="background">
<children>
<Label layoutX="14.0" layoutY="7.0" styleClass="label-header" text="Person Details" AnchorPane.leftAnchor="5.0" AnchorPane.topAnchor="5.0">
<font>
<Font size="18.0" />
</font></Label>
<ToolBar layoutX="216.0" layoutY="258.0" prefHeight="40.0" styleClass="background" AnchorPane.bottomAnchor="10.0" AnchorPane.rightAnchor="10.0">
<items>
<Button mnemonicParsing="false" onAction="#handleNewMovimento" text="New.." />
<Button mnemonicParsing="false" onAction="#handleEditMovimento" text="Edit.." />
<Button alignment="CENTER_RIGHT" mnemonicParsing="false" onAction="#handleDeleteMovimento" text="Delete.." textAlignment="CENTER" />
</items>
</ToolBar>
<TableView layoutX="5.0" layoutY="55.0" prefHeight="200.0" prefWidth="200.0" AnchorPane.bottomAnchor="100.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0" AnchorPane.topAnchor="60.0">
<columns>
<TableColumn fx:id="idColumn" prefWidth="75.0" text="ID" />
<TableColumn fx:id="contoColumn" prefWidth="75.0" text="Conto" />
<TableColumn fx:id="dataColumn" prefWidth="75.0" text="Data" />
<TableColumn fx:id="valutaColumn" prefWidth="75.0" text="Valuta" />
<TableColumn fx:id="importoColumn" prefWidth="75.0" text="Importo" />
<TableColumn fx:id="quotaTicketColumn" prefWidth="75.0" text="Quota Ticket" />
<TableColumn fx:id="causaleColumn" prefWidth="75.0" text="Causale" />
<TableColumn fx:id="noteColumn" prefWidth="75.0" text="Note" />
<TableColumn fx:id="tipoPagamentoColumn" prefWidth="75.0" text="Tipo Pagamento" />
<TableColumn fx:id="tipoMovimentoColumn" prefWidth="75.0" text="Tipo Movimento" />
<TableColumn fx:id="spesaInComuneColumn" prefWidth="75.0" text="Spesa in Comune" />
<TableColumn fx:id="spesaPerLavoroColumn" prefWidth="75.0" text="Spesa per Lavoro" />
<TableColumn fx:id="utenteColumn" prefWidth="75.0" text="Utente" />
<TableColumn fx:id="tagColumn" prefWidth="75.0" text="Tag" />
<TableColumn fx:id="bustaPagaColumn" prefWidth="75.0" text="Busta Paga" />
</columns>
</TableView>
</children>
</AnchorPane>
</items>
</SplitPane>
</children>
</AnchorPane>
MovimentoOverviewController
package it.evil.money.view;
import javafx.fxml.FXML;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import java.math.BigDecimal;
import java.time.LocalDate;
import it.evil.money.MainApp;
import it.evil.money.model.BustaPagaBean;
import it.evil.money.model.CausaleBean;
import it.evil.money.model.ContoBean;
import it.evil.money.model.MovimentoBean;
import it.evil.money.model.TagBean;
import it.evil.money.model.TipoMovimentoBean;
import it.evil.money.model.TipoPagamentoBean;
import it.evil.money.model.UtenteBean;
import it.evil.money.util.DateUtil;
public class MovimentoOverviewController {
#FXML
private TableView<MovimentoBean> movimentoTable;
#FXML
private TableColumn<MovimentoBean, BigDecimal> idColumn;
#FXML
private TableColumn<MovimentoBean, ContoBean> contoColumn;
#FXML
private TableColumn<MovimentoBean, LocalDate> dataColumn;
#FXML
private TableColumn<MovimentoBean, String> valutaColumn;
#FXML
private TableColumn<MovimentoBean, BigDecimal> importoColumn;
#FXML
private TableColumn<MovimentoBean, BigDecimal> quotaTicketColumn;
#FXML
private TableColumn<MovimentoBean, CausaleBean> causaleColumn;
#FXML
private TableColumn<MovimentoBean, String> noteColumn;
#FXML
private TableColumn<MovimentoBean, TipoPagamentoBean> tipoPagamentoColumn;
#FXML
private TableColumn<MovimentoBean, TipoMovimentoBean> tipoMovimentoColumn;
#FXML
private TableColumn<MovimentoBean, Boolean> spesaInComuneColumn;
#FXML
private TableColumn<MovimentoBean, Boolean> spesaPerLavoroColumn;
#FXML
private TableColumn<MovimentoBean, BustaPagaBean> bustaPagaColumn;
#FXML
private TableColumn<MovimentoBean, UtenteBean> utenteColumn;
#FXML
private TableColumn<MovimentoBean, TagBean> tagColumn;
#FXML
private Label idLabel;
#FXML
private Label contoLabel;
#FXML
private Label dataLabel;
#FXML
private Label valutaLabel;
#FXML
private Label importoLabel;
// Reference to the main application.
private MainApp mainApp;
public MovimentoOverviewController() {
}
#FXML
private void initialize() {
idColumn.setCellValueFactory(cellData -> cellData.getValue().idProperty() );
contoColumn.setCellValueFactory(cellData -> cellData.getValue().contoProperty() );
dataColumn.setCellValueFactory(cellData -> cellData.getValue().dataProperty() );
valutaColumn.setCellValueFactory(cellData -> cellData.getValue().valutaProperty() );
importoColumn.setCellValueFactory(cellData -> cellData.getValue().importoProperty() );
quotaTicketColumn.setCellValueFactory(cellData -> cellData.getValue().quotaTicketProperty() );
causaleColumn.setCellValueFactory(cellData -> cellData.getValue().causaleProperty() );
noteColumn.setCellValueFactory(cellData -> cellData.getValue().noteProperty() );
tipoPagamentoColumn.setCellValueFactory(cellData -> cellData.getValue().tipoPagamentoProperty() );
tipoMovimentoColumn.setCellValueFactory(cellData -> cellData.getValue().tipoMovimentoProperty() );
spesaInComuneColumn.setCellValueFactory(cellData -> cellData.getValue().spesaInComuneProperty() );
spesaPerLavoroColumn.setCellValueFactory(cellData -> cellData.getValue().spesaPerLavoroProperty() );
bustaPagaColumn.setCellValueFactory(cellData -> cellData.getValue().bustaPagaProperty() );
utenteColumn.setCellValueFactory(cellData -> cellData.getValue().utenteProperty() );
tagColumn.setCellValueFactory(cellData -> cellData.getValue().tagProperty() );
}
public void setMainApp(MainApp mainApp) {
this.mainApp = mainApp;
movimentoTable.setItems(mainApp.getMovimentoData());
}
}
MainApp
package it.evil.money;
import java.io.IOException;
import it.evil.money.dao.DatabaseManager;
import it.evil.money.dao.MovimentoDao;
import it.evil.money.model.*;
import it.evil.money.view.*;
import javafx.application.Application;
import javafx.collections.*;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.stage.Modality;
import javafx.stage.Stage;
public class MainApp extends Application {
private Stage primaryStage;
private BorderPane rootLayout;
private ObservableList<MovimentoBean> movimentoData = FXCollections.observableArrayList();
private MovimentoDao movimentoDao;
public MainApp() {
movimentoDao = new MovimentoDao(DatabaseManager.getConnection());
// Instead of use a empty bean...
// movimentoData.add(new MovimentoBean());
// I get the data for db
movimentoData.addAll(this.movimentoDao.getAll());
this.movimentoDao.distruttore();
}
public ObservableList<MovimentoBean> getMovimentoData() {
return movimentoData;
}
#Override
public void start(Stage primaryStage) {
this.primaryStage = primaryStage;
this.primaryStage.setTitle("AddressApp");
this.primaryStage.getIcons().add(new Image("file:resources/images/address_book_32.png"));
initRootLayout();
showMovimentoOverview();
}
public void initRootLayout() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("view/RootLayout.fxml"));
rootLayout = (BorderPane) loader.load();
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
public void showMovimentoOverview() {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("view/MovimentoOverview.fxml"));
AnchorPane personOverview = (AnchorPane) loader.load();
rootLayout.setCenter(personOverview);
MovimentoOverviewController controller = loader.getController();
controller.setMainApp(this);
} catch (IOException e) {
e.printStackTrace();
}
}
public Stage getPrimaryStage() {
return primaryStage;
}
public static void main(String[] args) {
launch(args);
}
}
MovimentoBean
package it.evil.money.model;
import java.time.LocalDate;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import java.math.BigDecimal;
public class MovimentoBean {
private final ObjectProperty<BigDecimal> id;
private final ObjectProperty<ContoBean> conto;
private final ObjectProperty<LocalDate> data;
private final StringProperty valuta;
private final ObjectProperty<BigDecimal> importo;
private final ObjectProperty<BigDecimal> quotaTicket;
private final ObjectProperty<CausaleBean> causale;
private final StringProperty note;
private final ObjectProperty<TipoPagamentoBean> tipoPagamento;
private final ObjectProperty<TipoMovimentoBean> tipoMovimento;
private final BooleanProperty spesaInComune;
private final BooleanProperty spesaPerLavoro;
private final ObjectProperty<BustaPagaBean> bustaPaga;
private final ObjectProperty<UtenteBean> utente;
private final ObjectProperty<TagBean> tag;
public MovimentoBean() {
this(null, null, null, null, null, null, null, null, null, null, false, false, null, null, null);
}
public MovimentoBean(BigDecimal id, ContoBean conto, LocalDate data, String valuta, BigDecimal importo,
BigDecimal quotaTicket, CausaleBean causale, String note, TipoPagamentoBean tipoPagamento,
TipoMovimentoBean tipoMovimento, boolean spesaInComune, boolean spesaPerLavoro, BustaPagaBean bustaPaga,
UtenteBean utente, TagBean tag) {
this.id = new SimpleObjectProperty<BigDecimal>(id);
this.conto = new SimpleObjectProperty<ContoBean>(conto);
this.data = new SimpleObjectProperty<LocalDate>(data);
this.valuta = new SimpleStringProperty(valuta);
this.importo = new SimpleObjectProperty<BigDecimal>(importo);
this.quotaTicket = new SimpleObjectProperty<BigDecimal>(quotaTicket);
this.causale = new SimpleObjectProperty<CausaleBean>(causale);
this.note = new SimpleStringProperty(note);
this.tipoPagamento = new SimpleObjectProperty<TipoPagamentoBean>(tipoPagamento);
this.tipoMovimento = new SimpleObjectProperty<TipoMovimentoBean>(tipoMovimento);
this.spesaInComune = new SimpleBooleanProperty(spesaInComune);
this.spesaPerLavoro = new SimpleBooleanProperty(spesaPerLavoro);
this.bustaPaga = new SimpleObjectProperty<BustaPagaBean>(bustaPaga);
this.utente = new SimpleObjectProperty<UtenteBean>(utente);
this.tag = new SimpleObjectProperty<TagBean>(tag);
}
public BigDecimal getId(){
return this.id.get();
}
public void setId(BigDecimal id){
this.id.set(id);
}
public ObjectProperty<BigDecimal> idProperty(){
return id;
}
......
When i start the program i get this error:
Exception in Application start method
java.lang.reflect.InvocationTargetException
.......
Caused by: java.lang.NullPointerException
at it.evil.money.view.MovimentoOverviewController.setMainApp(MovimentoOverviewController.java )
at it.evil.money.MainApp.showMovimentoOverview(MainApp.java )
at it.evil.money.MainApp.start(MainApp.java )