2

在处理 Java (Swing) 中的其他线程时,哪种设计模式最适合更新 GUI?

例如,假设一个对象(如自定义 JPanel)有一个 JList,而 DefaultListModel 支持它。一个在 Socket 上监听的线程可以接收数据,然后想要根据来自套接字的信息更新 JList。

我了解 SwingUtilities.invokeLater,但这似乎是笨拙的代码,因为实际上我有许多不同的函数可以调用(从非 EDT 线程)来操作不同的 GUI 组件。

我想到的想法是创建某种带有 ArrayBlockingQueue 的消息传递系统。基本上我实现了 Runnable 并在 SwingUtilities.invokeLater 方法调用中传入this. 然后该方法被执行,但它并不真正知道该做什么,但这就是我从线程安全的 ArrayBlockingQueue 中弹出“消息”的地方。

还有比这更好的设计模式吗?我的基本 JPanel 类

public class JPanelGUIThread extends JPanel implements Runnable
{
    protected ArrayBlockingQueue<Object> guiUpdateMessages;
    
    public JPanelGUIThread()
    {
        guiUpdateMessages = new ArrayBlockingQueue<Object>(10);
    }
    
    @Override
    public void run()
    {
        while(guiUpdateMessages.size() > 0)
        {
            try
            {
                Object data = guiUpdateMessages.take();
                
                if(data instanceof Object[])
                {
                    handleGUIUpdateArray((Object[])data);
                }
                else
                {
                    handleGUIUpdateObject(data);
                }
                
            } 
            catch (InterruptedException e)
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            
        }
    }
    
    public void handleGUIUpdateArray(Object[] objectArray)
    {
        
    }
    public void handleGUIUpdateObject(Object object)
    {
        
    }
}

我的主要JPanel



    
    public JLabel getChatLabel()
    {
        return chatLabel;
    }

    public JTextArea getChatArea()
    {
        return chatArea;
    }

    public JScrollPane getChatScrollPane()
    {
        return chatScrollPane;
    }

    public JTextField getMychat()
    {
        return mychat;
    }

    public JButton getSendButton()
    {
        return sendButton;
    }

    //This method is called from the EDT, so no need to perform adding messages
    @Override
    public void actionPerformed(ActionEvent e)
    {
        if(e.getSource() == sendButton)
        {
            client.sendChatInformation(mychat.getText());
            mychat.setText("");
        }
    }

    public void clearOldChat()
    {
        Object[] data = new Object[3];
        data[0] = chatArea;
        data[1] = MessageType.SET;
        data[2] = "";
        guiUpdateMessages.add(data);
        SwingUtilities.invokeLater(this);
    }


    @Override
    public void handleGUIUpdateArray(Object[] objectArray)
    {
        if(objectArray[0] == chatArea)
        {
            if(objectArray[1] == MessageType.APPEND)
            {
                chatArea.append((String) objectArray[2]);
            }
            else if(objectArray[1] == MessageType.SET)
            {
                chatArea.setText((String) objectArray[2]);
            }
            
        }
    }
}
4

1 回答 1

3

您正在重新发明首先使图形用户界面工作的“事件队列”。已经有一个队列,您可以在其中添加新消息,在java.awt.EventQueue类中实现。

将消息添加到事件队列的便捷方法是使用SwingUtilities.invokeLater(Runnable). 您传入的 Runnable 实例应包含处理事件所需的所有信息。更好的是:由于它是一个 Runnable,它可以封装您需要运行以处理事件的代码。

例如:以下是如何将一般的“对象数组”消息封装在 Runnable 中,并将其添加到事件队列中。

        public void clearOldChat() {
            Object[] data = new Object[3];
            data[0] = chatArea;
            data[1] = MessageType.SET;
            data[2] = "";
            SwingUtilities.invokeLater(new GUIUpdateArrayHandler(data));
        }

        class GUIUpdateArrayHandler implements Runnable {

            Object[] objectArray;

            public GUIUpdateArray(Object[] objectArray) {
                this.objectArray = objectArray;
            }

            public void run() {
                if (objectArray[0] == chatArea) {
                    if (objectArray[1] == MessageType.APPEND) {
                        chatArea.append((String) objectArray[2]);
                    } else if (objectArray[1] == MessageType.SET) {
                        chatArea.setText((String) objectArray[2]);
                    }

                }
            }
        }

就个人而言,我会为您要发送的每种类型的消息创建单独的“可运行”类,而不是一个通用GUIUpdateArrayHandler类:例如AppendHandlerfor MessageType.APPENDSetHandlerfor MessageType.SET,但如果您认为将它们放在同一个处理程序中的同一个位置“不那么草率”,由你决定。

于 2020-07-16T05:00:56.560 回答