在上一篇中有讲到绘制一个水波纹效果,当打开此界面过久时,会有明显的卡顿,查看内存很稳定,
这时候我们先打开开发者选项里的”GPU呈现模式分析“,设置为“在屏幕上显示为条形图”(不同的手机可能有略微的差异,我这里用的是小米)。
可以看到,当重复绘制时,GPU的负荷太高,卡顿也就再所难免。
如何解决
当我们只绘制一段水波纹不使用canvas.clipPath(mPath, Region.Op.INTERSECT);
时,查看
此时的GPU显示比较稳定的,那么现在有一个问题,画水波纹和画圆是两个独立的动作,能不能分开执行,答案是必须要等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.drawXxx();
bufferCanvas.drawXxx();
...
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(bufferBm,0,0,null);
}
双缓冲绘图的优缺点及适用场景
现在我们来改进上一篇的代码
...
mPaintClear = new Paint();
mPaintClear.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
...
...
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
offset = (int) animation.getAnimatedValue();
if (mScreenWidth > 0 && mScreenHeight > 0) {
drawWave(offset);
postInvalidate();
}
}
});
...
private void drawWave(int offset) {
if (mBufferBitmap == null) {
mBufferBitmap = Bitmap.createBitmap(mScreenWidth, mScreenHeight, Bitmap.Config.ARGB_8888);
mBufferCanvas = new Canvas(mBufferBitmap);
}
//画圆
mBufferCanvas.drawCircle(mScreenWidth / 2, mCenterY, 300, mPaint2);
mPath.reset();
mPath.moveTo(-mWL + offset, mCenterY);
for (int i = 0; i < mWaveCount; i++) {
mPath.quadTo((-mWL * 3 / 4) + (i * mWL) + offset, mCenterY + 60, (-mWL / 2) + (i * mWL) + offset, mCenterY);
mPath.quadTo((-mWL / 4) + (i * mWL) + offset, mCenterY - 60, i * mWL + offset, mCenterY);
}
mPath.lineTo(mScreenWidth, mScreenHeight);
mPath.lineTo(0, mScreenHeight);
mPath.close();
mPath2.addCircle(mScreenWidth / 2, mCenterY, 300, Path.Direction.CCW);
//api19以上才能用
// mPath.op(mPath2, Path.Op.INTERSECT);
//改用canvas.clipPath
mBufferCanvas.clipPath(mPath2, Region.Op.INTERSECT);
mBufferCanvas.drawPath(mPath, mPaint);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
/*
使用双缓存策略,减轻卡顿
*/
if (mBufferBitmap == null) {
return;
}
canvas.setDrawFilter(pfd);
canvas.drawBitmap(mBufferBitmap, 0, 0, null);
}
GPU绘制是正常了,我的波纹呢???
大写的黑人问号,其实问题出在上面,
if (mBufferBitmap == null) {
mBufferBitmap = Bitmap.createBitmap(mScreenWidth, mScreenHeight, Bitmap.Config.ARGB_8888);
mBufferCanvas = new Canvas(mBufferBitmap);
}
只要在绘制前把它清屏就好了
mPaintClear = new Paint();
mPaintClear.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
mBufferCanvas.drawColor(Color.TRANSPARENT);
//清屏
mBufferCanvas.drawPaint(mPaintClear);
我们再调试一把
从上面的实验数据我们可以得出结论:
- 在绘制数据量较小时,不使用双缓冲,GPU的负荷更低,即绘制性能更高;
- 在绘制数据量较大时,使用双缓冲绘图,绘制性能明显高于不使用双缓冲的情况;
- 使用双缓冲会增加内存消耗。
其实上面的结论也很好理解,就像上面举的搬砖的例子,如果砖少的话,用车来拉明显是划不来的,砖的数量很多的时候,用车来拉就可以节省很多时间,但是用车就要消耗额外的资源,这就需要根据不同的情况做出正确的选择。
android的双缓冲绘图技术今天就先讲到这里,有不对的地方或大家有什么问题欢迎留言。
github代码下载
也欢迎大家关注我的CSDN和github主页