2016-02-19 93 views
0

即使使用Java Swing一年以上,它仍然对我来说很神奇。如何正确使用BufferStrategy,特别是方法createBufferSrategy()什么是使用createBufferStrategy()的正确方法?

我想有一个JFrame和一个画布,它被添加到它,然后画。我还希望能够调整(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(); 
} 
} 

我只需拨打MyFrame的从一段时间(true)循环repaint()方法的主要方法。 当threshold很小(即2)时,约有70%的案例中bufferStrat.show()没有做任何事情 - 在开始程序时JFrame保持灰色。其余的30%它绘制矩形应该如何。如果我做threshold = 200;,绘画成功接近100%执行程序的时间。 Javadoc说createBufferStrategy()可能需要一段时间,所以我认为这是问题。但是,如何正确同步和使用它?显然,我在这里做错了什么。我无法想象这就是它应该如何使用。

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

+0

创建缓冲区看起来“没问题” “,有关更多详细信息,请参见['BufferStrategy'](https://docs.oracle.com/javase/8/docs/api/java/awt/image/BufferStrategy.html),但它的用处不大。不要重写'repaint',事实是,这是一个由Swing被动渲染引擎使用的机制,除了你没有调用'super.repaint'这个事实,这可能会导致问题,使用' BufferStrategy'是为了控制绘画过程(或主动绘画)。 – MadProgrammer

+0

由于框架之间的竞争条件在屏幕上显示并显示在屏幕上,以及当您认为有东西被涂漆以及未能调用'super.repaint'时,您遇到的许多问题 – MadProgrammer

回答

2

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

你使用它的方式是值得怀疑的。使用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非常接近)和直接控制绘制过程(现在真的是使用它的唯一原因)

相关问题