0

即使在使用 Java Swing 一年多之后,它对我来说仍然很神奇。如何正确使用 BufferStrategy,尤其是方法createBufferSrategy()

我想要一个 JFrame 和一个 Canvas 被添加到它然后绘制。我还希望能够调整 ( setSize()) 画布的大小。每次我调整 Canvas 的大小时,似乎我BufferStrategy都会被丢弃,或者更确切地说,变得无用,因为使用show()onBufferStrategy实际上并没有做任何事情。此外,createBufferStrategy()有一个奇怪的非确定性行为,我不知道如何正确同步它。

这就是我的意思:

public class MyFrame extends JFrame {
MyCanvas canvas;
int i = 0;

public MyFrame() {
    setUndecorated(false);
    setVisible(true);
    setSize(1100, 800);
    setLocation(100, 100);
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    canvas = new MyCanvas();
    add(canvas);
    canvas.makeBufferStrat();
}

@Override
public void repaint() {
    super.repaint();
    canvas.repaint();
    //the bigger threshold's value, the more likely it is that the BufferStrategy works correctly
    int threshold = 2;
    if (i < threshold) {
        i++;
        canvas.makeBufferStrat();
    }
}
}

MyCanvas有一个方法makeBufferStrat()repaint()

public class MyCanvas extends Canvas {

BufferStrategy bufferStrat;
Graphics2D g;

public MyCanvas() {
    setSize(800, 600);
    setVisible(true);
}

public void makeBufferStrat() {
    createBufferStrategy(2);
    //I'm not even sure whether I need to dispose() those two.
    if (g != null) {
        g.dispose();
    }
    if (bufferStrat != null) {
        bufferStrat.dispose();
    }
    bufferStrat = getBufferStrategy();
    g = (Graphics2D) (bufferStrat.getDrawGraphics());
    g.setColor(Color.BLUE);
}

@Override
public void repaint() {
    g.fillRect(0, 0, 100, 100);
    bufferStrat.show();
}
}

我只是从 main 方法中的 while(true) 循环中调用MyFrame'srepaint()方法。当threshold很小(即 2)时,bufferStrat.show()大约 70% 的情况下什么都不做 - JFrame 在启动程序时保持灰色。剩下的 30% 它按照它应该的方式绘制矩形。如果我这样做threshold = 200;了,那么在我执行程序的时间里,绘画成功率接近 100%。Javadoc 说这createBufferStrategy()可能需要一段时间,所以我认为这就是问题所在。但是,如何正确同步和使用它?显然,我在这里做错了什么。我无法想象它应该如何使用。

有没有人有一个最小的工作示例?

4

1 回答 1

2

您创建的方式BufferStrategy是“好的”,您可以查看JavaDocs,BufferStrategy其中有一个简洁的小示例。

你使用它的方式是有问题的。使用 a 的主要原因BufferStrategy是因为您希望从 Swing 的绘画算法(被动的)中控制绘画过程(主动绘画)

但是,你似乎试图同时做这两件事,这就是它导致你的问题的原因。相反,您应该有一个“主”循环,它负责决定缓冲区应该绘制什么以及何时绘制,例如......

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferStrategy;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                TestPane testPane = new TestPane();
                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(testPane);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
                // The component needs to be attached to displayed window before
                // the buffer can be created
                testPane.startPainting();
            }
        });
    }

    public class TestPane extends Canvas {

        private AtomicBoolean painting = new AtomicBoolean(true);
        private PaintCycle paintCycle;

        private Rectangle clickBounds;

        public TestPane() {
            addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    if (clickBounds != null && clickBounds.contains(e.getPoint())) {
                        painting.set(false);
                    }
                }
            });
        }

        public void startPainting() {
            if (paintCycle == null) {
                createBufferStrategy(2);
                painting.set(true);
                paintCycle = new PaintCycle();
                Thread t = new Thread(paintCycle);
                t.setDaemon(true);
                t.start();
            }
        }

        public void stopPainting() {
            if (paintCycle != null) {
                painting.set(false);
            }
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        public class PaintCycle implements Runnable {

            private BufferStrategy strategy;
            private int xDelta = 2;
            private int yDelta = 2;

            @Override
            public void run() {
                System.out.println("Painting has started");

                int x = (int) (Math.random() * (getWidth() - 40));
                int y = (int) (Math.random() * (getHeight() - 40));

                do {
                    xDelta = (int) (Math.random() * 8) - 4;
                } while (xDelta == 0);
                do {
                    yDelta = (int) (Math.random() * 8) - 4;
                } while (yDelta == 0);

                clickBounds = new Rectangle(x, y, 40, 40);
                strategy = getBufferStrategy();
                while (painting.get()) {
                    // Update the state of the model...
                    update();
                    // Paint the state of the model...
                    paint();
                    try {
                        // What ever calculations you want to use to maintain the framerate...
                        Thread.sleep(40);
                    } catch (InterruptedException ex) {
                    }
                }
                System.out.println("Painting has stopped");
            }

            protected void update() {
                int x = clickBounds.x + xDelta;
                int y = clickBounds.y + yDelta;

                if (x + 40 > getWidth()) {
                    x = getWidth() - 40;
                    xDelta *= -1;
                } else if (x < 0) {
                    x = 0;
                    xDelta *= -1;
                }
                if (y + 40 > getHeight()) {
                    y = getHeight() - 40;
                    yDelta *= -1;
                } else if (y < 0) {
                    y = 0;
                    yDelta *= -1;
                }

                clickBounds.setLocation(x, y);
            }

            protected void paint() {
                // 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 {
                        // Get a new graphics context every time through the loop
                        // to make sure the strategy is validated
                        Graphics2D graphics = (Graphics2D) strategy.getDrawGraphics();

                        // Render to graphics
                        // ...
                        graphics.setColor(Color.BLUE);
                        graphics.fillRect(0, 0, getWidth(), getHeight());

                        graphics.setColor(Color.RED);
                        graphics.fill(clickBounds);

                        // Dispose the graphics
                        graphics.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.4(或者可能是 1.5)开始,Swing 就一直在使用 DirectX 或 OpenGL 管道。使用的主要原因BufferStrategy是更直接地访问硬件(无论如何,Swing 都非常接近)和直接控制绘画过程(现在这确实是使用它的唯一原因)

于 2016-02-19T22:46:56.000 回答