Balançar cintilação componente quando actualiza um monte
-
21-08-2019 - |
Pergunta
Eu tenho um par mil linhas de algum código e tenho notado que minha JTextPane treme quando eu atualizá-lo muito .. Eu escrevi uma versão simplificada aqui:
import java.awt.*;
import javax.swing.*;
public class Test
{
static JFrame f;
static JTextPane a;
static final String NL = "\n";
public static void main(String... args)
{
EventQueue.invokeLater(new Runnable(){
public void run()
{
f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
f.setSize(400, 300);
f.setLocationRelativeTo(null);
a = new JTextPane();
f.add(new JScrollPane(a));
new Thread(new Runnable(){
public void run()
{
int i = 0;
StringBuffer b = new StringBuffer();
while(true)
{
b.append(++i+NL);
a.setText(b.toString());
a.setCaretPosition(b.length());
try{Thread.sleep(10);}catch(Exception e){}
}
}
}).start();
}
});
}
}
Este é um componente terminal (cmd) estilo GUI -
Eu acho que eu fiz todas as otimizações que eu poderia aqui, incluindo ter \n
como uma variável final, por isso não vai ser construído centenas de vezes. Ainda assim, a oscilação é perceptível e inaceitável. Depois de alguns minutos, o componente congela completamente. I deve atualizar o componente muito rapidamente, eo painel deve ser deslocado para a parte inferior e depois actualizada.
Eu estive pensando em fazer minha própria versão de JTextPane a partir do zero, mas eu gostaria de ver se vocês têm uma solução mais fácil.
Solução
Parte do seu erro é que você está acessando um componente Swing de fora do segmento de eventos! Sim, setText ( ) é thread-safe, mas os métodos do balanço não são thread-safe, a menos que sejam explicitamente declarados como tais. Assim, setCaretPosition () não é thread-safe e deve ser acessado a partir do segmento de eventos. Isto é quase certamente por isso que a sua aplicação, eventualmente congela.
NOTA: JTextPane
herda seu método setText()
de JEditorPane
e seu método setCaretPosition
de JTextComponent
, o que explica as ligações no parágrafo anterior não ir ao JTextPane
página JavaDoc.
Para ser thread-safe, você realmente precisa de pelo menos setCaretPosition()
chamada de dentro do segmento de eventos, o que você pode fazer com um código como este:
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
a.setText(b.toString());
a.setCaretPosition(b.length());
}
}
E uma vez que você tem que setCaretPosition()
chamada de dentro do segmento de eventos, você também pode também chamar setText()
do mesmo lugar.
É possível que você não pode precisar configurar manualmente a posição do cursor. Confira a seção "Caret Changes" no JavaDoc para JTextComponent .
Finalmente, você pode querer verificar para fora uma série de dois artigos:
Outras dicas
Não tenho certeza se isso vai funcionar, mas você pode tentar usar o método insertString()
de exemplo Document
do painel de texto. Gostaria de tentar ter um único espaço no final do documento e manter o cursor posicionado depois que o espaço; mas quando você inserir uma string, inseri-lo antes do espaço. Dessa forma, a posição do cursor permanecerá no final do documento automaticamente.
Estou pensando que o painel de texto pode estar recebendo redesenhado duas vezes, uma quando você chamar setText()
e uma vez quando você chamar setCaretPosition()
, e que pode estar contribuindo para a oscilação. Não tenho certeza, embora (já faz um tempo desde que eu trabalhei com Swing).