【问题标题】:How to append text to a TextArea using ObservableList如何使用 ObservableList 将文本附加到 TextArea
【发布时间】:2019-03-25 17:01:22
【问题描述】:

我正在尝试实现一个交换消息的客户端-服务器 JavaFx 应用程序。我正在尝试将文本(服务器端)从处理客户端请求的线程附加到 TextArea,该线程通过放置在与控制器通信的模型中的方法传递客户端请求。

例如: 在处理客户端请求的线程中,如果用户成功登录 --> 我调用模型中的方法 setLogMessages("user logged in.") 并设置我的 observableList -- > 字符串被发送到控制器,控制器应该将它们附加到 TextArea。

我写了这段代码:

服务器模型

public class ServerModel {

        private ObservableList<String> observableLogMessages;

        //other code

        /**
        * This method starts the server connection.
        */
        public void startServerConnection() {
           serverRequests.serverConnection();
        }

        public void setLogMessages(String logMessage) {

          observableLogMessages = FXCollections.observableArrayList();
          ArrayList<String> messagesList = new ArrayList<>();
          messagesList.add(logMessage);
          observableLogMessages.setAll(messagesList);
        }

        public ObservableList<String> getLogMessages() {
            return observableLogMessages;
        }

        public void changeObserver(ListChangeListener<String>messagesChangeListener) {
           observableLogMessages.addListener(messagesChangeListener);
        }

// other code

}

服务器控制器

public class LogController {

private final ServerModel serverModel = ServerModel.builServerModel();

@FXML
private ResourceBundle resources;

@FXML
private URL location;

@FXML
private AnchorPane logAnchorPane;

@FXML
public TextArea logTextArea;

@FXML
private JFXButton logClearButton;

@FXML
void initialize() {
    logTextArea.setEditable(false);

    serverModel.changeObserver((ListChangeListener.Change<? extends String> messagesChange) -> {
        if (messagesChange.next() && messagesChange.wasAdded()) {
            ObservableList<String> logMessages = serverModel.getLogMessages();
            for (int i = 0; i < logMessages.size(); i++) {
                logTextArea.appendText(logMessages.get(i)); 
            }
        }
    });
}

}

处理客户端请求的线程

public class ServerRequests {

   //other code 

   public void serverConnection() {
      ServerSocket serverSocket = null;

    try {
        //initialize the Server Socket class
        serverSocket = new ServerSocket(PORT);
    } catch (IOException e) {
        System.out.println("I/O error: " + e);
    }
    while (true) {
        System.out.println("waiting for clients...");
        try {
            clientSocket = serverSocket.accept(); 
            ClientRequest clientRequest = new ClientRequest(clientSocket);
            Runnable runnable = clientRequest;
            Thread thread = new Thread(runnable);
            thread.setDaemon(true);
            thread.start();

        } catch (IOException e) {
            System.out.println("I/O error: " + e);
        } catch (ClassNotFoundException ex) {
            Logger.getLogger(ServerRequests.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

 public class ClientRequest extends Thread {
      // other code
      public ClientRequest(Socket socket) throws ClassNotFoundException {
             // other code
      }

 @Override
    public void run() {

      // if user successfully login add, this message to the TextArea.
      serverModel.setLogMessages("User successfully connected.");
    }
    // other code
  }
}

如果我运行此代码,则会显示“用户成功连接”消息。 未添加到 TextArea 并且我还尝试在模型中打印 observableList 大小,即使我尝试添加多条消息,该大小也始终保持为 1。 我在这里阅读了许多类似的问题并尝试使用 Platform.runLater() 进行修复,但我的 TextArea 仍然没有发生任何事情,我不明白为什么!

更新

我试图在我的日志上添加一个按钮,只是为了查看是否在单击时返回一条消息。我发现我什至无法单击它,因为一切都卡住了(服务器端),它给了我“Java(TM) Platform SE 二进制文件已停止工作”错误消息。这是什么原因造成的?

LogController 更新后建议

public class LogController {

//serverModel
ObservableList<String> logMessages;

@FXML
private AnchorPane logAnchorPane;

@FXML
private TextArea logTextArea;

@FXML
private Label serverLogLabel;

@FXML
private JFXButton logClearButton;

@FXML
void initialize() {
    logTextArea.setEditable(false);
    logTextArea.appendText("Appending");   //This append text correctly
    Platform.runLater(() -> {
        serverModel.changeObserver((ListChangeListener.Change<? extends String> messagesChange) -> {
            if (messagesChange.next() && messagesChange.wasAdded()) {
                ObservableList<String> logMessages = serverModel.getLogMessages();
                for (int i = 0; i < logMessages.size(); i++) {
                    logTextArea.appendText(logMessages.get(i));
                }
            }
        });
    });
    logClearButton.setOnAction((ActionEvent event) -> {
        System.out.println("Clear clicked");
    });

}

}

仍然没有在 TextArea 上打印,并且仍然卡在打开 +“Java(TM) Platform SE 二进制文件已停止工作”错误消息。

【问题讨论】:

  • 不得从 fx 应用程序线程更改场景图中的任何内容:在您的特定上下文中,将侦听器中区域中的文本更新到已更改的日志列表在后台线程上。将该设置包装到 Platform.runlater() 应该可以工作
  • @kleopatra 你在说哪个设置? logTextArea.appendText(logMessages.get(i));或 serverModel.setLogMessages("用户连接成功。"); ?我尝试在 Platform.RunLater 中添加 LogController 的所有 serverModel.changeObserver() 但仍然无法正常工作。你能给我一个例子吗?也许我以错误的方式使用 Platform.runLater()。
  • 日志消息列表中只有一个 ListChangeListener .. ;)
  • 更改您的示例 - 并确保它是 minimal reproducible example
  • 错了——你不必包装监听器的注册,而是监听器更新 textArea 的行

标签: java multithreading javafx model-view-controller


【解决方案1】:

我注意到的一个可能的问题是,您每次都在为新消息创建一个新的 observable 列表,并使用 setAll 为您的 observableList 设置一个列表。因此,使用此实现,您永远不会让 observableList 拥有超过 1 个项目。

你需要在外面创建 observableList 实例并使用 addAll() 或者将消息直接添加到 observableList 中。

private ObservableList<String> observableLogMessages = FXCollections.observableArrayList();    
public void setLogMessages(String logMessage) {
       ArrayList<String> messagesList = new ArrayList<>();
       messagesList.add(logMessage);
       observableLogMessages.addAll(messagesList);
    }

private ObservableList<String> observableLogMessages = FXCollections.observableArrayList();
public void setLogMessages(String logMessage) {
   observableLogMessages.add(logMessage);
}

【讨论】:

  • 你是对的!我的坏..现在模型的 ObservableList 正确地填充了字符串 - 仍然需要找出它为什么不打印在我的 TextArea 上。
猜你喜欢
  • 2011-10-07
  • 1970-01-01
  • 2021-06-01
  • 1970-01-01
  • 2016-08-04
  • 2018-09-25
  • 1970-01-01
  • 2016-08-28
  • 1970-01-01
相关资源
最近更新 更多