Небрежные шаблоны проектирования для обновления графического интерфейса из другого потока в Java

Какой шаблон проектирования лучше всего подходит для обновления графического интерфейса при работе с другими потоками в Java (Swing)?

Например, представьте себе объект (например, пользовательскую панель JPanel), у которого есть JList, поддерживающий DefaultListModel. Поток, прослушивающий сокет, может получить данные, а затем хочет обновить JList на основе информации, полученной в сокете.

Я понимаю SwingUtilities.invokeLater, но это похоже на неуклюжий код, потому что на самом деле у меня есть много разных функций, которые можно вызывать (из потоков, отличных от EDT), которые манипулируют различными компонентами графического интерфейса.

Идея, о которой я подумал, заключается в создании какой-то системы обмена сообщениями с 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]);
            }
            
        }
    }
}

person Matthew    schedule 16.07.2020    source источник
comment
Кстати, функция Записи появилась в Java 16 и была предварительно представлена ​​в Java 14 и 15. можно использовать вместо использования массива Object в качестве кортежа.   -  person Basil Bourque    schedule 16.07.2020


Ответы (1)


Вы заново изобретаете очередь событий, которая в первую очередь заставляет работать графический интерфейс пользователя. Уже существует очередь, в которую вы можете добавлять новые сообщения, реализованная в 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]);
                    }

                }
            }
        }

Лично я бы создал отдельные классы Runnable для каждого типа сообщения, которое вы хотите отправить, вместо одного общего GUIUpdateArrayHandler: например, AppendHandler для MessageType.APPEND, SetHandler для MessageType.SET, но если вы считаете, что будет менее небрежно размещать их в одном месте в одном обработчике , вам решать.

person Joni    schedule 16.07.2020