Olá, estou com o seguinte problema:
Preciso que meu JTextArea atualize em tempo real, com posso fazer isso?
Ja dei uma olhada em SwingWorker:
public class Imprime extends SwingWorker{
private String texto;
public Imprime(String texto){
this.texto = texto;
}
@Override
protected JTextArea doInBackground() throws Exception {
return null;
}
@Override
protected void done(){
jTextArea1.append(texto);
}
}
Chamo isso no método:
public void imprimeTela(String texto) {
texto = texto + "\r\n";
new Imprime(texto).execute();
}
E nada, o programa primeiro executa tudo para depois escrever no JTextArea.
O que devo fazer?
Não seria algo como invokeLater?
O invokeLater não vai só colocar a atualização na pilha de execução da interface?
Preciso de alguma coisa que tenha mais prioridade sobre a esta execução, ja tentei alterar a prioridade da thread e não consegui tambem. Teria alguma ideia ?
Uai, pensei que iria criar um exemplo em que invokeLater fizesse diferença, mas não fez
- tente rodar o programa abaixo.
package guj;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import javax.swing.JButton;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class ExemploInvokeLater extends JFrame {
private JPanel panel;
private JButton btnUsandoInvokelater;
private JButton btnNaoUsandoInvokelater;
private JScrollPane scrollPane;
private JTextArea textArea;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
ExemploInvokeLater frame = new ExemploInvokeLater();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public ExemploInvokeLater() {
setBounds(100, 100, 450, 300);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
getContentPane().add(getPanel(), BorderLayout.SOUTH);
getContentPane().add(getScrollPane(), BorderLayout.CENTER);
}
private JPanel getPanel() {
if (panel == null) {
panel = new JPanel();
panel.add(getBtnUsandoInvokelater());
panel.add(getBtnNoUsandoInvokelater());
}
return panel;
}
private void addLineToJTextArea(String msg) {
getTextArea().append(msg);
}
private JButton getBtnUsandoInvokelater() {
if (btnUsandoInvokelater == null) {
btnUsandoInvokelater = new JButton("Usando invokeLater");
btnUsandoInvokelater.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
btnUsandoInvokelater.setEnabled(false);
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 100; ++i) {
final int x = i;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
getTextArea().append(String.format("Linha %d\n", x));
}
});
try {
Thread.sleep(200);
} catch (InterruptedException ex) {
}
}
btnUsandoInvokelater.setEnabled(true);
}
}).start();
}
});
}
return btnUsandoInvokelater;
}
private JButton getBtnNoUsandoInvokelater() {
if (btnNaoUsandoInvokelater == null) {
btnNaoUsandoInvokelater = new JButton("N\u00E3o usando invokeLater");
btnNaoUsandoInvokelater.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
btnNaoUsandoInvokelater.setEnabled(false);
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 100; ++i) {
final int x = i;
getTextArea().append(String.format("Linha %d\n", x));
try {
Thread.sleep(200);
} catch (InterruptedException ex) {
}
}
btnNaoUsandoInvokelater.setEnabled(true);
}
}).start();
}
});
}
return btnNaoUsandoInvokelater;
}
private JScrollPane getScrollPane() {
if (scrollPane == null) {
scrollPane = new JScrollPane();
scrollPane.setViewportView(getTextArea());
}
return scrollPane;
}
private JTextArea getTextArea() {
if (textArea == null) {
textArea = new JTextArea();
}
return textArea;
}
}
Bah… tentei:
public void colocaTela(String texto){
final String element = texto;
new Thread(new Runnable() {
public void run() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
jtextArea1.append(element + "\r\n");
}
});
}
}).start();
}
Mas não deu certo, primeiro foi executado todo o código e por ultimo foi executado o método de escrever, sendo que o método de escrever esta no meio do código.
Tem alguma ideia?
Não, eu queria que você escrevesse isto aqui:
public void colocaTela(String texto){
final String element = texto;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
jtextArea1.append(element + "\r\n");
}
});
}
Você leu o meu código? Você tentou rodá-lo?
Sim, eu li e rodei ele aqui.
Mas não entendi como vou coloca-lo no meu código, estou com dificuldade de entender esta parte, pois, devo utilizar o invokeLater dentro do método que eu chamo? Ou na hora de chamar o método?
Alem disso, como o programa vai utilizar muita memória eu devo ter um espaço reservado pra isso, não ?
Basicamente é assim.
Digamos que você tem uma operação lenta, que você precisa mostrar a progressão (no seu caso, preenchendo uma JTextArea).
Essa operação lenta deve ser feita em uma thread separada, que normalmente é implementada como uma thread simples (como é o caso do meu exemplo) ou então com um SwingWorker (que é seu caso).
Como a thread separada não é a thread do Swing, então para acessar a tela, você precisa usar sempre um invokeLater para que as operações do Swing sejam executadas na thread do Swing.
Quanto a problemas de memória, não creio que haja só por causa disso. Entretanto, concordo com você que se você estiver criando um JTextArea que representa o andamento de um processo longo, com muitas linhas, você deve ter uma lógica para ir “podando” as linhas mais antigas do JTextArea para evitar que você estoure a memória por causa dele. Se você além disso precisar inspecionar, durante a execução, todas as linhas do JTextArea, como se fosse um visualizador de log, você não pode usar um JTextArea e sim algum controle customizado que alguém deve ter escrito e que representa uma visão virtual sobre um arquivo de log (por exemplo).
Resolvi tirar do programa o JTextArea, não era uma necessidade, fiz mais só para auxilio mesmo. Obrigado