2016-08-13 6 views
1

Was ich versuche zu erreichen ist eine Controller-Instanz von der Hauptmethode zu bekommen, so kann ich Methoden des Controllers aus einer anderen Klasse aufrufen und aktualisieren Sie die fxml. Sowieso ist hier mein Code:JavaFX Zugriff auf Controller gibt Nullpointerexception

Hauptklasse:

public class Main extends Application { 

Controller controller; 
@Override 
public void start(Stage primaryStage) throws Exception{ 

    FXMLLoader fxmlLoader = new FXMLLoader(); 

    Parent root = fxmlLoader.load(getClass().getResource("sample.fxml")); 
    controller = fxmlLoader.getController(); 

    primaryStage.setTitle("uPick Smart Service"); 
    primaryStage.setScene(new Scene(root, 1600, 600)); 

    primaryStage.show(); 

    ConnectionHandling connectionHandling = new ConnectionHandling(); 
    Thread X = new Thread (connectionHandling); 
    X.start(); 

} 

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

} 

public Controller getController(){ 
    return controller; 
} 


} 

Mein Controller-Klasse:

public class Controller { 

public HBox billbox; 

public int childnr; 


public void createBill() { 
    System.out.println("Creating"); 
    TableView<Item> bill = new TableView<>(); 

    DropShadow dropShadow = new DropShadow(); 
    dropShadow.setRadius(5.0); 
    dropShadow.setOffsetX(3.0); 
    dropShadow.setOffsetY(3.0); 
    dropShadow.setColor(Color.color(0.4, 0.5, 0.5)); 

    VBox fullbill = new VBox(); 
    fullbill.setPadding(new Insets(1, 1, 1, 1)); 
    fullbill.getStyleClass().add("fullbill"); 

    TableColumn<Item, String> nameColumn = new TableColumn<>("Emri"); 
    nameColumn.setMinWidth(200); 
    nameColumn.setCellValueFactory(new PropertyValueFactory<>("name")); 

    TableColumn<Item, String> quantsColumn = new TableColumn<>("Sasia"); 
    quantsColumn.setMinWidth(50); 
    quantsColumn.setCellValueFactory(new PropertyValueFactory<>("quants")); 

    double tablewidth = nameColumn.getWidth() + quantsColumn.getWidth(); 

    Label tablenrlabel = new Label("Table 5"); 
    tablenrlabel.getStyleClass().add("tablenr-label"); 
    tablenrlabel.setMinWidth(tablewidth); 

    Button closebutton = new Button("Mbyll"); 
    closebutton.setMinWidth(tablewidth); 
    closebutton.getStyleClass().add("red-tint"); 

    bill.setItems(getItem()); 
    bill.setMinWidth(256); 
    bill.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); 
    bill.getColumns().addAll(nameColumn, quantsColumn); 

    fullbill.setEffect(dropShadow); 
    fullbill.getChildren().addAll(tablenrlabel, bill, closebutton); 

    billbox.getChildren().addAll(fullbill); 

    childnr += 1; 

    //Loops over every button in every vbox and gives it a seperate listener (the index of the button is hardcoded so it can cause problems if you add more items) 
    for (int i = 0; i < childnr; i++) { 
     VBox box = (VBox) billbox.getChildren().get(i); 
     Button btn = (Button) box.getChildren().get(2); //if sudden issues change this 
     btn.setId(Integer.valueOf(i).toString()); 
     btn.setOnAction(new EventHandler<ActionEvent>() { 
      @Override 
      public void handle(ActionEvent event) { 
       int index = billbox.getChildren().indexOf(btn.getParent()); 
       billbox.getChildren().remove(index); 
       childnr -= 1; 
       System.out.println(btn.getId()); 
      } 
     }); 
    } 
    System.out.println("done"); 
} 

} 

Und die Klasse versuchen, Controller-Methode aufzurufen:

public class TakeOrder implements Runnable { 
Socket SOCK; 
Controller controller; 

//OrderIndexes: "Order","Waiter","Payment" 

private int NonConnectedDatas = 2; 

public TakeOrder(Socket X){ 
    this.SOCK = X; 
} 

public void CheckConnection() throws IOException{ 
    System.out.println("Checking connection"); 
    if(!SOCK.isConnected()){ 
     System.out.println("Dissconectiong"); 
     for(int i = 0; i < ConnectionHandling.ConnectionArray.size(); i++){ 
      if(ConnectionHandling.ConnectionArray.get(i) == SOCK){ 
       ConnectionHandling.ConnectionArray.remove(i); 
      } 
     } 
    } 
} 

public void run(){ 
    try{ 
     try{ 
      CheckConnection(); 


      ObjectInputStream ob = new ObjectInputStream(SOCK.getInputStream()); 

      String[] structuredArray = (String[])ob.readObject(); 

      String tablenr = structuredArray[0]; 
      String index = structuredArray[1]; 

      ArrayList<String> names = new ArrayList<>(); 
      ArrayList<String> quants = new ArrayList<>(); 

      int a = 0; 
      int b = 0; 
      switch (index) { 
       case "Order": 
        for (int i = NonConnectedDatas; i < structuredArray.length; i++) { 
         if (i % 2 == 0) { 
          names.add(a, structuredArray[i]); 
          System.out.println(names.get(a)); 
          a++; 
         } else { 
          quants.add(b, structuredArray[i]); 
          System.out.println(quants.get(b)); 
          b++; 
         } 
        } 
        break; 
      } 

      Platform.runLater(new Runnable() { 
       @Override 
       public void run() { 

       } 
      }); 
     }finally{ 
      SOCK.close(); 
     } 
    }catch(Exception X){ 
     System.out.print(X); 
    } 
} 



} 

Und hier ist mein Fehler Nachricht:

Exception in thread "JavaFX Application Thread" java.lang.NullPointerException 
at sample.TakeOrder$1.run(TakeOrder.java:80) 
at com.sun.javafx.application.PlatformImpl.lambda$null$173(PlatformImpl.java:295) 
at java.security.AccessController.doPrivileged(Native Method) 
at com.sun.javafx.application.PlatformImpl.lambda$runLater$174(PlatformImpl.java:294) 
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95) 
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method) 
at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191) 
at java.lang.Thread.run(Thread.java:745) 

Die ConnectionHandling Klasse:

public class ConnectionHandling implements Runnable{ 

public static ArrayList<Socket> ConnectionArray = new ArrayList<Socket>(); 


public void run(){ 
    System.out.println("Starting"); 
    try{ 
     final int PORT = 60123; 
     ServerSocket SERVER = new ServerSocket(PORT); 
     System.out.println("Waiting for clients"); 
     while(true){ 
      Socket SOCK = SERVER.accept(); 
      ConnectionArray.add(SOCK); 

      TakeOrder ORDER = new TakeOrder(SOCK); 
      Thread X = new Thread(ORDER); 
      X.setDaemon(true); 
      X.start(); 
     } 
    }catch(Exception x){ 
     System.out.print(x); 
    } 

} 
+0

Was diese Rückkehr tut? 'controller = fxmlLoader.getController();' Wird der Controller nicht auf null gesetzt? – ppeterka

+0

Ja, es gibt null zurück – user6535530

+0

Wo instanziieren Sie 'TakeOrder' und führen ihre' run() 'Methode aus (oder übergeben sie an ein' Thread')? –

Antwort

5

Zuerst Sie die Methode staticFXMLLoader.load(URL) verwenden. Da es static ist, wird die Controller-Eigenschaft der FXMLLoader Instanz, die Sie erstellten, nicht durch Aufrufen dieser load-Methode initialisiert. So wird controller in Mainnull sein.

Stattdessen legen Sie den Speicherort und verwenden Sie die Instanzmethode load():

@Override 
public void start(Stage primaryStage) throws Exception{ 

    FXMLLoader fxmlLoader = new FXMLLoader(); 
    fxmlLoader.setLocation(getClass().getResource("sample.fxml")); 

    Parent root = fxmlLoader.load(); 
    controller = fxmlLoader.getController(); 

    primaryStage.setTitle("uPick Smart Service"); 
    primaryStage.setScene(new Scene(root, 1600, 600)); 

    primaryStage.show(); 



} 

Dies gilt nicht völlig lösen Ihr Problem, though. Wenn Sie die Anwendung starten, erstellt JavaFX eine Instanz von Main und ruft start() für diese Instanz auf. Mit den obigen Änderungen wird das Feld controller von diese Instanz ordnungsgemäß initialisiert.

jedoch in TakeOrder.run() erstellen Sie eine weitere Instanz von Main:

Main main = new Main(); 

Das controller Feld für diese Instanz nicht initialisiert werden (und auch wenn Sie es tun initialisieren, es ist nicht das gleiche wie die Instanz, die Sie wollen). Sie müssen also veranlassen, dass TakeOrder auf die Controller-Instanz, die in der Startmethode erstellt wurde, zugreifen kann.

Hier ist die einfachste Lösung, um Ihren Code, dass die Arbeit zu machen:

public class ConnectionHandling implements Runnable{ 

    private final Controller controller ; 

    public ConnectionHandling(Controller controller) { 
     this.controller = controller ; 
    } 

    // ... 

    public void run(){ 

     // existing code ... 

     TakeOrder ORDER = new TakeOrder(SOCK, connection); 

     // ... 
    } 
} 

und

public class TakeOrder implements Runnable { 

    Socket SOCK; 
    Controller controller; 

    //OrderIndexes: "Order","Waiter","Payment" 

    private int NonConnectedDatas = 2; 

    public TakeOrder(Socket X, Controller controller){ 
     this.SOCK = X; 
     this.controller = controller ; 
    } 

    // ... 

    public void run() { 

     // ... 

     Platform.runLater(controller::createBill); 

     // ... 
    } 

} 

und schließlich

public class Main extends Application { 

    @Override 
    public void start(Stage primaryStage) throws Exception{ 

     FXMLLoader fxmlLoader = new FXMLLoader(); 

     Parent root = fxmlLoader.load(getClass().getResource("sample.fxml")); 
     Controller controller = fxmlLoader.getController(); 

     primaryStage.setTitle("uPick Smart Service"); 
     primaryStage.setScene(new Scene(root, 1600, 600)); 

     primaryStage.show(); 

     ConnectionHandling connectionHandling = new ConnectionHandling(controller); 
     Thread X = new Thread (connectionHandling); 
     X.start(); 

    } 

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

    } 

    public Controller getController(){ 
     return controller; 
    } 


} 

Im Allgemeinen für Anwendungen wie diese, Sie möchten wahrscheinlich über die Verwendung eines MVC- oder MVP-Ansatzes nachdenken (dh Sie benötigen ein Modell Klasse, die die Dienste halten würde, wie Ihre ConnectionHandler).

Sie könnten auch interessiert sein an this article über die Integration von Diensten in JavaFX.

+0

Ok, vielen Dank. Mehr googeln für mich scheint es: D – user6535530

+0

@ user6535530 Wenn Sie in Ihrer Frage erklären, wo Sie die 'TakeOrder'-Instanz erstellen, könnte es ziemlich einfach zu beantworten sein. –

0

Wenn Sie Ihre Application manuell instanziieren, die in diesem Fall die Main-Klasse ist, wird die start()-Funktion nicht aufgerufen. Versuchen Sie, Ihre Anwendung mit

Application.launch

statt

Main main = new Main()

auch starten, die launch Methode nicht zurück, bis die Anwendung beendet wird. Daher möchten Sie vielleicht Ihren Aufruf an getController() neu anordnen.

Es gibt auch eine andere Möglichkeit, die Instanz Ihres Controllers zu verwalten, nämlich setController(Object controller) von Ihrem FXMLLoader zu verwenden.


Docs:

https://docs.oracle.com/javase/8/javafx/api/javafx/application/Application.html#launch-java.lang.Class-java.lang.String...-

https://docs.oracle.com/javase/8/javafx/api/javafx/fxml/FXMLLoader.html#setController-java.lang.Object-

Verwandte Themen