jTymbark
jTymbark

Reputation: 35

JavaFx set Label text from another controller

I want to change text for label not from class where i declare controller to fxml file.

I want to change it in ClientAccept class in Main file, but when i tried get Controller class and run function in Controller class from ClientAccept i get NullPointerException 'because "this.sStatus" is null'. I cant resolve it.

Main.java:

package sample;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Control;
import javafx.stage.Stage;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;

public class Main extends Application {

ServerSocket ss;
HashMap clientColl = new HashMap();

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

    FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
    Controller controller = new Controller();
    loader.setController(controller);
    Parent parent = loader.load();
    Scene scene  = new Scene(parent);
    stage.setScene(scene);
    stage.show();

    try{
        ss = new ServerSocket(8989);
    }catch (IOException ioe){
        System.out.println(ioe.getMessage());
    }

    ClientAccept cla = new ClientAccept();
    cla.isWorking();

}


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


class ClientAccept extends Thread{

    public void isWorking() throws IOException {
        Controller controller = new Controller();
        controller.setsStatus("test");
    }

    public void run(){
        while(true){
            try {
                Socket s = ss.accept();
                String i = new DataInputStream(s.getInputStream()).readUTF();
                if(clientColl.containsKey(i)){
                    DataOutputStream dos = new DataOutputStream(s.getOutputStream());
                    dos.writeUTF("Już jesteś Zarejestrowany");
                }else {
                    clientColl.put(i, s);
                    Platform.runLater(()->{
                        controller.setsStatus("TextToLabel");
                    });
                }
            }catch (Exception e){
                System.out.println(e.getMessage());
            }
        }
    }
}

}

Controller.java:

package sample;


import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;

public class Controller {
@FXML
Label sStatus;
@FXML
TextArea msgBox;

void setsStatus(String text){
    sStatus.setText(text);
    System.out.println(text);
}

}

sample.fxml:

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

<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.layout.AnchorPane?>


    <AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"     prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
   <children>
      <TextArea fx:id="msgBox" layoutX="53.0" layoutY="67.0" prefHeight="278.0" prefWidth="512.0" />
      <Label layoutX="67.0" layoutY="28.0" text="Server Status: " />
      <Label fx:id="sStatus" layoutX="142.0" layoutY="28.0" text=".............." />
   </children>
</AnchorPane>

I just get NullPointerException.

Upvotes: 1

Views: 1463

Answers (1)

James_D
James_D

Reputation: 209358

The @FXML-annotated fields are only initialized in the Controller instance used by the FXMLLoader. They won't be initialized in other instances, such as the one you create in your ClientAccept class.

A quick-and-dirty approach is just to arrange for the ClientAccept to have a reference to the correct Controller instance. E.g.

public void start(Stage stage) throws Exception{

    FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
    Controller controller = new Controller();
    loader.setController(controller);
    Parent parent = loader.load();
    Scene scene  = new Scene(parent);
    stage.setScene(scene);
    stage.show();

    try{
        ss = new ServerSocket(8989);
    }catch (IOException ioe){
        System.out.println(ioe.getMessage());
    }

    ClientAccept cla = new ClientAccept(controller);
    cla.isWorking();

}


class ClientAccept extends Thread{

    private final Controller controller ;

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

    public void isWorking() throws IOException {
        controller.setsStatus("test");
    }

    public void run(){
        while(true){
            try {
                Socket s = ss.accept();
                String i = new DataInputStream(s.getInputStream()).readUTF();
                if(clientColl.containsKey(i)){
                    DataOutputStream dos = new DataOutputStream(s.getOutputStream());
                    dos.writeUTF("Już jesteś Zarejestrowany");
                }else {
                    clientColl.put(i, s);
                    Platform.runLater(()->{
                        controller.setsStatus("TextToLabel");
                    });
                }
            }catch (Exception e){
                System.out.println(e.getMessage());
            }
        }
    }
}

It's generally not very good practice to share controller instances around like this. A better approach is to use a MVC design and share the model. E.g. you could do

public class DataModel {

    private final StringProperty status = new SimpleStringProperty();

    public StringProperty statusProperty() {
        return status ;
    }

    public final String getStatus() {
        return statusProperty().get();
    }

    public final void setStatus(String status) {
        statusProperty().set(status);
    }

    // Similarly for other data shared by the app....

}

Then

public class Controller {
    @FXML
    Label sStatus;
    @FXML
    TextArea msgBox;

    private final DataModel model ;

    public Controller(DataModel model) {
        this.model = model ;
    }

    @FXML
    public void initialize() {
        sStatus.textProperty().bind(model.statusProperty());
    }


}

and

class ClientAccept extends Thread{

    private final DataModel model ;

    public ClientAccept(DataModel model) {
        this.model = model ;
    }

    public void isWorking() throws IOException {
        model.setStatus("test");
    }

    public void run(){
        while(true){
            try {
                Socket s = ss.accept();
                String i = new DataInputStream(s.getInputStream()).readUTF();
                if(clientColl.containsKey(i)){
                    DataOutputStream dos = new DataOutputStream(s.getOutputStream());
                    dos.writeUTF("Już jesteś Zarejestrowany");
                }else {
                    clientColl.put(i, s);
                    Platform.runLater(()->{
                        model.setStatus("TextToLabel");
                    });
                }
            }catch (Exception e){
                System.out.println(e.getMessage());
            }
        }
    }
}

and then just tie it all together:

public void start(Stage stage) throws Exception{

    DataModel model = new DataModel();

    FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
    Controller controller = new Controller(model);
    loader.setController(controller);
    Parent parent = loader.load();
    Scene scene  = new Scene(parent);
    stage.setScene(scene);
    stage.show();

    try{
        ss = new ServerSocket(8989);
    }catch (IOException ioe){
        System.out.println(ioe.getMessage());
    }

    ClientAccept cla = new ClientAccept(model);
    cla.isWorking();

}

Upvotes: 2

Related Questions