Swing组件更新频繁时闪烁

发布于 2024-07-15 06:24:44 字数 1326 浏览 10 评论 0原文

我在某处有几千行代码,我注意到当我更新太多时,我的 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 版本,但我想看看你们是否有更简单的解决方案。

I've got a couple thousand lines of code somewhere and I've noticed that my JTextPane flickers when I update it too much.. I wrote a simplified version here:

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();
        }
        });

    }
}

This is for a terminal (cmd) style GUI component--

I think I've made all the optimizations I could here, including having \n as a final variable so it won't be constructed hundreds of times. Still, the flickering is noticeable and unacceptable. After a few minutes, the component freezes completely. I must update the component very quickly, and the pane must be scrolled to the bottom when updated.

I've been thinking about making my own version of JTextPane from scratch, but I'd like to see if you guys have an easier solution.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

一花一树开 2024-07-22 06:24:44

您的部分错误是您正在从事件线程外部访问 Swing 组件! 是的, setText( ) 是线程安全的,但 Swing 方法不是线程安全的,除非明确声明它们。 因此, setCaretPosition()< /a> 不是线程安全的,必须从事件线程访问。 这几乎肯定是您的应用程序最终冻结的原因。

注意:JTextPaneJEditorPane 继承其 setText() 方法,并从 JTextComponent 继承其 setCaretPosition 方法code>,它解释了上一段中的链接不会转到 JTextPane JavaDoc 页面。

为了实现线程安全,您确实至少需要从事件线程内调用 setCaretPosition() ,您可以使用如下代码来执行此操作:

SwingUtilities.invokeAndWait(new Runnable() {
  public void run() {
    a.setText(b.toString());
    a.setCaretPosition(b.length());
  }
}

并且因为您必须调用 setCaretPosition() 在事件线程内,您也可以从同一位置调用 setText()

您可能不需要手动设置插入符位置。 查看 JavaDoc 中的“插入符更改”部分,了解 JTextComponent

最后,您可能想查看由两篇文章组成的系列:

Part of your error is that you are accessing a Swing component from outside the event thread! Yes, setText() is thread-safe, but Swing methods are not Thread-safe unless they are explicitly declared as such. Thus, setCaretPosition() is not Thread-safe and must be accessed from the event thread. This is almost certainly why your application eventually freezes.

NOTE: JTextPane inherits its setText() method from JEditorPane and its setCaretPosition method from JTextComponent, which explains the links in the previous paragraph not going to the JTextPane JavaDoc page.

To be Thread-safe, you really need to at least call setCaretPosition() from within the event thread, which you can do with code like this:

SwingUtilities.invokeAndWait(new Runnable() {
  public void run() {
    a.setText(b.toString());
    a.setCaretPosition(b.length());
  }
}

And since you have to call setCaretPosition() from within the event thread, you might as well also call setText() from the same place.

It's possible that you may not need to manually set the caret position. Check out the section "Caret Changes" in the JavaDoc for JTextComponent.

Finally, you may want to check out a series of two articles:

深白境迁sunset 2024-07-22 06:24:44

不确定这是否有效,但您可以尝试使用文本窗格的 Document 实例的 insertString() 方法。 我会尝试在文档末尾添加一个空格,并将插入符号保留在该空格之后; 但是当您插入字符串时,请在空格之前插入它。 这样插入符号位置将自动保留在文档的末尾。

我认为文本窗格可能会重绘两次,一次当您调用 setText() 时,一次当您调用 setCaretPosition() 时,这可能会导致闪烁。 但不确定(自从我使用 Swing 以来已经有一段时间了)。

Not sure if this will work, but you could try using the insertString() method of the text pane's Document instance. I would try having a single space at the end of the document and keeping the caret positioned after that space; but when you insert a string, insert it before the space. That way the caret position will remain at the end of the document automatically.

I'm thinking that the text pane might be getting redrawn twice, once when you call setText() and once when you call setCaretPosition(), and that might be contributing to the flickering. Not sure, though (it's been a while since I worked with Swing).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文