Android Canvas 图层详解

QuibblerAgentQuibblerAgent 2026-05-03 约 19 分钟 389 次阅读

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 图层的使用,可以为应用添加更加丰富和专业的视觉效果,提升用户体验。

相关推荐

精选
ViewPager和PagerAdapter、FragmentPagerAdapter、FragmentStatePager
Android

ViewPager和PagerAdapter、FragmentPagerAdapter、FragmentStatePager

ViewPager1、ViewPagerandroidx.viewpager.widget.ViewPager,Android中使用非常广泛的控件,可以说是APP必备:首次打开引导页、页面Banner广告等。常用方法:setAdapter() 设置适配器setOffscreenPageLimit() 设置缓存的页面个数,默认是 1setCurrentItem() 跳转到特定的页面setOnPage

1.1k
获取Android内置WebView内核版本
Android

获取Android内置WebView内核版本

获取Android内置WebView内核版本竟然能遇到这样奇葩的事情,网页用的技术过于新颖,以至于只支持高版本Chromium内核的,低版本安卓系统中内置的内核版本较低,无法加载前端页面。1、设置查看在系统设置里 > 应用 > 应用管理 > 显示系统应用,查看WebView组件:2、页面查看通过WebView发起的网络请求,都会带上浏览器的UA,通常页面都可以通过UA判断浏览器的内核版本。这里有两

1.1w
Android 14适配总结
Android

Android 14适配总结

Android 14适配总结毕业工作至今已经适配了三个Android大版本,从Android 11到Android 12、再到Android 13。2023年,Google即将推出的Android 14,上半年已经开始第一批适配。现在,第四个Android版本已经适配完,总结记录一下。1、Android 14计划Google一般会在2月份对外发布预告,同时放出开发者预览版。“拉通”各大平台、厂商以

1.1w