使用createBufferStrategy()的正确方法是什么?

即使在使用Java Swing超过一年之后,它对我来说仍然是神奇的。 我如何正确使用BufferStrategy,特别是createBufferSrategy()方法?

我想有一个JFrame和一个Canvas,它被添加到它然后绘制。 我还希望能够调整Canvas的大小( setSize() )。 每次我调整Canvas的大小时,似乎我的BufferStrategy被删除了,或者更确切地说,变得无用,因为在BufferStrategy上使用show()实际上并没有做任何事情。 此外, 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)循环调用MyFramerepaint()方法。 当threshold很小(即2)时,大约70%的情况下bufferStrat.show()都没有做任何事情 – JFrame在启动程序时仍然是灰色的。 剩下的30%它绘制了矩形它应该如何。 如果我做threshold = 200; ,这幅画在我执行程序的过程中接近100%的成功。 Javadoc说createBufferStrategy()可能需要一段时间,所以我认为这是问题所在。 但是,如何正确同步和使用它? 显然,我在这里做错了。 我无法想象它应该如何被使用。

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

你创建BufferStrategy的方式是“好的”,你可以看一下BufferStrategy的JavaDocs,它有一个简洁的例子。

你使用它的方式是值得怀疑的。 使用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()); } } } } 

您还应该记住,Swing一直使用DirectX或OpenGL管道,因为大约1.4(或1.5)。 使用BufferStrategy主要原因是更直接访问硬件(Swing非常接近)和直接控制绘制过程(现在真的是使用它的唯一原因)

    Interesting Posts