在Java中围绕repaint()工作

在Java中围绕repaint()工作,java,swing,bufferedimage,paintcomponent,Java,Swing,Bufferedimage,Paintcomponent,我计划写一个简单的太空射手。我已经读到repaint()方法只是一个请求,并不是每次调用它时都执行。我相信我已经注意到了这一点,因为我的宇宙飞船在移动时往往会稍微滞后。目前,我只是用JPanel的paintComponent()方法绘制我的飞船,并定期调用repaint()(我的面板也可以运行)。鉴于repaint()可能会把我搞砸,我正试图找到一种方法在它周围工作,但我已经没有主意了。到目前为止,我掌握的代码是: private void renderGraphics() { if (

我计划写一个简单的太空射手。我已经读到repaint()方法只是一个请求,并不是每次调用它时都执行。我相信我已经注意到了这一点,因为我的宇宙飞船在移动时往往会稍微滞后。目前,我只是用JPanel的paintComponent()方法绘制我的飞船,并定期调用repaint()(我的面板也可以运行)。鉴于repaint()可能会把我搞砸,我正试图找到一种方法在它周围工作,但我已经没有主意了。到目前为止,我掌握的代码是:

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(),但是我不知道如何做。我会通知你关于这件事的任何意见。

任何绘图都将在Swing线程中执行,因此无论你尝试做什么,都不会有帮助


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

任何图形仍将在swing线程中执行,因此无论您尝试如何处理,它都不会有帮助


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

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

在图像中绘制静态内容一次,然后在paintComponent()中使用该图像。在静态图像之后调用动态零件绘制


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

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

在图像中绘制静态内容一次,然后在paintComponent()中使用该图像。在静态图像之后调用动态零件绘制


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

有多种方法可以实现此目的

最好的方法可能是使用
BufferStrategy
并利用它,其中我包含了一个应该适合您的代码片段

您可以更进一步,完全放弃Swing,只需使用Frame/buffer策略。在我的问题中有一个完全有效的示例(代码片段就是从中提取和改编的):

无论如何,这里有一个实现
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());
}

有多种方法可以做到这一点

最好的方法可能是使用
BufferStrategy
并利用它,其中我包含了一个应该适合您的代码片段

您可以更进一步,完全放弃Swing,只需使用Frame/buffer策略。在我的问题中有一个完全有效的示例(代码片段就是从中提取和改编的):

无论如何,这里有一个实现
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/正如你提到的
Runnable{…}.start()Swing是简单的线程,所有到GUI的输出都必须在EDT上完成;您必须查看,结果是来自
后台任务
的所有输出必须包装到
invokeLater()
,如果performancie有问题,则包装到
invokeAndWait()

3/如果要切换(在
JComponents
)/add/delete/change
Layout
则必须调用
revalidate()+repaint()
作为具体代码块中的最后一行

编辑:


肮脏的黑客行为将是

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

1/如果您想显示,那么最好、最简单的方法是

2/正如你提到的
Runnable{…}.start()Swing是简单的线程,所有到GUI的输出都必须在EDT上完成;您必须查看,结果是来自
后台任务
的所有输出必须包装到
invokeLater()
,如果performancie有问题,则包装到
invokeAndWait()

3/如果要切换(在
JComponents
)/add/delete/change
Layout
则必须调用
revalidate()+repaint()
作为具体代码块中的最后一行

编辑:


dirty hack将在没有任何参数的情况下调用repait,这意味着整个面板都被重新绘制

如果需要重新绘制部分屏幕(宇宙飞船已移动到其他位置),则应使舒尔仅重新绘制屏幕的这些部分。不应触摸保持不变的区域

“重新绘制”获取应重新绘制的矩形的坐标。移动船舶时,您应该知道船舶的旧坐标以及船舶应移动到的坐标

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


另请参见:-尤其是步骤3

在没有任何参数的情况下调用repait意味着整个面板都被重新绘制

如果需要重新绘制部分屏幕(宇宙飞船已移动到其他位置),则应使舒尔仅重新绘制屏幕的这些部分。不应触摸保持不变的区域

“重新绘制”获取应重新绘制的矩形的坐标。移动船舶时,您应该知道船舶的旧坐标以及船舶应移动到的坐标

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

另请参见:-尤其是步骤3