Pergunta

Eu tenho usado a seguinte abordagem para criar componentes e retornar valores de swing de/para fora do EDT. Por exemplo, o seguinte método pode ser uma extensão para JFrame, para criar um JPanel e adicione ao pai JFrame:

public JPanel threadSafeAddPanel() {

    final JPanel[] jPanel = new JPanel[1];

    try {
        EventQueue.invokeAndWait(new Runnable() {
            public void run() {
                jPanel[0] = new JPanel();
                add(jPanel[0]);
            }
        });
    } catch (InterruptedException ex) {
    } catch (InvocationTargetException ex) {
    }

    return jPanel[0];
}

A matriz local de 1 comprimento é usada para transferir o "resultado" de dentro do Runnable, que é invocado no EDT. Bem, parece "um pouco" hacky e, portanto, minhas perguntas:

  1. Isso faz sentido? Alguém mais está fazendo algo assim?
  2. A matriz de 1 comprimento é uma boa maneira de transferir o resultado?
  3. Há uma maneira mais fácil de fazer isso?
Foi útil?

Solução

  • Engolir exceções sem sequer registrá -las: mau!! - Você se odiará quando encontrar algo assim depois de 2 horas de caça aos insetos
  • Não, a matriz não é uma boa maneira; Por um lado, não oferece um método fácil para o código de chamada esperar o tópico da EDT executar o Runnable Antes de buscar o resultado
  • Há uma classe projetada explicitamente para esse tipo de coisa: SwingWorker

Outras dicas

Embora esse método possa fazer sentido em algumas situações, será inútil na maioria das vezes.

O motivo é que a criação da maioria (se não todas) de seus componentes sempre ocorrerá no EDT, como resultado de uma ação do usuário (item de menu ou clique no botão) que são sempre executados a partir do EDT.

Nos casos em que você tem um grande trabalho a ser executado antes de criar seu painel e não deseja bloquear o EDT, então, como sugerido por outra pessoa, use swingworker ou uma estrutura de swing que ofereça suporte para tarefas longas (geralmente com base em Swingworker internamente de qualquer maneira, mas não necessariamente).

Em relação à sua pergunta 2, infelizmente você não tem muitas maneiras de fazer isso:

  • Use uma matriz de 1 itens como você fez, essa é a solução mais fácil, mas também mais feia
  • Crie uma classe de item do item (veja abaixo) que faz quase o mesmo, requer um pouco mais de trabalho e é mais limpo, na minha opinião
  • Por último, use java.util.Concurrent Instalações (futuro e chamável); Isso seria o mais limpo, eu acho, mas também requer o maior esforço

Aqui está, simplificado, a classe do item do item:

public class ItemHolder<T> {
    public void set(T item) {...}
    public T get() {...}
    private T item;
}
  1. a) Faz sentido. b) Não que eu conheça.
  2. Tão bom quanto qualquer.
  3. Criar o jpanel fora do invokeAndWait ligar

// esta linha adicionada para apaziguar a marcação

public JPanel threadSafeAddPanel() {
    final JPanel jPanel = new JPanel();
    try {
        EventQueue.invokeAndWait(new Runnable() {
            public void run() {
                add(jPanel);
            }
        });
    } catch (InterruptedException ex) {
    } catch (InvocationTargetException ex) {
    }
    return jPanel;
}

Você pode verificar facilmente se o thread atual é o EDT e, em seguida, executar corretamente e mais simplesmente nesse contexto. Quanto ao uso da matriz final para obter o valor de retorno, essa é a maneira mais fácil de usar uma classe interna anônima como essa.

public JPanel threadSafeAddPanel() throws InterruptedException, 
        InvocationTargetException {
    if (EventQueue.isDispatchThread()) {
        JPanel panel = new JPanel();
        add(panel);

        return panel; 
    } else {
        final JPanel[] jPanel = new JPanel[1];
        EventQueue.invokeAndWait(new Runnable() {
            public void run() {
                jPanel[0] = new JPanel();
                add(jPanel[0]);
            }
        });

        return jPanel[0];
    }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top