0

I tried to make two fxml files. The first one is main fxml. And the second one is TableView's fxml. And I put them together.

This is the Main class:

package sceneBuilder;

import java.io.IOException;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class Ana extends Application{

    private Stage window;
    private BorderPane layout;

    @Override
    public void start(Stage args) throws Exception {
        try{
            window = args;
            showMainView();
            showMainItems();
        }
        catch(Exception e){
            e.printStackTrace();
        }
    }


    private void showMainView() throws IOException {
        FXMLLoader loader = new FXMLLoader();
        loader.setLocation(Ana.class.getResource("interface.fxml"));
        layout = loader.load();
        Scene scene = new Scene(layout);
        window.setScene(scene);
        window.show();

    }

    private void showMainItems() throws IOException {
        FXMLLoader loader = new FXMLLoader();
        loader.setLocation(Ana.class.getResource("interface2.fxml"));
        BorderPane layout2 = loader.load();
        layout.setCenter(layout2);

    }

    public static void main(String[] args) {
        launch(args);
    }
}

This is the Main FXML's Controller (Controller.java):

package sceneBuilder;

import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.control.TextField;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;

public class Controller implements Initializable{

    //TreeView
    @FXML
    private TreeView<String> mainTree = new TreeView<>();

    //TextFields
    @FXML
    private TextField txtf1 = new TextField();
    @FXML
    private TextField txtf2 = new TextField();
    @FXML
    private TextField txtf3 = new TextField();

    @Override
    public void initialize(URL location, ResourceBundle recources) {
        TreeItem<String> root = new TreeItem<>("Tables");
        TreeItem<String> table1 = new TreeItem<>("Table-1");
        TreeItem<String> table2 = new TreeItem<>("Table-2");
        root.getChildren().add(table1);
        root.getChildren().add(table2);
        mainTree.setRoot(root);
        mainTree.setShowRoot(false);
    }

    @FXML
     void addClicked(ActionEvent event) throws Exception{
        FXMLLoader loader = new FXMLLoader(getClass().getResource("interface2.fxml"));
        Controller2 controller = loader.getController();
        controller.addData(txtf1.getText(), txtf2.getText(), txtf3.getText());
        }

    @FXML
     void deleteClicked(ActionEvent event){
        FXMLLoader loader = new FXMLLoader(getClass().getResource("interface2.fxml"));
        Controller2 controller = loader.getController();
        controller.addData(txtf1.getText(), txtf2.getText(), txtf3.getText());
        }


}

And this is the TableView's Controller (Controller2.java):

package sceneBuilder;

import java.net.URL;
import java.util.ResourceBundle;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;

public class Controller2 implements Initializable{

    //Table and columns
    @FXML
    private TableView<Product> table = new TableView<>();
    @FXML
    private TableColumn<Product, String> nameColumn = new TableColumn<>();
    @FXML
    private TableColumn<Product, String> priceColumn = new TableColumn<>();
    @FXML
    private TableColumn<Product, String> quantityColumn = new TableColumn<>();


    //Items
    ObservableList<Product> data = FXCollections.observableArrayList(
            new Product("Tablet", "20", "1200,00"),
            new Product("Samsung Note 7", "50", "3450,00"),
            new Product("Kitap", "1000", "50,00")   
    );

    //Column settings
    @Override
    public void initialize(URL location, ResourceBundle recources) {
        nameColumn.setCellValueFactory(new PropertyValueFactory<Product, String>("ad"));
        priceColumn.setCellValueFactory(new PropertyValueFactory<Product, String>("fiyat"));        
        quantityColumn.setCellValueFactory(new PropertyValueFactory<Product, String>("miktar"));

        table.setItems(data);
    }

    public void addData(String t1, String t2, String t3){
        //Add data
        Product satir = new Product(t1, t2, t3);

        data.add(satir);
    }

    public void deleteData(){
        ObservableList<Product> productSelected, allProducts;
        allProducts = table.getItems();
        productSelected = table.getSelectionModel().getSelectedItems();

        productSelected.forEach(allProducts::remove);
    }
}

This is the problem: I wanted to add or delete item from the TableView. So I created three TextField and two buttons. I set setOnAction methods for buttons. And in Controller.java, I fetched my TableView's FXML (interface2.fxml) file. And tried to start the addItem method which exist in TableView's Controller (Controller2.java). But it gives me java.lang.reflect.InvocationTargetException and java.lang.NullPointerException whenever I fill the TextFields and click the add or delete button. I don't know WHY??? Please help me. I don't know English well so please don't write hard sentences. (EXCEPT Programming terms).

And if you need, These are the fxml files: The Main fxml: (interface.fxml)

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Menu?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.TreeView?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Region?>
<?import javafx.scene.layout.VBox?>

<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sceneBuilder.Controller">
   <top>
      <VBox BorderPane.alignment="CENTER">
         <children>
            <MenuBar>
              <menus>
                <Menu mnemonicParsing="false" text="Dosya">
                  <items>
                    <MenuItem mnemonicParsing="false" text="Yeni" />
                        <MenuItem mnemonicParsing="false" text="Kaydet" />
                  </items>
                </Menu>
                <Menu mnemonicParsing="false" text="Yardım">
                  <items>
                    <MenuItem mnemonicParsing="false" text="Hakkında" />
                  </items>
                </Menu>
              </menus>
            </MenuBar>
            <HBox spacing="8.0">
               <children>
                  <TextField fx:id="txtf1" promptText="İsim" />
                  <TextField fx:id="txtf2" layoutX="10.0" layoutY="10.0" promptText="Fiyat" />
                  <TextField fx:id="txtf3" layoutX="175.0" layoutY="18.0" promptText="Miktar" />
                  <Region HBox.hgrow="ALWAYS" />
                  <Button maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" mnemonicParsing="false" onAction="#addClicked" text="Ekle" textAlignment="CENTER" textOverrun="CLIP">
                     <padding>
                        <Insets bottom="4.0" left="10.0" right="10.0" top="4.0" />
                     </padding></Button>
                  <Button layoutX="159.0" layoutY="10.0" mnemonicParsing="false" onAction="#deleteClicked" text="Sil">
                     <padding>
                        <Insets bottom="4.0" left="15.0" right="15.0" top="4.0" />
                     </padding></Button>
               </children>
               <padding>
                  <Insets bottom="8.0" left="8.0" right="8.0" top="8.0" />
               </padding>
            </HBox>
         </children>
      </VBox>
   </top>
   <left>
      <TreeView fx:id="mainTree" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER" />
   </left>
   <bottom>
      <Label text="Sorunlar:" BorderPane.alignment="CENTER_LEFT">
         <padding>
            <Insets bottom="8.0" left="8.0" right="8.0" top="8.0" />
         </padding></Label>
   </bottom>
   <center>
      <TableView fx:id="table" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
        <columns>
          <TableColumn fx:id="nameColumn" prefWidth="75.0" text="C1" />
          <TableColumn fx:id="priceColumn" prefWidth="75.0" text="C2" />
            <TableColumn fx:id="quantityColumn" prefWidth="75.0" text="Column X" />
        </columns>
      </TableView>
   </center>
</BorderPane>

And the TableView's FXML (interface2.fxml):

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.BorderPane?>

<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="300.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sceneBuilder.Controller2">
   <center>
      <TableView fx:id="table" BorderPane.alignment="CENTER">
        <columns>
          <TableColumn fx:id="nameColumn" maxWidth="200.0" minWidth="200.0" prefWidth="200.0" resizable="false" text="Ad" />
          <TableColumn fx:id="priceColumn" maxWidth="100.0" minWidth="100.0" prefWidth="100.0" text="Fiyat" />
            <TableColumn fx:id="quantityColumn" maxWidth="100.0" minWidth="100.0" prefWidth="100.0" text="Miktar" />
        </columns>
      </TableView>
   </center>
</BorderPane>
James_D
  • 201,275
  • 16
  • 291
  • 322
  • Before loader.getController(); you have to call method loader.load(); this is the first idea to rid NPE – BadVegan Sep 25 '16 at 15:43

1 Answers1

2

You need to share the data between the two controllers. You could do this with something like:

public class Controller2 implements Initializable {

    // existing code...

    public ObservableList<Product> getData() {
        return data ;
    }
}

Then give your first controller a reference to the data:

public class Controller implements Initializable {

    private ObservableList<Product> data ;

    public ObservableList<Product> getData() {
        return data ;
    }

    public void setData(ObservableList<Product> data) {
        this.data = data ;
    }

    // ...

    @FXML
    void addClicked(ActionEvent event) {
        data.add(new Product(txtf1.getText(), txtf2.getText(), txtf3.getText());
    }

    // etc ...
}

and then you can tie it all together with

public class Ana extends Application{

    private Stage window;
    private BorderPane layout;

    @Override
    public void start(Stage args) throws Exception {
        try{
            window = args;
            Controller mainController = showMainView();
            Controller2 controller = showMainItems();
            mainController.setData(controller.getData());
        }
        catch(Exception e){
            e.printStackTrace();
        }
    }


    private Controller showMainView() throws IOException {
        FXMLLoader loader = new FXMLLoader();
        loader.setLocation(Ana.class.getResource("interface.fxml"));
        layout = loader.load();
        Scene scene = new Scene(layout);
        window.setScene(scene);
        window.show();
        return loader.getController();
    }

    private Controller2 showMainItems() throws IOException {
        FXMLLoader loader = new FXMLLoader();
        loader.setLocation(Ana.class.getResource("interface2.fxml"));
        BorderPane layout2 = loader.load();
        layout.setCenter(layout2);
        return loader.getController();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Also see Applying MVC With JavaFx for another version of this idea. (In the answer to that question, the data is moved into its own class, which is probably better.)

Community
  • 1
  • 1
James_D
  • 201,275
  • 16
  • 291
  • 322