双缓冲技术局部更新原理

  • SurfaceView支持局部更新,可以通过Canvas lockCanvas(Rect dirty)函数指定获取画布的区域和大小,画布以外的地方会将现在屏幕上的内容复制过来,以保持与屏幕一致,而画布以内的部分将会保持原画布的内容:
  • lockCanvas():用于获取整个画布的内容,屏幕内容不会被更新到画布上,画布保持原画布内容(不会继承当前屏幕内容,只保持所有在自己上面所画的内容,即为利用unlockCanvasAndPost(canvas)更新的内容,拿到之后初始态就是这些内容)
  • lockCanvas(Rect dirty):用于获取指定区域的画布,画布以外的区域会保持与屏幕内容一致,画布以内的区域依然保持原画布内容
public class RectView extends SurfaceView {
    private Paint paint;
    public RectView(Context context) {
        super(context);
        init();
    }


    public RectView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }


    public RectView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }


    private void init() {
        paint = new Paint();
        paint.setTextSize(30);
        SurfaceHolder surfaceHolder = getHolder();
        surfaceHolder.addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                drawText(holder);
            }


            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {


            }


            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {


            }
        });
    }
    private void drawText(final SurfaceHolder surfaceHolder){
        new Thread(new Runnable() {
            @Override
            public void run() {
                while(true) {
                    Rect dirtyRect = new Rect(0, 0, 1, 1);
                    Canvas canvas = surfaceHolder.lockCanvas(dirtyRect);
                    Rect canvasRect = canvas.getClipBounds();
                    if (getWidth() == canvasRect.width() && getHeight() == canvasRect.height()) {
                        canvas.drawColor(Color.BLACK);
                        surfaceHolder.unlockCanvasAndPost(canvas);
                    } else {
                        surfaceHolder.unlockCanvasAndPost(canvas);
                        break;
                    }
                }


                for(int i = 0;i<10;i++){
                    //绘制最大的方形,拿到缓冲画布,讲话不填充为红色,然后更新到屏幕上,画布意外的部分是黑色的原因是我们使用的获取方法,在矩形内部是使用我们绘制的结果,矩形外面是使用杯换下来的屏幕上的内容(清平使用的是黑色背景)b
                    if(i == 0){
                        Canvas canvas = surfaceHolder.lockCanvas(new Rect(10, 10, 600, 600));
                        canvas.drawColor(Color.RED);
                        surfaceHolder.unlockCanvasAndPost(canvas);
                    }
                    
                    if(i == 1){
                        Canvas canvas = surfaceHolder.lockCanvas(new Rect(30, 30, 570, 570));
                        canvas.drawColor(Color.GREEN);
                        surfaceHolder.unlockCanvasAndPost(canvas);
                    }
                    if(i==2){
                        Canvas canvas = surfaceHolder.lockCanvas(new Rect(60,60,540,540));
                        canvas.drawColor(Color.BLUE);
                        surfaceHolder.unlockCanvasAndPost(canvas);
                    }
                    if(i==3){
                        Canvas canvas = surfaceHolder.lockCanvas(new Rect(200, 200, 400, 400));
                        canvas.drawColor(Color.argb(0x3F, 0xFF, 0xFF, 0xFF));
                        canvas.drawCircle(300, 300, 100, paint);
                        surfaceHolder.unlockCanvasAndPost(canvas);
                    }


                    if(i==4){
                        Canvas canvas = surfaceHolder.lockCanvas();
                        paint.setColor(Color.RED);
                        canvas.drawText(i + " ", 300, 300, paint);
                        surfaceHolder.unlockCanvasAndPost(canvas);
                    }
                    try{
                        Thread.sleep(800);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }




    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        paint.setColor(Color.RED);
        canvas.drawRect(new Rect(10, 10, 600, 600), paint);
        paint.setColor(Color.GREEN);
        canvas.drawRect(new Rect(30, 30, 570,  570),paint);
        paint.setColor(Color.BLUE);
        canvas.drawRect(new Rect(60, 60, 540, 540), paint);
        paint.setColor(Color.argb(0x3F, 0xFF, 0xFF, 0xFF));
        canvas.drawCircle(300, 300, 100,paint);
        paint.setColor(Color.GREEN);
        canvas.drawText("6", 300, 300, paint);
    }
}
  • 总结:
    • 缓冲画布是根据LRU策略被存取使用的
    • 使用holder.lockCanvas(rect)函数获取到的画布区域,再通过unlockCanvasAndPost(canvas)函数提交到屏幕上时,指定区域内的内容是我们自己的绘图结果,指定区域外的内容是从屏幕上复制过来的,与当前屏幕一致
    • 为了防止画布以内的缓冲画布本身的图像与所画内容产生冲突,在对画布以内的区域作画时,建议先清空画布
Paint paint  = new Paint();
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawPaint(paint);
  • 为什么使用清屏函数:
    • 首先这里有三块画布,有一块画布初始化地被显示在屏幕上,已经被默认填充为黑色,而另外两块画布还没有被绘制过,虽然我们指定了获取画布的区域范围,但是系统认为整块画布都是脏区域,都应该被画上,所以会返回屏幕大小的画布。只有我们将每块画布都画过后,才会按照我们指定的区域返回画布的大小
    • 所以每次我们只需要指定一个特别小的区域作为我们占用画布的技巧,可以看到这样做之后,每次返回的画布不和当前控件的大小一致,证明可以正常的使用画布了
Rect dirtyRect = new Rect(0, 0, 1, 1);
    Canvas canvas = surfaceHolder.lockCanvas(dirtyRect);
  • 完整清屏代码
while(true) {
    Rect dirtyRect = new Rect(0, 0, 1, 1);
    Canvas canvas = surfaceHolder.lockCanvas(dirtyRect);
    Rect canvasRect = canvas.getClipBounds();
    if (getWidth() == canvasRect.width() && getHeight() == canvasRect.height()) {
        canvas.drawColor(Color.BLACK);
        surfaceHolder.unlockCanvasAndPost(canvas);
    } else {
        surfaceHolder.unlockCanvasAndPost(canvas);
        break;
    }
}


猜你喜欢

转载自blog.csdn.net/qq_39424143/article/details/94589384