【问题标题】:Add elements on TextFlow using external Thread in JavaFX在 JavaFX 中使用外部线程在 TextFlow 上添加元素
【发布时间】:2017-04-10 21:45:18
【问题描述】:

我的程序向服务器发送一条消息,该消息显示在一个窗口中,并从一个窗口接收消息,该窗口也添加到客户端窗口中。

但我添加的程序并没有显示从服务器收到的消息:

textFlow.getChildren().add(text);接受 线程中。

我该怎么办?

public class NewClient extends Application {

Thread send;
Thread accept;

DatagramPacket pack;
DatagramSocket sock;
private List<String> str;
String name , sname;
int listeningPort;
InetAddress server_ip;

String sender;
ScrollPane sp = new ScrollPane();
TextFlow textFlow = new TextFlow();
TextField userTextField = new TextField();
TextField fTextField = new TextField();

private String message;
private String recip = null;
Button button = new Button("Send");

volatile boolean st = false;
Stage theStage;

public Scene Chat_Box() {

    TextFlow textFlow = new TextFlow();
    textFlow.setPadding(new Insets(10));
    textFlow.setLineSpacing(10);
    TextField textField = new TextField();
    textField.setPrefSize(50, 30);
    Button button = new Button("Send");
    button.setPrefSize(80, 30);
    VBox container = new VBox();
    VBox box = new VBox();
    box.getChildren().addAll(sp, textFlow);
    container.setPadding(new Insets(10));
    container.getChildren().addAll(box, new HBox(textField, button));
    VBox.setVgrow(sp, Priority.ALWAYS);
    VBox.setVgrow(textFlow, Priority.ALWAYS);

    textField.prefWidthProperty().bind(container.widthProperty().subtract(button.prefWidthProperty()));

    textField.setOnKeyPressed(e -> {
        if (e.getCode() == KeyCode.ENTER) {
            button.fire();
        }
    });
    button.setOnAction(e -> {
        st = true;
        Text text;
        if (textFlow.getChildren().size() == 0) {
            text = new Text(textField.getText());
            message = textField.getText();
            System.out.println(message);
        } else {
            message = textField.getText();
            text = new Text("\n" + textField.getText());
        }
        if (textField.getText().contains(":)")) {
            ImageView imageView = new ImageView(
                    "http://files.softicons.com/download/web-icons/network-and-security-icons-by-artistsvalley/png/32x32/Regular/Friend%20Smiley.png");
            // Remove :) from text
            text.setText(text.getText().replace(":)", " "));
            textFlow.getChildren().addAll(text, imageView);
        } else {
            textFlow.getChildren().add(text);
        }
        textField.clear();
        textField.requestFocus();
    });
    VBox vb = new VBox();
    vb.getChildren().addAll(textFlow);
    sp.setVmax(440);
    sp.setPrefSize(400, 300);
    sp.setContent(vb);
    sp.vvalueProperty().bind((ObservableValue<? extends Number>) vb.heightProperty());
    Scene scene2 = new Scene(container, 400, 300);
    return scene2;
}

public void start(Stage stage) throws IOException {
    this.str = getParameters().getRaw();
    name = str.get(0);
    listeningPort = Integer.parseInt(str.get(1));
    server_ip = InetAddress.getByName(str.get(2));
    theStage = stage;
    Scene scene = Chat_Box();
    stage.setScene(scene);
    stage.show();
    send = new Thread() {

        public void run() {
            DatagramSocket sock = null;
            try {
                sock = new DatagramSocket();
            } catch (SocketException ex) {
                Logger.getLogger(NewClient.class.getName()).log(Level.SEVERE, null, ex);
            }
            while (true) {
                InetAddress host = server_ip;
                try {
                    if (st && ( message != null )) {
                        String in = message;
                        byte[] data = new byte[1024];
                        data = in.getBytes();
                        DatagramPacket sendPack = new DatagramPacket(data, data.length);
                        sendPack.setPort(5050);
                        sendPack.setAddress(host);
                        sock.send(sendPack);
                        st = false;
                    }
                } catch (Exception e) {
                    System.out.println(e);
                }
            }
        }

    };
    send.start();
    accept = new Thread() {

        public void run() {
            try {
                sock = new DatagramSocket(listeningPort);
            } catch (SocketException e) {
                e.printStackTrace();
            }
            while (true) {
                byte[] data = new byte[1024];
                pack = new DatagramPacket(data, data.length);
                try {
                    sock.receive(pack);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                String incoming = null;
                try {
                    incoming = new String(data, "UTF-8");
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                Text text = new Text(incoming);
                textFlow.getChildren().add(text);
            }
        }
    };
    accept.start();
}

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

}

【问题讨论】:

  • 仍在尝试从不是应用程序线程的线程更新 UI?
  • 那么我怎样才能接收消息并将其附加到我的文本字段?我是 javaFX 的新手,你能告诉我应该在哪里进行更改以使其正常工作吗?
  • @fabian 谢谢,但你能建议我在哪里调用 platform.runlater() 吗?

标签: java user-interface javafx textfield


【解决方案1】:

你应该使用:

Platform.runLater(()->{

 Text text = new Text(incoming);
 textFlow.getChildren().add(text);

});

SceneGraph 不允许从其他 Threads 更改。只有JavaFX main Thread 可以修改SceneGraph。

所以调用这个方法你把这个Runnable对象发送到QUEUE,它会被JavaFX Main Thread调用来修改场景图。


什么是场景图?

JavaFX 中的 GUI 被构建为场景图。场景图是一个 视觉元素的集合,称为节点,按层次结构排列 时尚。使用公共 JavaFX API 构建场景图。


如何在 JavaFX 中管理事件?

在 JavaFX 中,事件队列由单个操作系统级别管理 线程称为 JavaFX 应用程序线程。所有用户输入事件都是 在 JavaFX 应用程序线程上调度。 JavaFX 要求一个 必须仅在 JavaFX 应用程序上修改实时场景图 线程。


我应该在哪里使用 Platform.runLater(()->{....});

您从外部修改实时场景图的每一个地方 Thread 不是 JavaFX Main Thread

额外链接:

How JavaFX application thread works?

Platform.runLater and Task in JavaFX

【讨论】:

  • @IAmBlake 您可以从任何外部 Thread 调用它。代码将在 JavaFX Main Thread 几毫秒后执行。看看Fabian的评论链接。
  • 我在接受线程中添加了该部分,但它仍然不起作用
  • @IAmBlake 您在external Thread 中修改SceneGraph 的任何地方都应该调用Platform.runLater(....) 中的代码
  • 你能编辑你的答案,告诉我我应该在哪里进行更改吗?
  • @IAmBlake 这里似乎你甚至没有启动应用程序,因为它需要启动......这是你唯一使用的类吗?
猜你喜欢
  • 2017-07-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多