为什么用双缓冲
在制造小球运动效果的时候,首先是用小球色画出小球t时刻所在的位置,然后t+tsleep时刻用背景色擦除t时刻小球,用小球色画出该时刻状态的小球,这样就导致了一帧一帧的切换。所以小球运动一次至少有三次绘图调用repaint。这样绘制的图像刷新太快,会出现屏闪现象。
什么是双缓冲?
用序列的操作模拟瞬间或者同时发生的事情。通俗地讲就是一个后台缓冲来接受数据,当填充完整后交换给前台缓冲,这样就保证了前台缓冲里的数据都是完整的。
要点:
1,一个双缓冲类封装了一个缓冲:一段可改变的状态。这个缓冲被增量的修改,但我们想要外部的代码将其视为单一的元素修改。 为了实现这点,双缓冲类需保存两个缓冲的实例:下一缓存和当前缓存。
2,当信息从缓冲区中读取,我们总是去读取当前的缓冲区。当信息需要写到缓存,我们总是在下一缓冲区上操作。 当改变完成后,一个交换操作会立刻将当前缓冲区和下一缓冲区交换, 这样新缓冲区就是公共可见的了。旧的缓冲区则成为了下一个重用的缓冲区。
3,双缓冲模式两点警示:
- 交换本身需要时间,我们一般是通过指针操作,如果交换消耗的时间过长,那么就毫无裨益了。
- 我们得保存两个缓冲区,在内存受限的设备上,你可能要付出惨痛的代价。 如果你不能接受使用两份内存,你需要使用别的方法保证状态在修改时不会被请求。
4,双缓冲模式常用来做帧缓冲区交换。我们几乎可以在任何一个图形API中找到双缓冲模式的应用。如OpenGl中的 swapBuffers() 函数, Direct3D中的“swap chains”,微软XNA框架的 endDraw() 方法。
使用场合:
一般使用情形是在类似同一块内存数据多个对象同时访问的情况。最常用在帧缓冲区交换。大多数主机和电脑上,显卡驱动提供了这种底层的图形系统支持。
以上来自博主夜色魅影文章《序列模式--双缓冲模式——python》
在线程小球上运用双缓冲
- 先在内存中分配一个和我们动画窗口一样大的空间(缓冲区),然后利用getGraphics()方法去获得双缓冲画笔,接着利用双缓冲画笔在缓冲区中绘制我们想要的东西,最后将缓冲区一次性的绘制到窗体中显示出来,这样在我门的动画窗口上面显示出来的图像就非常流畅了。
- 我们可以将小球在不同位置的图作为一帧,那么就需要在双缓冲里面完成前一次小球的擦除和后一次小球的绘制
- 另外将前一篇博客中的多线程改为单线程,小球虽然独立运动,但是现在由缓冲一次绘出同一时刻的所有小球,Ball类不用实现Runnable接口,BallUI来实现:
BallUI重写run函数
public void run() {
//得到缓冲图片
BufferedImage bfimg= new BufferedImage(this.getWidth(),this.getHeight(),BufferedImage.TYPE_INT_ARGB);
//通过缓冲图片对象取到一张缓冲画布
bg = bfimg.getGraphics();
while(true){
try{
Thread.sleep(100);
}catch(Exception e){
e.printStackTrace();
}
UI.drawPic();
}
}
BallUI中在缓冲区绘图:
public void drawPic(){
// 需要绘制的对象 都通缓冲画布来绘制到缓冲图片上
bg.setColor(new Color(240,240,240));
bg.fillRect(0, 0, this.getWidth(), this.getHeight());
for(int i=0;i<num;i++){
ball_array[i].drawBall(bg);
ball_array[i].moveBall(bg);
}
//将缓冲图片通过窗体的画布绘制到窗体上
g.drawImage(bfimg, 0, 0, UI);
}
效果&&全部代码
无检测小球之间相互碰撞的功能
提取码:4uxl
Ball:
package Ball_thread_5_20;
import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;
public class Ball {
public int x,y,dx,dy;
private Color color;
private int r;
private BallUI bui;
Random random=new Random();
//构造函数
public Ball(int x,int y,int r){
this.x=x;
this.y=y;
this.r=random.nextInt(r)+20;
//界面的高度和宽度
//随机生成速度向量
dx=random.nextInt(60)-30;
dy=random.nextInt(60)-30;
//随机生成颜色
int c1,c2,c3;
c1=random.nextInt(255);
c2=random.nextInt(255);
c3=random.nextInt(255);
color=new Color(c1,c2,c3);
}
//得到界面
void setUI(BallUI ui){
this.bui=ui;
}
//判断是否超出边界
public void overEdge(){
if(x+r>bui.getWidth()){
x=bui.getWidth()-r;
dx=-dx;
}
if(y+r>bui.getHeight()){
y=bui.getWidth()-r;
dy=-dy;
}
if(x-r<0){
x=r;
dx=-dx;
}
if(y-r<0){
y=r;
dy=-dy;
}
}
//画出小球
public void drawBall(Graphics gr){
gr.setColor(color);
gr.fillOval(x, y, r, r);
}
//移动小球
public void moveBall(Graphics gr){
x+=dx;y+=dy;
overEdge();
}
}
BallUI:
package Ball_thread_5_20;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
public class BallUI extends JFrame implements MouseListener,Runnable{
/**
* 继承JFrame,实现鼠标监听器接口
*/
private static final long serialVersionUID = 1L;
private Ball []ball_array= new Ball[100];//最多100个ball
private Graphics g,bg ;//窗口和双缓冲画布
private BufferedImage bfimg;//双缓冲图片
private BallUI UI;//窗口对象
private int num,r=20;//小球个数和半径的控制量
//主函数
public static void main(String[] args){
BallUI bui=new BallUI();
bui.init();
}
@Override
/*实现runnable接口*/
public void run() {
//得到缓冲图片
bfimg= new BufferedImage(this.getWidth(),this.getHeight(),BufferedImage.TYPE_INT_ARGB);
//通过缓冲图片对象取到一张缓冲画布
bg = bfimg.getGraphics();
while(true){
try{
Thread.sleep(100);
}catch(Exception e){
e.printStackTrace();
}
UI.drawPic();
}
}
public void drawPic(){
// 需要绘制的对象 都通缓冲画布来绘制到缓冲图片上
bg.setColor(new Color(240,240,240));
bg.fillRect(0, 0, this.getWidth(), this.getHeight());
for(int i=0;i<num;i++){
ball_array[i].drawBall(bg);
ball_array[i].moveBall(bg);
}
//将缓冲图片通过窗体的画布绘制到窗体上
g.drawImage(bfimg, 0, 0, UI);
}
//显示界面
public void init(){
num=0;
UI=this;
this.setTitle("willow's balls~");
this.getContentPane().setBackground(new Color(240,240,240));
this.setSize(1000, 600);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
//添加监听器
this.addMouseListener(this);
g=this.getGraphics();//获得画布
//启动单线程
Thread t= new Thread(this);
t.start();
}
//实现接口函数鼠标点击,响应点击事件
@Override
public void mouseClicked(MouseEvent arg0) {
//在(x,y)生成新的小球并加入小球集合ball_array
int x=arg0.getX(),y=arg0.getY();
Ball b=new Ball(x,y,r);
b.setUI(UI);
ball_array[num++]=b;
}
@Override
public void mouseEntered(MouseEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void mouseExited(MouseEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void mousePressed(MouseEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void mouseReleased(MouseEvent arg0) {
// TODO Auto-generated method stub
}
}