首页 > 解决方案 > 从非 EDT 触发 Java 重绘很慢

问题描述

我在repaint()不是 EDT 的线程中调用 JFrame 中的 Swing 组件(与repaintSwing 侦听器/适配器/动作映射中的典型调用相反)。从技术上讲,它是在 javax.sound.midi 的回调中调用的Reciever,但我认为这与一般情况没有什么不同。

组件更新缓慢。这个玩具代码显示了一个JLabel其文本应该立即更新到当前时间,然后是我们repaint,然后我们打印到当前时间的标准输出。在我的系统上,标签和标准输出时间看起来是正确的——标签repaint比标准输出早了几毫秒。但是,标签在视觉上不会更新一段时间,看起来是随机的,甚至长达半秒。当它确实更新时,它会显示一个合理的时间,但它是一个旧时间。

从鼠标侦听器(或按键侦听器等)的回调中执行 [更新显示时间、重绘、打印时间] 测试工作正常,延迟低。当然调用repaint只是安排它发生,但行为不应该是相同的,特别是如果包裹在SwingUtilities.invokeLater? 我是否缺少事件优先级或简单的线程事实?如果不是,什么组件需要很长时间才能重新绘制JLabel?总之,我如何repaint从非 Swing、非 EDT 线程调用?

非工作解决方案:

import javax.sound.midi.*;import javax.swing.*;import java.lang.reflect.InvocationTargetException;
public class Main {
    public static String time() {return (System.currentTimeMillis()/1000)+"."+(System.currentTimeMillis()%1000);}
    public static void main(String[] args) {
        JFrame w = new JFrame();
        JLabel label = new JLabel("hey");
        Transmitter tr = MidiUtil.firstTransmitterExcept("gervill","real time sequencer");//makes a MidiDevice and returns its transmitter
        tr.setReceiver(new Receiver(){public void send(final MidiMessage message,final long t) {
                try {
                    label.setText(time());
                    SwingUtilities.invokeAndWait(()->{label.paintImmediately(0,0,label.getWidth(),label.getHeight());});
                }catch(final InterruptedException ie) {}catch(final InvocationTargetException ite) {}
                System.out.println(time());
        }public void close() {}});
        w.add(label);
        w.pack();
        w.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        w.setVisible(true);
    }
}

提前感谢您的阅读。

标签: javamultithreadingswingrepaintevent-dispatch-thread

解决方案


推荐阅读