Update TextFlow from another class JavaFx

1.2k Views Asked by At

I'm here with my JavaFx app.

I have two class:

I start these class in two different Thread. Because server is blockable.

  • My UI class ( Called Main ( I know i need to change this)).
  • A Server class ( Called Serveur )).

In my Server Class when i receive bytes with a ServerSocket.

I need to update a TextFlow (called flowMessafe) in my UI class : with flowMessage.getChildren().add(); I can update this TextFlow without problem in my UI class. But i my Server class i can't.

EDIT : I try all the things, but i think i found a big problem. I update the wrong instance of my javafx app. I change the code with current code

He is a part of my server code

My Server Constructor
public Serveur(Mediator med){
    this.med=med;
    try {
        lancer();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}


Loop to catch message or file.
for(;;){
        try {
            Thread.sleep(1L);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("Attente de communication ...");

        try {
            socket = serverSocket.accept();
        } catch (IOException ex) {
            System.out.println("Can't accept client connection. ");
        }

        try {
            in = socket.getInputStream();
        } catch (IOException ex) {
            System.out.println("Can't get socket input stream. ");
        }

        byte[] bytes = new byte[16*1024];           
        while ((count = in.read(bytes)) > 0) {} 

        String mode = new String(bytes);
        String[] split = mode.split(";");
        if(split[0].compareTo("Message")==0){
            recevoirMessage();
        } else if(split[0].compareTo("Fichier")==0){
            recevoirFichier(split[2]);
        }       
        in.close();
        socket.close();

    }

When i receive a message i go to this function :

public void recevoirMessage() {
    output = new ByteArrayOutputStream();
    try {
        socket = serverSocket.accept();
    } catch (IOException ex) {
        System.out.println("Can't accept client connection. ");
    }
    try {
        in = socket.getInputStream();
    } catch (IOException ex) {
        System.out.println("Can't get socket input stream. ");
    }
    byte[] bytes = new byte[16*1024];
    try {
        while ((count = in.read(bytes)) > 0) {}
    } catch (IOException e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
    }
    System.out.println("Message reçus");
    String recu = "Message : "+new String(bytes);
    System.out.println(recu);
    Platform.runLater(() -> {
        Label mes = new Label(new String(bytes));
        med.cli.flowMessage.getChildren().add(mes);
    });
    try {
        in.close();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

And in my main i have only empty constructor like

 public Main(){}

In my UI class i have this to create my App :

'@Override
public void start(Stage primaryStage) {
    try {
        this.pStage = primaryStage;
        this.pStage = new Stage();
        idPane = new BorderPane();
        Parent page;
        page = FXMLLoader.load(getClass().getResource("/application/application.fxml"));
        this.pStage.setTitle("Messagerie");
        Scene scene = new Scene(page);
        flowMessage = new TextFlow();
        idContenuMessage= new ChoiceBox<String>();
        scrollPane= new ScrollPane();
        //flowMessage = new TextFlow();
        String css = this.getClass().getResource("application.css").
 toExternalForm();
                scene.getStylesheets().clear();
                scene.getStylesheets().add(css);
        this.pStage.setScene(scene);
        this.pStage.setResizable(false);
        this.pStage.show();
        this.pStage.setOnCloseRequest(event ->  {
            Serveur.close();
        });
    } catch (IOException e) {
        e.printStackTrace();
    }
}'

And i don't know how to update my UI TextFlow in my server Class.

I saw different things like the Mediator Pattern, i try this but it didn't work ( maybe i do something wrong ).

I start my app with this class :

package communication;

import application.Main;
import javafx.application.Application;
import javafx.stage.Stage;

public class Mediator extends Application implements Runnable {

private Serveur serv;

public Main cli;

public Thread thread;

private Stage primaryStage;

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

public Mediator(){
    cli = new Main();
    thread = new Thread(this,"serv");   
    thread.start(); 
}   

@Override
public void run() {
    setServ(new Serveur(this));
}

@Override
public void start(Stage stage) throws Exception {
    primaryStage = stage;
    cli.start(primaryStage);
}

public Serveur getServ() {
    return serv;
}

public void setServ(Serveur serv) {
    this.serv = serv;
}   
}

Thanks for Helping.

2

There are 2 best solutions below

6
On

I would use Platform.runLater() to put your GUI changes on the Javafx application thread, e.g.

public void recevoirMessage() {
output = new ByteArrayOutputStream();
try {
    socket = serverSocket.accept();
} catch (IOException ex) {
    System.out.println("Can't accept client connection. ");
}
try {
    in = socket.getInputStream();
} catch (IOException ex) {
    System.out.println("Can't get socket input stream. ");
}
byte[] bytes = new byte[16*1024];
try {
    while ((count = in.read(bytes)) > 0) {}
} catch (IOException e1) {
    // TODO Auto-generated catch block
    e1.printStackTrace();
}
System.out.println("Message reçus");
String recu = "Message : "+new String(bytes);
System.out.println(recu);

//inserted here
Platform.runLater(new Runnable() {
        @Override public void run() {
            //HERE I WANT TO UPDATE MY TEXTFLOW
        }
    });


try {
    in.close();
} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}   

}

5
On

Firstly TextFlow does not have text property so you can't set that value to it. Add Text object to text flow:

Text text = new Text();    
flowMessage = new TextFlow(text);

Then create StringProperty, bind it to Text component textProperty and update this value in Server class.

Application class:

public class Mediator extends Application implements Runnable {

    private Serveur serv;

    public Main cli;

    private StringProperty textProperty = new SimpleStringProperty("text");

    public Thread thread;

    private Stage primaryStage;

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

    public Mediator(){
        cli = new Main(this,textProperty);
        thread = new Thread(this,"serv");   
        thread.start(); 
    }   

    @Override
    public void run() {
        serv = new Serveur(this,textProperty);
    }

    @Override
    public void start(Stage stage) throws Exception {
        primaryStage = stage;
        cli.start(primaryStage);
    }   
}

Pass textProperty to Main and Serveur classes.

cli = new Main(this, textProperty);
serv = new Serveur(this, textProperty);

Bind text property to Text component:

text.textProperty().bind(textProperty);

Finally update textProperty in JavaFX Application Thread in your Serveur class:

public void recevoirMessage() {
    output = new ByteArrayOutputStream();
    try {
        socket = serverSocket.accept();
    } catch (IOException ex) {
        System.out.println("Can't accept client connection. ");
    }
    try {
        in = socket.getInputStream();
    } catch (IOException ex) {
        System.out.println("Can't get socket input stream. ");
    }
    byte[] bytes = new byte[16*1024];
    try {
        while ((count = in.read(bytes)) > 0) {}
    } catch (IOException e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
    }
    System.out.println("Message reçus");
    String recu = "Message : "+new String(bytes);
    System.out.println(recu);

    //inserted here
    Platform.runLater(new Runnable() {
            @Override public void run() {
                //HERE I WANT TO UPDATE MY TEXTFLOW
                textProperty.setValue(new String(bytes));
            }
        });


    try {
        in.close();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }   
}