首页 > 解决方案 > 无法附加到 JTextArea

问题描述

我正在尝试使用 Java 创建文本聊天。我有一个服务器和一个客户端,它们使用 Streams 相互连接,并使用 objectInputStream 和 objectOutputStream 发送数据。

我有客户端和服务器的 GUI。我使用 intellij 的 GUI 表单制作了这些 GUI。

服务器 GUI 表单图像

我遇到的问题是当我尝试向服务器的 GUI 显示文本时。如果我从 JTextField actionlistener 调用我的 relayToAll 方法,我可以附加到 GUI,然后将消息发送到所有客户端并在服务器 GUI 中打印出来。

如果我尝试从接收输入的位置调用相同的方法,则附加到文本区域不起作用。

谁能告诉我为什么它不附加?

谢谢

public class ServerTest {
private JTextField textField1;
private JTextArea textArea1;
private JPanel Panel;
static private ObjectOutputStream objectOutputStream;
static private ObjectInputStream objectInputStream;
static private Socket client;
static private ArrayList<Socket> clients = new ArrayList<Socket>();
static private ArrayList<ObjectOutputStream> objectOutputStreams = new ArrayList<>();

public void relayToAll(String message){
    try {
        for(int i = 0; i < clients.size(); i++) {
            ObjectOutputStream output = objectOutputStreams.get(i);
            output.writeObject(message);
            output.flush();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    appendTextArea(message);
}

public void appendTextArea(String text){
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            System.out.println("This should go to the Server GUI: " + text);
            textArea1.append(text + "\n");
        }
    });
}

public ServerTest() {

    textField1.addActionListener(e -> {
        System.out.println(e.getActionCommand());
        relayToAll(e.getActionCommand());
        textField1.setText("");
    });
}

public void ReadInput(ObjectInputStream input, int port){
    try {
        String oldMessage = "";
        while (true) {
            String message = (String) input.readObject();
            if (message != oldMessage){
                System.out.println(port + ": " + message);
                oldMessage = message;
                relayToAll(port + ": " + message);
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}


public void IOSetup(){
    try {
        ServerSocket serverSocket = new ServerSocket( 6969 );

        ExecutorService executor = Executors.newFixedThreadPool(5);

        System.out.println("server on\n");
        for (int i = 0; i < 5; i++){

            client = serverSocket.accept();
            clients.add(client);
            System.out.println("Connection from: "+ client.getPort());

            objectOutputStream = new ObjectOutputStream(client.getOutputStream());
            objectOutputStreams.add(objectOutputStream);

            objectInputStream = new ObjectInputStream(clients.get(i).getInputStream());


            executor.submit(() -> {
                ReadInput(objectInputStream, client.getPort());
            });
        }


    } catch (IOException e) {
        e.printStackTrace();
    }

}

public static void main(String[] args) {

    JFrame frame = new JFrame("Server");
    frame.setContentPane(new ServerTest().Panel);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.pack();
    frame.setVisible(true);

    ServerTest application = new ServerTest();


    application.IOSetup();

}

标签: javaswingjtextarea

解决方案


实际上你犯了一个愚蠢的错误。请检查下面的 (A) 和 (B) 行:

public static void main(String[] args) {
    JFrame frame = new JFrame("Server");
    frame.setContentPane(new ServerTest().Panel); //  *************** (A)
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.pack();
    frame.setVisible(true);
    ServerTest application = new ServerTest();  //  *************** (B)
    application.IOSetup();
}

你看到问题了吗?您正在创建两个ServerTest 对象,一个将其 Panel 变量添加到 JFrame 并显示,另一个设置用于 IO 通信。ActionListener 更改显示的 JTextArea 的状态,而 IO 通信更改第二个 ServerTest 实例中的 JTextArea 的状态,该实例未显示。

一项改进是只创建一个实例:

public static void main(String[] args) {

    ServerTest application = new ServerTest();  // create one instance

    JFrame frame = new JFrame("Server");
    // frame.setContentPane(new ServerTest().Panel);

    frame.setContentPane(application.Panel);     // and use in both places

    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.pack();
    frame.setVisible(true);

    //ServerTest application = new ServerTest();
    application.IOSetup();                 // and use in both places
}

其他问题:

  • 您在后台线程中长时间运行并阻塞了长时间运行的代码,这具有潜在的危险,而您的 GUI 没有被冻结的唯一原因是因为您在主线程上(错误地)启动了 GUI,并且脱离 Swing 事件线程。有关这方面的更多信息,您需要阅读 Swing 并发:课程:Swing 中的并发
  • 您将想要学习和使用Java 命名约定。变量名应全部以小写字母开头,而类名应以大写字母开头。学习这一点并遵循这一点将使我们更好地理解您的代码,并使您更好地理解其他人的代码。

推荐阅读