SurfaceView(6)双缓冲
1.定义
不用画布,直接在窗口上进行绘图叫做无缓冲绘图。用了一个画布,将所有内容都先画到画布上,在整体绘制到窗口上,就该叫做单缓冲绘图,那个画布就是一个缓冲区。用了两个画布,一个进行临时的绘图,一个进行最终的绘图,这样就叫做双缓冲绘图。
surfaceView自身实现了双缓冲,而View没有。其实view你也可以自己实现,但是实现的结构不如surfaceView好。
surfaceView通过 surfaceHolder.lockCanvas 锁定画布,实现下一张图片的绘制,再通过另外的线程刷新界面,绘制图片。
view则是直接在ondraw里绘制图片,刷新界面。其实view也可以实现双缓冲机制,
你可以在另个出ondraw的方法中绘制下一张bitmap(参见:http://blog.csdn.net/liubingzhao/article/details/5563113),
也可以另开一个线程,处理除了绘制图片以外的操作(参见:http://topic.csdn.net/u/20110901/23/e283f805-20dc-40c3-8381-403dd1ca69b0.html),就实现了view的双缓冲。
2.为什么动态绘图surfaceView要比View好?
因为View是在UI主线程中进行绘制的,绘制时会阻塞主线程,如果ontouch事件又处理的比较多的话会导致界面卡。而surfaceView是另开了一个线程绘制的,再加上双缓冲机制,所以要高效。不会卡。其实现在一般实现view的时候一般都会在其他出先生成bitmap在给ondraw去画,所以双缓冲的作用不是那么明显了(个人认为)。
双缓冲是为了防止动画闪烁而实现的一种多线程应用,基于SurfaceView的双缓冲实现很简单,开一条线程并在其中绘图即可。本文介绍基于SurfaceView的双缓冲应用 ,以及介绍类似的更高效的实现方法。
本文程序运行截图如下,左边是开单个线程读取并绘图,右边是开两个线程,一个专门读取图片,一个专门绘图:
对比一下,右边动画的帧速明显比左边的快,左右两者都没使用Thread.sleep()。为什么要开两个线程一个读一个画,而不去开两个线程像左边那样都 “边读边画”呢?因为SurfaceView每次绘图都会锁定Canvas,也就是说同一片区域这次没画完下次就不能画,因此要提高双缓冲的效率,就得开一条线程专门画图,开另外一条线程做预处理的工作。
本文程序运行截图如下,左边是开单个线程读取并绘图,右边是开两个线程,一个专门读取图片,一个专门绘图:
对比一下,右边动画的帧速明显比左边的快,左右两者都没使用Thread.sleep()。为什么要开两个线程一个读一个画,而不去开两个线程像左边那样都 “边读边画”呢?因为SurfaceView每次绘图都会锁定Canvas,也就是说同一片区域这次没画完下次就不能画,因此要提高双缓冲的效率,就得开一条线程专门画图,开另外一条线程做预处理的工作。
3. 程序运行截图

4.java文件
public class TwoCachesFrgmt extends Fragment implements OnClickListener { private SurfaceView surfaceView; private SurfaceHolder surfaceHolder; private ArrayList<Bitmap> imgList = new ArrayList<Bitmap>(); private Button btnNoTwocaches, btnTwocaches; private int imgWidth, imgHeight; private Bitmap bitmap;// 独立线程读取,独立线程绘图 private Paint paint = new Paint(); private ExecutorService threadPool = Executors.newFixedThreadPool(3); @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.frgmt_two_caches, container, false); surfaceView = (SurfaceView) v.findViewById(R.id.sfv); surfaceHolder = surfaceView.getHolder(); surfaceHolder.addCallback(new SurfaceCallback()); // 下面代码修复闪黒屏问题 surfaceView.setBackgroundColor(Color.argb(0x00, 0, 0, 0));// 透明背景色 surfaceView.setZOrderOnTop(true);// SurfaceView位于布局的最顶部 surfaceHolder.setFormat(PixelFormat.TRANSPARENT);// 设置背景透明 btnNoTwocaches = (Button) v.findViewById(R.id.btn_no_two_caches); btnTwocaches = (Button) v.findViewById(R.id.btn_two_caches); btnNoTwocaches.setOnClickListener(this); btnTwocaches.setOnClickListener(this); return v; } public int getSize() { return imgList.size(); } public synchronized void getNextImage(int index) { bitmap = imgList.get(index); } public void drawImg(int x, int y) { Canvas canvas = surfaceHolder.lockCanvas(new Rect(x, y, x + imgWidth, y + imgHeight)); if (canvas != null) { canvas.drawBitmap(bitmap, x, y, paint); surfaceHolder.unlockCanvasAndPost(canvas);// 更新屏幕显示内容 } } @Override public void onClick(View v) { final TwoCachesFrgmt tcf = this; switch (v.getId()) { case R.id.btn_no_two_caches: btnNoTwocaches.setClickable(false); /** * 读取并显示图片的线程 */ Runnable t1 = new Runnable() { int index = 0; public void run() { while (getSize() > 0) { tcf.getNextImage(index); tcf.drawImg(0, 0); index++; if (index == imgList.size()) index = 0; } } }; threadPool.execute(t1);// 开一条线程读取并绘图 break; case R.id.btn_two_caches: btnTwocaches.setClickable(false); /** * 只负责读取图片的线程 */ Runnable t2 = new Runnable() { int index = 0; public void run() { while (getSize() > 0) { tcf.getNextImage(index); index++; if (index == imgList.size()) // 如果到尽头则重新读取 index = 0; } } }; threadPool.execute(t2);// 开一条线程读取 /** * 只负责绘图的线程 */ Runnable t3 = new Runnable() { public void run() { while (getSize() > 0) { tcf.drawImg(imgWidth + 10, 0); } } }; threadPool.execute(t3);// 开一条线程绘图 break; default: break; } } class SurfaceCallback implements SurfaceHolder.Callback { @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.i("Surface:", "Change"); } @Override public void surfaceCreated(SurfaceHolder holder) { Log.i("Surface:", "Create"); Bitmap img; // 用反射机制来获取资源中的图片ID和尺寸 Field[] fields = R.drawable.class.getDeclaredFields(); for (Field field : fields) { if (!"ic_launcher".equals(field.getName()))// 除了icon之外的图片 { int imgId = 0; try { imgId = field.getInt(R.drawable.class); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } // 保存图片 img = BitmapFactory.decodeResource(getResources(), imgId); imgList.add(img); } } // 取得图像大小 img = imgList.get(0); imgWidth = img.getWidth(); imgHeight = img.getHeight(); } @Override public void surfaceDestroyed(SurfaceHolder holder) { Log.i("Surface:", "Destroy"); threadPool.shutdown(); imgList.clear(); } } }