2016-02-19 21 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非常接近)和直接控制繪製過程(現在真的是使用它的唯一原因)

相關問題