简体   繁体   English

从 Java 中的不同线程更新 GUI 的非马虎设计模式

[英]Non Sloppy design patterns for updating GUI from a different thread in Java

What design pattern is best for updating the GUI when dealing with other threads in Java (Swing)?在处理 Java (Swing) 中的其他线程时,哪种设计模式最适合更新 GUI?

For example, imagine an Object (like a custom JPanel) that has a JList that has a DefaultListModel supporting it.例如,想象一个 Object(如自定义 JPanel),其 JList 具有支持它的 DefaultListModel。 A threading listening on a Socket can receive data and then wants to update the JList from the information that came in on the socket.一个在 Socket 上监听的线程可以接收数据,然后想要根据来自套接字的信息更新 JList。

I understand the SwingUtilities.invokeLater, but that seems like sloopy code, because in reality I have many different functions that can be called (from non EDT threads) that manipulate different GUI components.我了解 SwingUtilities.invokeLater,但这似乎是笨拙的代码,因为实际上我有许多不同的函数可以调用(从非 EDT 线程)来操作不同的 GUI 组件。

The idea that I thought of is creating some kind of messaging system with an ArrayBlockingQueue.我想到的想法是创建某种带有 ArrayBlockingQueue 的消息传递系统。 Basically I implement Runnable and in the SwingUtilities.invokeLater method call I pass in this .基本上我实现了 Runnable 并在 SwingUtilities.invokeLater 方法调用中传递了this Then the method gets executed, but it doesn't really know what to do, but that is where I pop the "messages" from the thread safe ArrayBlockingQueue.然后该方法被执行,但它并不真正知道该做什么,但这就是我从线程安全的 ArrayBlockingQueue 中弹出“消息”的地方。

Is there a better design pattern than this?还有比这更好的设计模式吗? My base JPanel Class我的基础JPanel Class

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)
    {
        
    }
}

My main JPanel我的主要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]);
            }
            
        }
    }
}

You are reinventing the "event queue" that makes a graphical user interface work in the first place.您正在重新发明首先使图形用户界面工作的“事件队列”。 There already is a queue where you can add new messages, implemented in the java.awt.EventQueue class.已经有一个队列可以添加新消息,在java.awt.EventQueue class 中实现。

The convenient way to add a message to the event queue is with SwingUtilities.invokeLater(Runnable) .将消息添加到事件队列的便捷方法是使用SwingUtilities.invokeLater(Runnable) The Runnable instance that you pass in should contain all the information necessary to process the event.您传入的 Runnable 实例应包含处理事件所需的所有信息。 Even better: since it's a Runnable, it can encapsulate the code you need to run to process the event.更好的是:因为它是一个 Runnable,它可以封装您需要运行以处理事件的代码

For example: here is how you can encapsulate your general "object array" message within a Runnable, and add it to the event queue.例如:以下是如何将一般的“对象数组”消息封装在 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]);
                    }

                }
            }
        }

Personally, I would create separate "Runnable" classes for each type of message you want to send instead of one generic GUIUpdateArrayHandler : like AppendHandler for MessageType.APPEND , SetHandler for MessageType.SET , but if you think it's "less sloppy" to have them in the same place in a single handler, up to you.就个人而言,我会为您要发送的每种类型的消息创建单独的“可运行”类,而不是一个通用的GUIUpdateArrayHandler :例如MessageType.APPENDAppendHandlerMessageType.SETSetHandler ,但是如果您认为拥有它们“不那么草率”在同一个处理程序的同一个地方,由您决定。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM