Компонент Swing часто мерцает при обновлении
-
21-08-2019 - |
Вопрос
У меня где-то есть пара тысяч строк кода, и я заметил, что мой JTextPane мерцает, когда я слишком часто его обновляю..Я написал здесь упрощенную версию:
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();
}
});
}
}
Это для компонента GUI в стиле терминала (cmd)--
Я думаю, что я сделал здесь всю возможную оптимизацию, включая наличие \n
в качестве конечной переменной, чтобы она не создавалась сотни раз.Тем не менее, мерцание заметно и неприемлемо.Через несколько минут компонент полностью застывает.Я должен обновить компонент очень быстро, и при обновлении панель должна быть прокручена до самого низа.
Я подумывал о создании своей собственной версии JTextPane с нуля, но я хотел бы посмотреть, есть ли у вас, ребята, более простое решение.
Решение
Часть вашей ошибки заключается в том, что вы обращаетесь к компоненту Swing извне потока событий!ДА, setText() является потокобезопасным, но методы Swing не являются потокобезопасными, если они явно не объявлены как таковые.Таким образом, setCaretPosition() не является потокобезопасным и должен быть доступен из потока событий.Почти наверняка именно поэтому ваше приложение в конечном итоге зависает.
ПРИМЕЧАНИЕ: JTextPane
наследует свой setText()
способ из JEditorPane
и его setCaretPosition
способ из JTextComponent
, что объясняет ссылки в предыдущем абзаце , не ведущие на JTextPane
Страница JavaDoc.
Чтобы быть потокобезопасным, вам действительно нужно, по крайней мере, вызвать setCaretPosition()
из потока событий, что вы можете сделать с помощью такого кода, как этот:
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
a.setText(b.toString());
a.setCaretPosition(b.length());
}
}
И поскольку ты должен позвонить setCaretPosition()
из потока событий вы также можете вызвать setText()
из того же самого места.
Вполне возможно, что вам не понадобится вручную устанавливать положение курсора.Ознакомьтесь с разделом "Изменения курсора" в JavaDoc для JTextКомпонент.
Наконец, возможно, вы захотите ознакомиться с серией из двух статей:
Другие советы
Не уверен, сработает ли это, но вы могли бы попробовать использовать insertString()
метод отображения текстовой панели Document
пример.Я бы попробовал поставить один пробел в конце документа и сохранить курсор расположенным после этого пробела;но когда вы вставляете строку, вставляйте ее перед пробелом.Таким образом, положение курсора автоматически сохранится в конце документа.
Я думаю, что текстовая панель может перерисовываться дважды, один раз при вызове setText()
и однажды, когда ты позвонишь setCaretPosition()
, и это может способствовать мерцанию.Хотя и не уверен (прошло много времени с тех пор, как я работал со Swing).