在Java中使用arround repaint()

时间:2011-08-01 09:56:27

标签: java swing bufferedimage paintcomponent

我打算写一个简单的太空射击游戏。我已经读过repaint()方法只是一个请求,并且每次调用它都不会执行。我相信我注意到了这一点的影响,因为当我移动时,我的宇宙飞船往往会略微滞后。目前我只是在一个JPanel的paintComponent()方法中绘制我的船,并且定期调用repaint()(我的面板也是Runnable)。看起来像重绘()可能会让我感到困惑,我正试图找到一种方法来解决它,但是我已经没有了想法。我到目前为止的代码:

private void renderGraphics() {
    if (MyImage == null) {
        MyImage = new BufferedImage(getPreferredSize().width,
                getPreferredSize().height, BufferedImage.TYPE_INT_RGB);
    }
    MyGraphics = MyImage.getGraphics();
    MyGraphics.setColor(Color.BLACK);
    MyGraphics.fillRect(0, 0, getPreferredSize().width, getPreferredSize().height);
    MyGraphics.drawImage(ship.getImage(), ship.getCurrentX(), ship.getCurrentY(), null);       
}

我的想法是创建自己的图形,然后让JPanel绘制它,并在我的run()方法中继续调用它而不是repaint(),但我不知道如何做到这一点。我会对此事的任何意见表示赞同。

6 个答案:

答案 0 :(得分:4)

有多种方法可以解决这个问题。

最好的可能是使用BufferStrategy并绘制到那个,其中包含了一个适合您的代码段。

你可以更进一步,完全放弃Swing,只需使用Frame / BufferStrategy。在我的问题中有一个完整的工作示例(从中获取和修改了代码片段):

AWT custom rendering - capture smooth resizes and eliminate resize flicker

无论如何,这是一个实现BufferStrategy,您应该可以直接进入:

// you should be extending JFrame
public void addNotify() {
    super.addNotify();
    createBufferStrategy(2);
}

private synchronized void render() {
    BufferStrategy strategy = getBufferStrategy();
    if (strategy==null) return;
    sizeChanged = false;
    // Render single frame
    do {
        // The following loop ensures that the contents of the drawing buffer
        // are consistent in case the underlying surface was recreated
        do {
            MyGraphics draw = strategy.getDrawGraphics();
            draw.setColor(Color.BLACK);
            draw.fillRect(0, 0, getPreferredSize().width, getPreferredSize().height);
            draw.drawImage(ship.getImage(), ship.getCurrentX(), ship.getCurrentY(), null);
            draw.dispose();

            // Repeat the rendering if the drawing buffer contents 
            // were restored
        } while (strategy.contentsRestored());

        // Display the buffer
        strategy.show();

        // Repeat the rendering if the drawing buffer was lost
    } while (strategy.contentsLost());
}

答案 1 :(得分:2)

任何绘图仍将在Swing Thread中执行,因此无论您尝试解决什么,它都无济于事。

确保你没有在swing线程中进行任何冗长的计算,这可能会在需要执行时立即停止重绘

答案 2 :(得分:2)

将所有逻辑分成两部分。静态和动态。 (例如海运和移动船舶。船舶在海洋静态图像上改变形状/位置)

在图像中绘制一次静态内容并使用paintComponent()中的图像。在静态图像之后调用动态部件绘制。

使用setClip()限制重绘区域。

答案 3 :(得分:2)

仅针对您在此处发布的代码:

1 /如果您想显示Image/ImageIcon,那么最好也是最简单的方式是Use Labels

2 /正如您所提到的Runnable{...}.start(); Swing是简单的线程,所有输出到GUI都必须在EDT上完成;您必须查看Concurrency in Swing,结果是BackGround Task(s)的所有输出都必须包含在invokeLater()中,如果有问题,则会出现问题,然后进入invokeAndWait()

3 /如果你切换(在JComponents之间)/添加/删除/更改Layout,那么你必须在具体代码块中调用revalidate() + repaint()作为最后一行

编辑:

脏黑客将是paintImmediately()

答案 4 :(得分:2)

调用不带任何参数的重绘意味着重新绘制整个面板。

如果你需要重新绘制部分屏幕(宇宙飞船已移动到不同的位置),你应该确保只重新绘制屏幕的那些部分。不应触及保持不变的区域。

重绘会获取应重新绘制的矩形的坐标。移动船舶时,您应该知道船舶的旧坐标和船舶应移动的坐标。

repaint( oldShipCoordinateX, oldShipCoordinateY, shipWidth, shipLength );
repaint( newShipCoordinateX, newShipCoordinateY, shipWidth, shipLength );

这通常比不带参数调用repaint()快得多。但是,您需要额外的努力来记住船的最后位置,并且必须能够计算船的新位置。

另请参阅:http://download.oracle.com/javase/tutorial/uiswing/painting/index.html - 尤其是第3步

答案 5 :(得分:1)

  

我已经读过repaint()方法只是一个请求,并且每次调用它都不会执行

它将多个repaint()请求合并为一个以提高效率。

  

我相信我注意到了这一点的影响,因为当我移动飞船时,我的飞船往往会略微落后。

然后发布演示此问题的SSCCE。我怀疑问题是你的代码。

关于您接受的解决方案,请查看Charles上次发布的帖子:Swing/JFrame vs AWT/Frame for rendering outside the EDT比较Swing与AWT解决方案。