【问题标题】:JFrame super.paint(g) Causing FlickeringJFrame super.paint(g) 导致闪烁
【发布时间】:2017-07-02 18:14:45
【问题描述】:

我正在使用我自己的双缓冲进行渲染循环,我没有 JPanel 而是 JFrame(据我了解 JPanel 自动双缓冲)。我的问题是,当我调用 super.paint(g) 时,它会导致我的屏幕闪烁。当我对此发表评论时,它就会消失。

ATM 我创建了一个 BufferedImage 并获取它的 Graphics2D,然后在每个渲染循环中我用纯色刷新图像并调用 repaint()。在paint 方法中,我调用super 并使用paint() 方法的图形来绘制图像。即使在渲染 1 fps 时,这也会导致我的屏幕闪烁。

public void run()
{
    long startTime;
    long runTime;
    double residualTime = 0;

    while(isRunning)
    {
        startTime = System.nanoTime();
        **update.update();**
        render.render();

        runTime = System.nanoTime() - startTime;
        if(runTime < 1e9/fpsTarget)
        {
            Double sleepTime = (1e9/fpsTarget - runTime) / 1e6;

            residualTime += sleepTime % 1;
            sleepTime -= sleepTime % 1;

            if(residualTime > 1)
            {
                sleepTime++;
                residualTime--;
            }

            try
            {
                sleep(sleepTime.longValue());
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
            finally
            {
                /*
                System.out.println("\n     Run time: " + runTime / 1e6);
                System.out.println("   Sleep time: " + sleepTime);
                System.out.println("Residual Time: " + residualTime);
                */
            }
        }
        //fps(startTime);
    }
}

public class Render
{
    private Screen screen;

    Graphics2D graphics;
    BufferedImage image;

    public Render()
    {
        screen = new Screen();

        image = new BufferedImage(screen.getWidth(), screen.getHeight(), BufferedImage.TYPE_INT_RGB);
        graphics = image.createGraphics();
    }

    public void render()
    {
        flush();
        screen.setImage(image);
        screen.repaint();
    }

    private void flush()
    {
        graphics.setPaint(Color.BLUE);
        graphics.fillRect(0, 0, image.getWidth(), image.getHeight());
    }
}

public class Screen extends JFrame
{   
    private BufferedImage image;

    public Screen()
    {
        super(TITLE);
        setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
        setLocation(300, 150);
        setResizable(false);
        setVisible(true);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    @Override
    public void paint(Graphics g)
    {
        super.paint(g);
        g.drawImage(image, 0, 0, null);
    }

    public void setImage(BufferedImage image)
    {
        this.image = image;
    }

我之前尝试过添加 JPanel 并通过 paintComponent() 进行渲染,但遇到了同样的问题。任何想法建议都会有所帮助!

编辑 - 已解决

在 Render.render() 方法中设置我的图像时,可能会自动调用 repaint()。然后我在导致双重刷新后立即调用 repaint() 。删除我自己的 repaint() 调用解决了这个问题。

奇怪的是,在我调用 repaint() 之前,删除 super.paint() 也解决了这个问题。任何想法为什么?

【问题讨论】:

  • “我之前尝试过添加 JPanel 并通过paintComponent() 进行渲染,但遇到了同样的问题。任何想法建议都会有所帮助!” 尝试显示minimal reproducible example使用面板。
  • 不要使用paint,而是使用paintComponent 和子类JPanel 而不是JFrame(将面板插入框架)。很多例子在网上很容易找到......

标签: java swing jframe paint


【解决方案1】:

看起来您正在处理循环中调用睡眠。

 sleep(sleepTime.longValue());

如果它在单独的线程中被调用,则您正在从另一个线程(不是 Swing 的 AWT 线程)调用重绘。不建议从 AWT 线程外部调用 swing 组件。如果是 AWT 线程,它会在睡眠期间阻塞绘画。

虽然这可能不是导致闪烁的主要问题,但您应该修改代码以使用 Swing 计时器,以便在 AWT 线程上调用重绘逻辑并且不会阻塞。

【讨论】:

  • 所以基本上因为我使用的计时器不在我正在绘制的同一线程上运行,这可能会导致在计时器线程处于睡眠状态时在两个线程之间切换的问题?
【解决方案2】:

好吧,我不确定这是否是答案,但我试图向 Andrew 展示当我使用 Panel 渲染而不是 Frame 渲染但没有任何成功时我的代码是什么样的。我删除了我更改的所有内容,并尝试追求 Ashwinee 的线程理论。我在更改任何内容之前运行了一次代码,它运行良好。

比较我当前的代码和提交时的代码,我认为不同之处在于我删除了 Render.render() 方法中的 repaint() 语句。我唯一的猜测是 JFrame 识别出它之前绘制的东西发生了变化,所以它会自动重新绘制,所以我实际上每个循环都重新绘制了两次。

如果我在闪烁的返回中添加 repaint() 回调。

【讨论】:

    猜你喜欢
    • 2011-10-23
    • 2015-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-18
    • 1970-01-01
    相关资源
    最近更新 更多