Android Canvas 图层详解
1、Canvas 图层概述
Canvas 图层是 Android 绘图系统中的重要概念,它允许开发者在绘制过程中创建和管理多个绘图层,实现复杂的视觉效果。图层可以看作是独立的绘图表面,支持保存和恢复绘图状态,为绘制操作提供了更大的灵活性。
核心概念:
- 图层:独立的绘图表面,拥有自己的状态
- 画布状态:包括矩阵变换、裁剪区域、绘制属性等
- 图层栈:用于管理多个图层的堆栈结构
- 合成模式:控制图层间的混合方式
图层的作用:
- 实现复杂的视觉效果
- 隔离不同的绘制操作
- 提高绘制效率
- 支持状态的保存和恢复
2、Canvas 对象的获取方法
2.1、重写 onDraw 和 dispatchDraw 方法
在自定义 View 时,系统会通过 onDraw 和 dispatchDraw 方法传入 Canvas 对象:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 使用 canvas 绘制视图自身
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
// 使用 canvas 绘制子视图
} 区别:
- onDraw():绘制视图自身
- dispatchDraw():绘制子视图
- ViewGroup 有背景时会调用 onDraw(),否则直接调用 dispatchDraw()
- View 中 onDraw() 和 dispatchDraw() 都会被调用
2.2、使用 Bitmap 创建 Canvas
通过 Bitmap 创建 Canvas,可以在内存中进行绘制:
// 创建空白 Bitmap
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
// 创建 Canvas
Canvas canvas = new Canvas(bitmap);
// 或者
Canvas canvas = new Canvas();
canvas.setBitmap(bitmap);
注意事项:
- 在 Bitmap 上绘制的内容不会直接显示在 View 上
- 需要使用 onDraw() 中的 Canvas 将 Bitmap 绘制出来
示例:
public class BitmapCanvasView extends View {
private Bitmap mBmp;
private Paint mPaint;
private Canvas mBmpCanvas;
public BitmapCanvasView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mPaint.setColor(Color.RED);
mBmp = Bitmap.createBitmap(500, 500, Bitmap.Config.ARGB_8888);
mBmpCanvas = new Canvas(mBmp);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setTextSize(100);
// 在 Bitmap 上绘制
mBmpCanvas.drawText("Hello Canvas", 0, 100, mPaint);
// 将 Bitmap 绘制到 View 上
canvas.drawBitmap(mBmp, 0, 0, mPaint);
}
}
2.3、使用 SurfaceHolder 获取 Canvas
在使用 SurfaceView 时,可以通过 SurfaceHolder 获取 Canvas:
SurfaceHolder holder = surfaceView.getHolder();
Canvas canvas = holder.lockCanvas();
try {
// 使用 canvas 绘制
} finally {
holder.unlockCanvasAndPost(canvas);
}
3、图层操作核心方法
3.1、save() 和 restore() 方法
save() 方法保存当前画布状态到栈中,restore() 方法恢复到之前保存的状态:
// 保存当前状态
int saveCount = canvas.save();
// 执行绘制操作
canvas.translate(100, 100);
canvas.drawRect(0, 0, 200, 200, paint);
// 恢复到之前的状态
canvas.restore();
3.2、saveLayer() 方法
saveLayer() 方法创建一个新的图层并保存到栈中:
// 保存指定区域的图层
int layerId = canvas.saveLayer(0, 0, width, height, paint, Canvas.ALL_SAVE_FLAG);
// 执行绘制操作
canvas.drawBitmap(dstBitmap, 0, 0, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(srcBitmap, 0, 0, paint);
paint.setXfermode(null);
// 恢复到之前的状态
canvas.restoreToCount(layerId);
saveLayer() 方法的参数:
- rect:要保存的矩形区域
- paint:用于图层合成的 Paint
- saveFlags:保存标志,如 ALL_SAVE_FLAG、CLIP_SAVE_FLAG 等
3.3、restoreToCount() 方法
restoreToCount() 方法恢复到指定的保存点:
int saveCount = canvas.save();
// 绘制操作 1
int saveCount2 = canvas.save();
// 绘制操作 2
// 恢复到第一次保存的状态
canvas.restoreToCount(saveCount);
4、图层的实际应用
4.1、实现复杂的合成效果
public class XferModeView extends View {
private int width = 400;
private int height = 400;
private Bitmap dstBitmap;
private Bitmap srcBitmap;
private Paint paint;
public XferModeView(Context context, AttributeSet attrs) {
super(context, attrs);
setLayerType(LAYER_TYPE_SOFTWARE, null);
paint = new Paint();
dstBitmap = makeDst(width, height);
srcBitmap = makeSrc(width, height);
}
static Bitmap makeSrc(int width, int height) {
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bitmap);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(0xFF66AAFF);
c.drawRect(0, 0, width, height, paint);
return bitmap;
}
static Bitmap makeDst(int width, int height) {
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bitmap);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setColor(0xFFFFCC44);
c.drawOval(new RectF(0, 0, width, height), p);
return bitmap;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.GREEN);
int layerId = canvas.saveLayer(0, 0, width * 2, height * 2, paint, Canvas.ALL_SAVE_FLAG);
canvas.drawBitmap(dstBitmap, 0, 0, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(srcBitmap, width / 2, height / 2, paint);
paint.setXfermode(null);
canvas.restoreToCount(layerId);
}
}
4.2、实现阴影效果
public class ShadowView extends View {
private Paint mPaint;
public ShadowView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mPaint.setColor(Color.BLUE);
mPaint.setShadowLayer(10, 5, 5, Color.GRAY);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(100, 100, 300, 300, mPaint);
}
}
4.3、实现倒影效果
public class ReflectionView extends View {
private Bitmap mOriginalBitmap;
private Paint mPaint;
public ReflectionView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mOriginalBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 绘制原始图片
canvas.drawBitmap(mOriginalBitmap, 0, 0, mPaint);
// 绘制倒影
Matrix matrix = new Matrix();
matrix.preScale(1, -1);
Bitmap reflectionBitmap = Bitmap.createBitmap(mOriginalBitmap, 0, 0,
mOriginalBitmap.getWidth(), mOriginalBitmap.getHeight(), matrix, false);
canvas.save();
canvas.translate(0, mOriginalBitmap.getHeight() * 2);
canvas.drawBitmap(reflectionBitmap, 0, 0, mPaint);
canvas.restore();
}
}
5、最佳实践与注意事项
5.1、性能优化
- 避免频繁创建图层:图层创建会消耗内存和CPU资源
- 合理使用 save() 和 restore():只保存必要的状态
- 对于复杂绘制,考虑使用硬件加速
- 适当使用 LAYER_TYPE_HARDWARE 或 LAYER_TYPE_SOFTWARE
5.2、图层使用建议
- 对于简单绘制,直接使用 onDraw() 中的 Canvas
- 对于复杂合成效果,使用 saveLayer()
- 对于离屏绘制,使用 Bitmap 创建 Canvas
- 对于 SurfaceView,使用 SurfaceHolder 获取 Canvas
5.3、常见问题
问题1:绘制内容不显示
解决:检查是否正确使用 Canvas,特别是使用 Bitmap 创建的 Canvas 需要调用 drawBitmap()
问题2:图层操作后绘制异常
解决:确保每次 save() 后都有对应的 restore()
问题3:性能问题
解决:减少图层创建次数,合理使用硬件加速
5.4、总结
Canvas 图层是 Android 绘图系统中的强大工具,通过合理使用图层,可以实现各种复杂的视觉效果。掌握图层的创建、保存和恢复操作,对于开发自定义 View 和实现高级视觉效果至关重要。
关键要点:
- 理解 onDraw() 和 dispatchDraw() 的区别
- 掌握 save()、restore() 和 saveLayer() 方法
- 合理使用不同的 Canvas 获取方式
- 注意性能优化,避免过度使用图层
- 结合合成模式实现复杂效果
通过学习和实践 Canvas 图层的使用,可以为应用添加更加丰富和专业的视觉效果,提升用户体验。