I am just trying to build a simple Gui with SceneBuilder and JavaFx however I can't figure out why I can't populate my TableView, it just stays empty even after inserting simple Testobjects. Here is the main Class and the Goal class.
package application;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.URL;
import java.util.ResourceBundle;
import Objects.Goal;
import UtilityClasses.GoalManager;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.AnchorPane;
public class Main extends Application implements Initializable {
private Stage primaryStage;
private AnchorPane mainLayout;
public static ObservableList<Goal> goalsData = FXCollections.observableArrayList();
@FXML
static TableView<Goal> goalTable = new TableView<Goal>();
@FXML
static TableColumn<Goal, String> goalsColumn = new TableColumn<>();
@FXML
static TableColumn<Goal, String> statusColumn = new TableColumn<>();
@Override
public void start(Stage primaryStage) {
this.primaryStage = primaryStage;
this.primaryStage.setTitle("MainWindow");
try {
showMainView();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
public static void main(String[] args) {
try {
setDefaultSettings();
launch(args);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void showMainView() throws IOException {
SettingControlls mainController = new SettingControlls();
FXMLLoader loader = new FXMLLoader();
loader.setController(mainController);
loader.setLocation(Main.class.getResource("mainFXML.fxml"));
mainLayout = loader.load();
Scene scene = new Scene(mainLayout);
primaryStage.setScene(scene);
primaryStage.show();
}
private static void setDefaultSettings() throws IOException {
File f = new File("Goals.txt");
if(!f.exists()) {
OutputStream os = new FileOutputStream("Goals.txt");
Writer w = new OutputStreamWriter(os);
w.close();
}
}
private static ObservableList<Goal> getObservableList() throws FileNotFoundException, IOException {
ObservableList<Goal> ol = FXCollections.observableArrayList(new Goal("testGoal"));
return ol;
}
@Override
public void initialize(URL location, ResourceBundle resources) {
goalsData.add(new Goal("test"));
System.out.println(goalsData.get(0).getGoal());
goalsColumn.setCellValueFactory(new PropertyValueFactory<Goal, String>("goal"));
statusColumn.setCellValueFactory(new PropertyValueFactory<Goal, String>("status"));
goalTable.setItems(goalsData);
}
}
Here is the Goal class:
package Objects;
import javafx.beans.property.SimpleStringProperty;
public class Goal {
private SimpleStringProperty goal;
private SimpleStringProperty status;
public Goal(String goal) {
this.goal = new SimpleStringProperty(goal);
this.status = new SimpleStringProperty("ongoing");
}
public Goal(String goal, String status) {
this.goal = new SimpleStringProperty(goal);
this.status = new SimpleStringProperty(status);
}
public String getGoal() {
return this.goal.get();
}
public String getStatus() {
return this.status.get();
}
public void setGoal(String newGoal) {
this.goal = new SimpleStringProperty(newGoal);
}
public void setStatus(String newStatus) {
this.status = new SimpleStringProperty(newStatus);
}
}
Because the TableView and the columns get declared in my fxml file it seemed weird to me to generate them with new however if I don't do that I get an Nullpointerexception.
Edit: Added the fxml file:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ContextMenu?>
<?import javafx.scene.control.Menu?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>
<?import javafx.scene.text.Text?>
<AnchorPane prefHeight="406.0" prefWidth="721.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1">
<children>
<MenuBar layoutX="-7.0" layoutY="14.0" prefHeight="25.0" prefWidth="733.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<menus>
<Menu mnemonicParsing="false" onAction="#GoalsClicked" style="-fx-font-size: 23;" text="Goals" />
<Menu disable="true" mnemonicParsing="false" style="-fx-font-size: 23;" text="Matchups" />
</menus>
</MenuBar>
<Text layoutX="14.0" layoutY="87.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Your current Goals:">
<font>
<Font size="24.0" />
</font>
</Text>
<TableView fx:id="goalTable" layoutY="103.0" prefHeight="200.0" prefWidth="200.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0">
<columns>
<TableColumn fx:id="goalsColumn" maxWidth="1.7976931348623157E308" minWidth="0.0" prefWidth="625.0" text="Goals" />
<TableColumn fx:id="statusColumn" maxWidth="1.7976931348623157E308" minWidth="0.0" prefWidth="72.0" text="Status" />
</columns>
<contextMenu>
<ContextMenu>
<items>
<MenuItem mnemonicParsing="false" text="Delete" />
<MenuItem mnemonicParsing="false" text="Set status" />
</items>
</ContextMenu>
</contextMenu>
</TableView>
<Button layoutX="619.0" layoutY="315.0" mnemonicParsing="false" onAction="#addGoal" text="+" />
<Button layoutX="655.0" layoutY="315.0" mnemonicParsing="false" onAction="#removeGoal" text="-" />
</children>
</AnchorPane>
You have many problems there :
Main
class as a Controller
class.Node
s used in .fxml
are never static since they belong to the instance not to the class so remove them.Node
s defined in the .fxml
file. There is nothing to do with new
. The FXMLLoader
does the work for you.So rewrite the following part:
@FXML
static TableView<Goal> goalTable = new TableView<Goal>();
@FXML
static TableColumn<Goal, String> goalsColumn = new TableColumn<>();
@FXML
static TableColumn<Goal, String> statusColumn = new TableColumn<>();
I would also suggest splitting the Main class into a Main
and a Controller
class.
In Main you should just load the file, and in the Controller
do the UI related stuff.
You can split the following way:
Main:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("View.fxml"));
AnchorPane pane = loader.load();
primaryStage.setScene(new Scene(pane, 400, 400));
primaryStage.show();
}
}
Controller:
import javafx.fxml.Initializable;
import java.net.URL;
import java.util.ResourceBundle;
public class Controller implements Initializable {
@Override
public void initialize(URL location, ResourceBundle resources) {
}
}
and the .fxml
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="stackoverflow.dummy.Controller">
</AnchorPane>
Then you can complete these classes respecting those two rules I mentioned at 2. and 3.