【问题标题】:Working my way arround repaint() in Java在 Java 中围绕 repaint() 工作
【发布时间】:2011-08-01 09:56:26
【问题描述】:

我打算写一个简单的太空射击游戏。我读过 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(),但是我不知道该怎么做。我会就此事提出任何意见。

【问题讨论】:

    标签: java swing bufferedimage paintcomponent


    【解决方案1】:

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

    最好的方法可能是使用BufferStrategy 并绘制它,其中我包含了一个适合您的代码 sn-p。

    您可以更进一步,完全放弃 Swing,只需使用 Frame/BufferStrategy。在我的问题中有一个完整的工作示例(代码 sn-p 是从中提取和改编的):

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

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

        【解决方案3】:

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

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

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

        【讨论】:

          【解决方案4】:

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

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

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

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

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

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

          【讨论】:

          • 这似乎是一个节省资源的好解决方案,但是,它引出了一个问题:如果屏幕上有多个移动对象,是否仍然值得用参数调用 repaint()? (例如宇宙飞船、射弹和来袭的流星)
          • 好点。考虑到这一点,我还认为查尔斯古德温的回答更适合您的需求。为他 +1。
          【解决方案5】:

          仅用于您在此处发布的代码:

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

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

          3/ 如果你是切换(JComponents 之间)/add/delete/change Layout 那么你必须调用revalidate() + repaint() 作为具体代码块中的最后一行

          编辑:

          肮脏的黑客将是paintImmediately()

          【讨论】:

            【解决方案6】:

            我看过repaint()方法只是一个请求,并不是每次调用都执行

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

            我相信我注意到了这一点,因为我的宇宙飞船在移动时往往会稍微滞后。

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

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

            【讨论】:

              猜你喜欢
              • 2015-05-13
              • 1970-01-01
              • 1970-01-01
              • 2014-02-17
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多