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>
5
  • goalTable is not part of any scene. Commented Jul 23, 2018 at 11:54
  • Sry I just started yesterday learning about javafx. How do I change that? Commented Jul 23, 2018 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 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: docs.oracle.com/javase/8/javafx/properties-binding-tutorial/… Commented Jul 23, 2018 at 12:04
  • ok thx :o will try to patch things up :) Commented Jul 23, 2018 at 12:07
  • ok rearranged things now it works. Really thx a lot fabian wouldn't have found that myself for a long time.... Commented Jul 23, 2018 at 12:10

1 Answer 1

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.