0

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>
Uwe Allner
  • 3,399
  • 9
  • 35
  • 49
  • `goalTable` is not part of any scene. – fabian Jul 23 '18 at 11:54
  • Sry I just started yesterday learning about javafx. How do I change that? – SyntaxIsNotTheProblem Jul 23 '18 at 11:57
  • a) never initialize fields that are supposed to be set from fxml in code b) Using a different class than the application class as controller is highly recommended c) [`static` fields aren't applicable for injection](https://stackoverflow.com/questions/23105433/javafx-8-compatibility-issues-fxml-static-fields) d) Unless you use the properties for something but storing the values, you should use plain fields for storing the data instead. If you use properties, use them as described in example 1-1 here: https://docs.oracle.com/javase/8/javafx/properties-binding-tutorial/binding.htm – fabian Jul 23 '18 at 12:04
  • ok thx :o will try to patch things up :) – SyntaxIsNotTheProblem Jul 23 '18 at 12:07
  • ok rearranged things now it works. Really thx a lot fabian wouldn't have found that myself for a long time.... – SyntaxIsNotTheProblem Jul 23 '18 at 12:10

1 Answers1

3

You have many problems there :

  1. It is not recommended to use your Main class as a Controller class.
  2. Your Nodes used in .fxml are never static since they belong to the instance not to the class so remove them.
  3. Don't instantiate the Nodes 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.

Sunflame
  • 2,993
  • 4
  • 24
  • 48