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,也就是说同一片区域这次没画完下次就不能画,因此要提高双缓冲的效率,就得开一条线程专门画图,开另外一条线程做预处理的工作。
 

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();
        }
    }
}

 

posted @ 2015-06-10 19:57  f9q  阅读(800)  评论(0)    收藏  举报