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();
}
});
}
}
这是针对终端 (cmd) 风格的 GUI 组件——
我想我已经在这里做了所有我能做的优化,包括 \n
作为最终变量,因此它不会被构造数百次。尽管如此,闪烁仍然是明显且不可接受的。几分钟后,该组件完全冻结。我必须非常快速地更新组件,并且更新时窗格必须滚动到底部。
我一直在考虑从头开始制作我自己的 JTextPane 版本,但我想看看你们是否有更简单的解决方案。
解决方案
您的部分错误是您正在从事件线程外部访问 Swing 组件!是的, 设置文本() 是线程安全的,但 Swing 方法不是线程安全的,除非明确声明它们。因此, 设置插入符位置() 不是线程安全的,必须从事件线程访问。这几乎肯定是您的应用程序最终冻结的原因。
笔记: 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合作)。