【问题标题】:Using JFrame from another class使用另一个类的 JFrame
【发布时间】:2018-08-17 03:27:11
【问题描述】:

您好,我在尝试将一些文本附加到同一包中另一个类的 JTextArea 时遇到了一个小问题。下面是属于 JFrame 的主要类:

  public class Client extends JFrame{

        //Static variables
        private static final String host = "localhost";
        private static final int portNumber = 4444;

        //Variables
        private String userName;

        //JFrame Variables
        private JPanel contentPanel;
        private JTabbedPane panel_Social;
        private JPanel jpanel_Social;
        private JPanel jpanel_Chat;
        private JTextArea textArea_Receive;
        private JTextField textField_Send;
        private JTextArea textArea_ClientList;
        private JButton btn_Enter;


        public JTextArea getTextArea_Receive(){
            return this.textArea_Receive;
        }

        //Constructor
        private Client(String userName, String host, int portNumber){
            this.userName = userName;
            this.serverHost = host;
            this.serverPort = portNumber;
        }

        public void main(String args[]){
            //Requests user to enter name
            String readName = null;
            Scanner scan = new Scanner(System.in);
            System.out.println("Please enter username");
            readName = scan.nextLine();

            //Start client
            Client client = new Client(readName, host, portNumber);
            client.startClient(scan);
        }

        private void startClient(Scanner scan){
            try{
                //Create new socket and wait for network communication
                Socket socket = new Socket(serverHost, serverPort);
                Thread.sleep(1000);

                //Create thread and start it
                serverThread = new ServerThread(socket, userName);
                Thread serverAccessThread = new Thread(serverThread);
                serverAccessThread.start();
            }
        }
    }


Below is the serverThread class

    public class ServerThread implements Runnable{

        private Socket socket;
        private String userName;
        private boolean isAlived;
        private final LinkedList<String> messagesToSend;
        private boolean hasMessages = false;

        //Constructor
        ServerThread(Socket socket,  String userName){
            this.socket = socket;
            this.userName = userName;
            messagesToSend = new LinkedList<String>();
        }

        public void run(){   
            try{
                Client test1 = new Client();
                JTextArea test2 = test1.getTextArea_Receive();
                String test3 = "Hello World";
                test2.append(test3);
            } catch (IOException e){
}
    }

我包含测试变量只是为了轻松地重新创建问题,但是每当运行附加函数时,jFrame 的文本区域中不会出现任何内容。在我的场景中,我让客户端从服务器接收文本,然后将其附加到文本框中。

顺便说一句,我正在为 JFrame 使用 IntelliJ GUI 设计器。我只包含了重新创建问题所需的代码。我仍在尝试创建 MCVE 问题,因此请随时告诉我我犯的错误。

【问题讨论】:

    标签: java swing jframe


    【解决方案1】:

    append 函数运行,jFrame 的文本区域中没有出现任何内容

    您的问题中没有足够的信息来确定为什么会发生这种情况,但是,您需要考虑一些重要的信息。

    1. Swing 是单线程的,不是线程安全的
    2. 您希望尽可能地解耦代码

    基本上,第一点意味着您不应该在事件调度线程中运行任何长时间运行或阻塞的进程,并且您不应该从上下文之外修改 UI(或 UI 依赖的任何状态)事件调度线程

    首先查看Concurrency in Swing了解更多详情。

    第二点意味着,即使给定部分代码,您也想问“用其他实现替换它有多难?” - 如果工作量让你害怕,那么你的代码可能是紧耦合的。

    为此,我开始...

    public interface Client {
        public void append(String message);
    }
    

    这确实是基本的,但这意味着某些组件可以向其他组件发送消息,并且除了此功能之外,任何一方都不应该相互关心。

    接下来,我查看了ServerThread。基本上,此类负责管理Socket 并将消息传递到Client。由于 Swing 的要求,我使用了SwingWorker。这允许我在后台线程上运行Socket 代码,但确保消息在事件调度线程的上下文中传递到Client

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.net.Socket;
    import java.util.List;
    import javax.swing.SwingWorker;
    
    public class ServerThread extends SwingWorker<Void, String> {
    
        private String host;
        private int port;
        private Client client;
    
        //Constructor
        ServerThread(String host, int port, Client client) {
            this.host = host;
            this.port = port;
            this.client = client;
        }
    
        @Override
        protected void process(List<String> chunks) {
            for (String message : chunks) {
                client.append(message);
            }
        }
    
        @Override
        protected Void doInBackground() throws Exception {
            try (Socket socket = new Socket(host, port)) {
                try (BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
                    String text = null;
                    while ((text = reader.readLine()) != null) {
                        System.out.println(text);
                        if (text.equals("<stop>")) {
                            break;
                        }
                        publish(text);
                    }
                }
            } catch (IOException exp) {
                exp.printStackTrace();
                publish("Failed to establish connection to " + host + ":" + port + " - " + exp.getMessage());
            }
            return null;
        }
    }
    

    然后是实际的 UI 客户端本身...

    import java.awt.BorderLayout;
    import java.awt.EventQueue;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JScrollPane;
    import javax.swing.JTextArea;
    
    public class Main {
    
        public static void main(String[] args) {
            new Main();
        }
    
        //Constructor
        public Main() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                        ClientUI clientUI = new ClientUI();
    
                        ServerThread thread = new ServerThread("localhost", 4321, clientUI);
                        thread.execute();
    
                        JFrame frame = new JFrame("Client");
                        frame.add(clientUI);
                        frame.pack();
                        frame.setLocationRelativeTo(null);
                        frame.setVisible(true);
                }
            });
        }
    
        public class ClientUI extends JPanel implements Client {
    
            private JTextArea ta;
    
            public ClientUI() {
                setLayout(new BorderLayout());
                ta = new JTextArea(10, 20);
                add(new JScrollPane(ta));
            }
    
            @Override
            public void append(String message) {
                ta.append(message + "\n");
            }
    
        }
    }
    

    这里不多

    最后,我编写了一个简单的Server 测试代码,它只是将当前日期/时间作为String 发送到连接的客户端。

    当我说简单时,我的意思是简单。根据设计,这旨在通过单个客户端连接进行判断,并且仅用于测试上述代码

    import java.io.BufferedWriter;
    import java.io.IOException;
    import java.io.OutputStreamWriter;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.text.DateFormat;
    import java.util.Date;
    
    public class Server {
    
        public static void main(String[] args) {
            DateFormat format = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG);
            try (ServerSocket server = new ServerSocket(4321)) {
                Socket socket = server.accept();
                System.out.println("Connected");
                try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()))) {
                    while (true) {
                        System.out.println("Write >> ");
                        writer.write(format.format(new Date()));
                        writer.newLine();
                        writer.flush();
                        Thread.sleep(1000);
                    }
                }
            } catch (IOException | InterruptedException exp) {
                exp.printStackTrace();
            }
        }
    }
    

    要全部测试,请启动 Server,然后运行 ​​Main

    【讨论】:

    • 我不需要配置 UI,因为这是通过 UI 设计器和 .form 文件中的所有设置完成的。我的服务器类也是多线程的,可以接受多个客户端。所以我在那里很好。我不明白 serverThread 中的 process 函数以及它在做什么。同样在 doInBackground 中,我对发布(文本)是什么感到困惑。谢谢你的解释,虽然它帮助我理解了很多
    • 那么你应该通读链接的文档——包括Worker Threads and SwingWorker
    • 这行得通。一旦扫描仪看到我进行了输入,我的主文件中的语句就会更改为 true。在 ServerThread 中为真后,我拥有它,以便它将我所做的输入写入服务器。但是,这不是执行这些指令。
    【解决方案2】:

    您应该通过构造函数将Client 传递给ServerThread。您在run() 中实例化的Client 与您在main() 中创建的Client 的引用不同。所以你的ServerThread 类会是这样的

    ServerThread(Client client, Socket socket,  String userName) {
        this.client = client;
        this.socket = socket;
        this.userName = userName;
        messagesToSend = new LinkedList<String>();
    }
    
    public void run() {
        try
        {
            JTextArea test2 = this.client.getTextArea_Receive();
            String test3 = "Hello World";
            test2.append(test3);
        } 
        catch (IOException e)
        {}
    }
    

    您的 startClient() 方法将更新为类似的内容

    private void startClient(Client client, Scanner scan)
    {
        try
        {
            //Create new socket and wait for network communication
            Socket socket = new Socket(serverHost, serverPort);
            Thread.sleep(1000);
    
            //Create thread and start it
            ServerThread serverThread = new ServerThread(client, socket, userName);
            serverAccessThread.run();
        }
    }
    

    说了这么多,

    我建议将您的 main() 移出 Client 并移到与 Swing UI 代码不那么耦合的类中。像这样的:

    public class MySwingApplication {
    
        private static final String host = "localhost";
        private static final int portNumber = 4444;
    
        public static void main(String[] args) {
            // Requests user to enter name
            // Start client
        }
    }
    

    然后你的Client 被构建得更像一个实例对象

    public class Client extends JFrame {
        public JTextArea getTextArea_Receive(){
            // Return the text area
        }
    
        // Constructor -- public to allow instantiation from main()
        public Client(String userName, String host, int portNumber) {
            // Do stuff
        }
    
        private void startClient(Scanner scan) {
            // Show the JFrame on screen
            // Spawn Server
        }
    }
    

    【讨论】:

    • 这很好用。我可以在调试器中看到所有信息都匹配,但 TextArea 仍然没有显示我发送给它的文本。另外,您为什么建议将我的主要业务移出客户? @j.seashell
    • 我不确定您的文本为什么没有更新。您可能需要查看某种刷新/重新渲染,抱歉,我无法提供更多帮助。但是关于main()...您的Client 对象实际上并不需要保持状态(static)。实际上,您将拥有一个与您的 Swing UI 无关的类,它包含您的main()。然后,您将从该 main() 方法启动客户端。我将用我所描述的单独部分更新我的答案
    • 我们是否也能认识到 op 和您的解决方案都违反了 Swing 的单线程规则
    • @MadProgrammer 是的。我只是不想卷入这一切。答案变得很长。
    • @Lucky38i “但是 TextArea 仍然没有显示我发送给它的文本” - 你确定客户端正在接收文本吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-09-04
    • 2013-06-21
    • 1970-01-01
    • 1970-01-01
    • 2014-03-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多