android绘制知识--双缓冲绘图技术

来源:互联网 发布:淘宝鼠标经过变换图片 编辑:程序博客网 时间:2024/05/15 05:47

今天我们说下双缓冲绘图技术。其实也没有那么神秘,可以理解为就是一个装载工具,说点接地气的比如搬砖。我们每次可以搬十块砖,从A搬到B,如果是就搬十块我们当然是直接搬过去比较快,但是如果是一百或者一千块呢?你直接搬效率肯定很低,于是我们用身边的小推车一直装上然后推过去。我们的双缓冲技术就是这个“小推车”。

我们知道,我们在绘图时有两样东西是少不了的,一个是Canvas(画布),一个是Paint(画笔)。Canvas提供画各种图形的方法,如画圆(drawCircle),画矩形(drawRect)等等,Paint用来设置画笔的样式,比如笔的粗细,颜色等。每个Canvas内部持有一个Bitmap对象的引用,画图的过程其实就是往这个Bitmap当中写入ARGB信息。

那我们现在考虑一件事情是不是我们调用drawCircle等方法就能立刻画出来了呢
这里我们写个代码测试下

@Override    protected void onDraw(Canvas canvas) {        canvas.drawRect(rect,mPaint);        try {            TimeUnit.MILLISECONDS.sleep(2000);        } catch (InterruptedException e) {            e.printStackTrace();        }        canvas.drawCircle(cx,cy,100,mPaint);    }

上面的代码运行显示,矩形和圆同时出来而不是先出矩形然后再出现圆形,这也就说明了并不是我们调用drawCircle等方法就能立刻绘制出来的。这就说明必须要等onDraw方法执行完成之后,才会把数据交给GPU去处理展示。这就是android绘图当中的第一道缓冲,即显示缓冲区。
而所谓的双缓冲,在android绘图中其实就是再创建一个Canvas和对应的Bitmap,然后在onDraw方法里默认的Canvas通过drawBitmap画刚才new的那个bitmap从而实现双缓冲。用代码简单的表述是这样的:

private void init(){    Bitmap bufferBm = Bitmap.create(getWidth,getHeight,Bitmap.Config.ARGB_8888);    Canvas bufferCanvas = new Canvas(bufferBm);}private void drawSomething(){    bufferCanvas.drawRect();    bufferCanvas.drawCircle();    ...    invalidate();}@Overrideprotected void onDraw(Canvas canvas) {    canvas.drawBitmap(bufferBm,0,0,null);}

示意图如下:
这里写图片描述

这样你可以清晰的看到我们如果只是单纯的换一个图的话简直是画蛇添足,但是我们如果是做画板或者是动态的制造图形的话我们这样就很有必要了就不用每次在ondraw重复的调用绘制方法,这样说有点抽象我给出个点击出现圆形的栗子看看就比较清楚了

没用双缓冲的时候的代码

public class MyView extends View {    private Paint mPaint;    private List<Point> mPoints;    public MyView(Context context) {        super(context);    }    public MyView(Context context, AttributeSet attrs) {        super(context, attrs);        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);        mPaint.setStyle(Paint.Style.FILL);        mPaint.setColor(Color.GREEN);        setBackgroundColor(Color.WHITE);        mPoints = new ArrayList<>();    }    @Override    public boolean onTouchEvent(MotionEvent event) {        int action = event.getAction();        switch (action){            case MotionEvent.ACTION_DOWN:                mPoints.add(new Point((int)event.getX(),(int)event.getY()));                break;            case MotionEvent.ACTION_UP:                invalidate();                break;        }        return true;    }    @Override    protected void onDraw(Canvas canvas) {        for (Point p : mPoints) {            canvas.drawCircle(p.x,p.y,50,mPaint);        }    }}

用了双缓冲

public class NewMyView extends View {    private Paint mPaint;    private Canvas mBufferCanvas;    private Bitmap mBufferBitmap;    public NewMyView(Context context) {        super(context);    }    public NewMyView(Context context, AttributeSet attrs) {        super(context, attrs);        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);        mPaint.setStyle(Paint.Style.FILL);        mPaint.setColor(Color.GREEN);        setBackgroundColor(Color.WHITE);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        int action = event.getAction();        switch (action){            case MotionEvent.ACTION_DOWN:                if (mBufferBitmap == null) {                    mBufferBitmap = Bitmap.createBitmap(getWidth(),getHeight(), Bitmap.Config.ARGB_8888);                    mBufferCanvas = new Canvas(mBufferBitmap);                }                mBufferCanvas.drawCircle((int)event.getX(),(int)event.getY(),50,mPaint);                break;            case MotionEvent.ACTION_UP:                invalidate();                break;        }        return true;    }    @Override    protected void onDraw(Canvas canvas) {        if (mBufferBitmap == null) {            return;        }        canvas.drawBitmap(mBufferBitmap,0,0,null);    }}

咱们对比下,ondraw

 @Override    protected void onDraw(Canvas canvas) {        for (Point p : mPoints) {            canvas.drawCircle(p.x,p.y,50,mPaint);        }    }//使用双缓冲的    @Override    protected void onDraw(Canvas canvas) {        if (mBufferBitmap == null) {            return;        }        canvas.drawBitmap(mBufferBitmap,0,0,null);    }

没用双缓冲的每次我们点击屏幕绘制圆形的时候会在onDraw()循环调用绘制就好像一块一块的搬砖毫无效率,而我们的双缓冲呢只需绘制一次,其他的绘制都绘制在了mBufferBitmap 就好像装车一样

从上面我们可以得出结论:

  • 在绘制数据量较小时,不使用双缓冲,GPU的负荷更低,即绘制性能更高;
  • 在绘制数据量较大时,使用双缓冲绘图,绘制性能明显高于不使用双缓冲的情况;
  • 使用双缓冲会增加内存消耗。

其实上面的结论也很好理解,就像上面举的搬砖的例子,如果砖少的话,用车来拉明显是划不来的,砖的数量很多的时候,用车来拉就可以节省很多时间,但是用车就要消耗额外的资源,这就需要根据不同的情况做出正确的选择。

0 0
原创粉丝点击