一、引言

在Android开发中,Canvas(画布)是2D图形绘制的核心,它为开发者提供了丰富的绘图API。从简单的几何图形到复杂的自定义控件,从静态图表到动态特效,Canvas几乎无处不在。然而,很多开发者仅仅停留在使用Canvas绘制基本图形的层面,对于其高级特性和性能优化了解甚少。

本文将从Canvas的基础知识出发,逐步深入到高级绘图技巧和特效实现。我们将探讨Canvas的核心组件、高级绘图技巧、性能优化方法以及如何实现各种炫酷的视觉效果。无论你是刚接触Canvas的新手,还是有一定经验想要深入学习的开发者,本文都将为你提供实用的知识和技巧。

为什么需要掌握Canvas高级技巧?

  1. 自定义UI需求:当系统提供的控件无法满足设计需求时,自定义View成为唯一选择
  2. 性能优化:不当的Canvas使用会导致性能问题,影响应用流畅度
  3. 视觉效果提升:高级绘图技巧可以实现更丰富的视觉效果,提升用户体验
  4. 游戏和图形应用开发:Canvas是2D游戏和图形应用的基础

二、Canvas绘图基础回顾

2.1 Canvas核心组件

Canvas类:绘图画布
Canvas是绘图的载体,提供了各种绘制方法。需要注意的是,Canvas本身并不存储绘制的图形,它更像是一个"绘制指令"的执行者。

// Canvas的基本使用
Canvas canvas = new Canvas(bitmap); // 使用Bitmap作为画布
canvas.drawColor(Color.WHITE); // 设置画布背景色

Paint类:画笔与样式控制
Paint控制绘制的样式,包括颜色、粗细、字体、特效等。正确配置Paint是高效绘图的关键。

// Paint的详细配置
Paint paint = new Paint();
paint.setColor(Color.RED);          // 设置颜色
paint.setStyle(Paint.Style.FILL);   // 填充样式
paint.setStrokeWidth(5);            // 线条宽度
paint.setAntiAlias(true);           // 开启抗锯齿
paint.setTextSize(36);              // 文字大小
paint.setAlpha(128);                // 设置透明度(0-255)

Bitmap类:图像载体
Bitmap是Canvas绘图的最终载体,也是离屏缓冲的基础。

// 创建Bitmap的几种方式
// 1. 创建空Bitmap
Bitmap bitmap = Bitmap.createBitmap(800, 600, Bitmap.Config.ARGB_8888);

// 2. 从资源加载
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image);

// 3. 从文件加载
Bitmap bitmap = BitmapFactory.decodeFile("/sdcard/image.jpg");

Path类:路径绘制
Path用于绘制复杂的图形,支持直线、曲线和组合图形。

// Path的基本使用
Path path = new Path();
path.moveTo(100, 100);      // 移动到起点
path.lineTo(200, 100);      // 画直线
path.quadTo(250, 50, 300, 100); // 画二次贝塞尔曲线
path.close();               // 闭合路径
canvas.drawPath(path, paint);

2.2 基本绘图操作

几何图形绘制

// 矩形
canvas.drawRect(100, 100, 300, 300, paint);
// 圆角矩形
canvas.drawRoundRect(100, 100, 300, 300, 20, 20, paint);
// 圆形
canvas.drawCircle(200, 200, 100, paint);
// 椭圆
canvas.drawOval(100, 100, 300, 200, paint);
// 弧形
canvas.drawArc(100, 100, 300, 300, 0, 90, true, paint);

文本绘制

// 基本文本绘制
canvas.drawText("Hello Canvas", 100, 100, paint);

// 获取文本尺寸
Rect bounds = new Rect();
paint.getTextBounds("Hello", 0, "Hello".length(), bounds);
float textWidth = paint.measureText("Hello");
float textHeight = bounds.height();

// 多行文本绘制
String text = "This is a multi-line\ntext example";
StaticLayout layout = new StaticLayout(
    text, paint, canvas.getWidth(),
    Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
layout.draw(canvas);

图像绘制

// 基本图像绘制
canvas.drawBitmap(bitmap, 100, 100, paint);

// 缩放绘制
Rect src = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
Rect dst = new Rect(100, 100, 500, 400);
canvas.drawBitmap(bitmap, src, dst, paint);

2.3 坐标系与变换基础

屏幕坐标系
Android的Canvas使用左上角为原点的坐标系,X轴向右为正,Y轴向下为正。

// 坐标系变换示例
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    
    // 保存当前画布状态
    canvas.save();
    
    // 将原点移动到视图中心
    canvas.translate(getWidth()/2f, getHeight()/2f);
    
    // 旋转45度
    canvas.rotate(45);
    
    // 绘制一个矩形,现在会围绕视图中心旋转
    canvas.drawRect(-100, -100, 100, 100, paint);
    
    // 恢复画布状态
    canvas.restore();
}

变换的保存与恢复
Canvas的变换操作是累积的,使用save()和restore()管理状态栈非常重要。

canvas.save();              // 保存状态1
canvas.translate(100, 100);
canvas.drawRect(0, 0, 50, 50, paint);  // 在(100,100)处绘制

canvas.save();              // 保存状态2
canvas.rotate(45);
canvas.drawRect(0, 0, 50, 50, paint);  // 旋转45度绘制

canvas.restore();           // 恢复到状态2(平移后的状态)
canvas.drawRect(50, 0, 100, 50, paint); // 在(150,100)处绘制

canvas.restore();           // 恢复到状态1(原始状态)

三、Canvas高级绘图技巧

3.1 路径(Path)高级应用

贝塞尔曲线绘制复杂图形

贝塞尔曲线是绘制平滑曲线的关键工具,Android支持二次和三次贝塞尔曲线。

// 心形路径绘制
private Path createHeartPath(float width, float height) {
    Path path = new Path();
    
    // 从底部尖端开始
    path.moveTo(width / 2, height);
    
    // 绘制左侧曲线
    path.cubicTo(
        width / 8, height * 3 / 4,    // 控制点1
        width / 8, height / 4,        // 控制点2
        width / 2, height / 4         // 结束点
    );
    
    // 绘制右侧曲线
    path.cubicTo(
        width * 7 / 8, height / 4,    // 控制点1
        width * 7 / 8, height * 3 / 4, // 控制点2
        width / 2, height             // 结束点
    );
    
    path.close();
    return path;
}

路径运算

Android P及以上版本支持路径布尔运算,可以创建复杂的组合图形。

// 路径运算示例
@RequiresApi(api = Build.VERSION_CODES.P)
private Path createCompositePath() {
    // 创建两个圆形路径
    Path circle1 = new Path();
    circle1.addCircle(200, 200, 100, Path.Direction.CW);
    
    Path circle2 = new Path();
    circle2.addCircle(300, 200, 100, Path.Direction.CW);
    
    // 执行并集运算
    circle1.op(circle2, Path.Op.UNION);
    
    // 其他运算类型:
    // Op.INTERSECT - 交集
    // Op.DIFFERENCE - 差集
    // Op.REVERSE_DIFFERENCE - 反向差集
    // Op.XOR - 异或
    
    return circle1;
}

路径测量与文字沿路径排列

PathMeasure可以精确测量路径长度和位置,常用于进度指示器和路径动画。

// 路径测量和文字沿路径排列
private void drawTextAlongPath(Canvas canvas, String text, Path path) {
    PathMeasure pathMeasure = new PathMeasure(path, false);
    float pathLength = pathMeasure.getLength();
    
    // 计算文字总长度
    float textWidth = paint.measureText(text);
    
    // 计算起始偏移,使文字居中
    float offset = (pathLength - textWidth) / 2;
    
    // 绘制文字沿路径
    canvas.drawTextOnPath(text, path, offset, 0, paint);
    
    // 获取路径上的点
    float[] pos = new float[2];
    float[] tan = new float[2];
    
    // 获取路径中点位置和切线角度
    pathMeasure.getPosTan(pathLength / 2, pos, tan);
    float angle = (float) (Math.atan2(tan[1], tan[0]) * 180 / Math.PI);
    
    // 在路径中点绘制标记
    canvas.save();
    canvas.translate(pos[0], pos[1]);
    canvas.rotate(angle);
    canvas.drawCircle(0, 0, 10, paint);
    canvas.restore();
}

路径特效

DashPathEffect可以创建虚线、点线等效果。

// 虚线效果
private void drawDashedPath(Canvas canvas) {
    Paint dashedPaint = new Paint(paint);
    
    // 创建虚线效果:实线10px,间隔5px
    DashPathEffect dashEffect = new DashPathEffect(
        new float[]{10, 5}, 0);
    dashedPaint.setPathEffect(dashEffect);
    
    // 创建相位动画的虚线
    float phase = (System.currentTimeMillis() / 50) % 15;
    DashPathEffect animatedDashEffect = new DashPathEffect(
        new float[]{10, 5}, phase);
    
    // 复合路径效果:先虚线再模糊
    PathEffect pathEffect = new ComposePathEffect(
        animatedDashEffect,
        new CornerPathEffect(20) // 圆角效果
    );
    dashedPaint.setPathEffect(pathEffect);
    
    // 绘制路径
    Path path = new Path();
    path.moveTo(100, 100);
    path.lineTo(500, 100);
    path.lineTo(500, 500);
    path.lineTo(100, 500);
    path.close();
    
    canvas.drawPath(path, dashedPaint);
}

3.2 画笔(Paint)高级配置

3.2.1 着色器(Shader)深度应用

着色器定义了图形的填充方式,可以创建渐变、纹理等效果。

// 线性渐变
private Shader createLinearGradient() {
    return new LinearGradient(
        0, 0,                   // 起始点
        getWidth(), getHeight(), // 结束点
        Color.RED,              // 起始颜色
        Color.BLUE,             // 结束颜色
        Shader.TileMode.CLAMP   // 边缘处理模式
    );
}

// 径向渐变
private Shader createRadialGradient() {
    return new RadialGradient(
        getWidth() / 2f,        // 圆心X
        getHeight() / 2f,       // 圆心Y
        Math.min(getWidth(), getHeight()) / 2f, // 半径
        Color.YELLOW,           // 中心颜色
        Color.TRANSPARENT,      // 边缘颜色
        Shader.TileMode.CLAMP
    );
}

// 扫描渐变(环形渐变)
private Shader createSweepGradient() {
    return new SweepGradient(
        getWidth() / 2f,
        getHeight() / 2f,
        new int[]{Color.RED, Color.YELLOW, Color.GREEN, Color.BLUE, Color.RED},
        new float[]{0, 0.25f, 0.5f, 0.75f, 1}
    );
}

// 位图着色器
private Shader createBitmapShader(Bitmap bitmap) {
    return new BitmapShader(
        bitmap,
        Shader.TileMode.REPEAT,   // X方向平铺模式
        Shader.TileMode.MIRROR    // Y方向镜像模式
    );
}

// 组合着色器
private Shader createComposeShader() {
    Shader gradient = new LinearGradient(0, 0, 0, getHeight(),
        Color.RED, Color.BLUE, Shader.TileMode.CLAMP);
    
    Bitmap patternBitmap = BitmapFactory.decodeResource(
        getResources(), R.drawable.pattern);
    Shader bitmapShader = new BitmapShader(patternBitmap,
        Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
    
    // 使用PorterDuff混合模式组合着色器
    return new ComposeShader(gradient, bitmapShader,
        PorterDuff.Mode.MULTIPLY);
}

3.2.2 颜色过滤器(ColorFilter)

颜色过滤器可以修改绘图的颜色,实现各种滤镜效果。

// LightingColorFilter - 模拟光照效果
private ColorFilter createLightingFilter() {
    // 参数1: 乘以RGB值 (0xRRGGBB)
    // 参数2: 加上RGB值 (0xRRGGBB)
    return new LightingColorFilter(0xFF808080, 0x00000000);
}

// PorterDuffColorFilter - 使用PorterDuff模式混合颜色
private ColorFilter createPorterDuffFilter() {
    return new PorterDuffColorFilter(
        Color.argb(150, 255, 0, 0), // 红色,150透明度
        PorterDuff.Mode.SRC_ATOP
    );
}

// ColorMatrixColorFilter - 强大的矩阵变换
private ColorFilter createColorMatrixFilter() {
    ColorMatrix colorMatrix = new ColorMatrix();
    
    // 1. 黑白效果(去饱和度)
    // colorMatrix.setSaturation(0);
    
    // 2. 复古效果(棕褐色)
    float[] sepia = {
        0.393f, 0.769f, 0.189f, 0, 0,
        0.349f, 0.686f, 0.168f, 0, 0,
        0.272f, 0.534f, 0.131f, 0, 0,
        0, 0, 0, 1, 0
    };
    colorMatrix.set(sepia);
    
    // 3. 亮度调整
    colorMatrix.postConcat(new ColorMatrix(new float[]{
        1, 0, 0, 0, 50,    // 红色通道加50
        0, 1, 0, 0, 50,    // 绿色通道加50
        0, 0, 1, 0, 50,    // 蓝色通道加50
        0, 0, 0, 1, 0
    }));
    
    return new ColorMatrixColorFilter(colorMatrix);
}

3.2.3 特效与遮罩

// 模糊效果
private MaskFilter createBlurMaskFilter() {
    // 参数:模糊半径,模糊类型
    return new BlurMaskFilter(20, BlurMaskFilter.Blur.NORMAL);
    
    // 其他模糊类型:
    // BlurMaskFilter.Blur.SOLID - 内部模糊,外部正常绘制
    // BlurMaskFilter.Blur.OUTER - 只模糊外部
    // BlurMaskFilter.Blur.INNER - 只模糊内部
}

// 浮雕效果
private MaskFilter createEmbossMaskFilter() {
    // 参数:光源方向,环境光,镜面光,模糊半径
    float[] direction = {1, 1, 1}; // 光源方向向量
    float ambient = 0.4f;          // 环境光强度
    float specular = 0.6f;         // 镜面光强度
    float blurRadius = 3.0f;       // 模糊半径
    
    return new EmbossMaskFilter(direction, ambient, specular, blurRadius);
}

3.3 图层(Layer)与混合模式

3.3.1 图层保存与恢复

图层是Canvas绘图的重要概念,允许在独立区域进行绘制操作。

// 使用saveLayer创建透明图层
private void drawWithLayer(Canvas canvas) {
    int saveCount = canvas.saveLayer(
        0, 0, getWidth(), getHeight(), // 图层边界
        null,                          // 使用默认Paint
        Canvas.ALL_SAVE_FLAG           // 保存所有状态
    );
    
    try {
        // 在图层上绘制半透明矩形
        Paint layerPaint = new Paint();
        layerPaint.setColor(Color.argb(128, 255, 0, 0));
        canvas.drawRect(100, 100, 300, 300, layerPaint);
        
        // 绘制其他内容...
    } finally {
        // 恢复画布状态,将图层内容合成到主画布
        canvas.restoreToCount(saveCount);
    }
}

// 带Alpha通道的图层
private void drawWithAlphaLayer(Canvas canvas) {
    Paint alphaPaint = new Paint();
    alphaPaint.setAlpha(128); // 设置图层透明度
    
    int layerId = canvas.saveLayerAlpha(
        0, 0, getWidth(), getHeight(),
        128,                    // 图层整体透明度
        Canvas.ALL_SAVE_FLAG
    );
    
    // 在图层内绘制
    canvas.drawCircle(200, 200, 100, paint);
    
    canvas.restoreToCount(layerId);
}

3.3.2 PorterDuff混合模式详解

PorterDuff混合模式定义了源图像(当前绘制)和目标图像(已存在内容)如何混合。

// PorterDuff混合模式示例
private void demonstratePorterDuffModes(Canvas canvas) {
    Paint paint = new Paint();
    
    // 先绘制目标图像(蓝色矩形)
    paint.setColor(Color.BLUE);
    canvas.drawRect(100, 100, 300, 300, paint);
    
    // 设置混合模式
    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
    
    // 绘制源图像(红色圆形)
    paint.setColor(Color.RED);
    canvas.drawCircle(200, 200, 100, paint);
    
    // 清除混合模式
    paint.setXfermode(null);
}

// 常用混合模式实际应用
private void practicalPorterDuffExamples(Canvas canvas) {
    // 1. 实现圆形头像(使用DST_IN)
    Bitmap avatar = getAvatarBitmap();
    int centerX = getWidth() / 2;
    int centerY = getHeight() / 2;
    int radius = 100;
    
    // 先绘制圆形遮罩
    Paint maskPaint = new Paint();
    canvas.drawCircle(centerX, centerY, radius, maskPaint);
    
    // 设置DST_IN模式,只显示圆形区域内的头像
    maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
    canvas.drawBitmap(avatar, 
        centerX - avatar.getWidth() / 2,
        centerY - avatar.getHeight() / 2,
        maskPaint);
    maskPaint.setXfermode(null);
    
    // 2. 实现橡皮擦效果(使用CLEAR)
    Paint eraserPaint = new Paint();
    eraserPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
    eraserPaint.setStyle(Paint.Style.FILL);
    canvas.drawCircle(touchX, touchY, eraserRadius, eraserPaint);
    
    // 3. 实现叠加效果(使用MULTIPLY)
    Paint multiplyPaint = new Paint();
    multiplyPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
    multiplyPaint.setColor(Color.argb(128, 255, 0, 0)); // 半透明红色
    canvas.drawRect(rect, multiplyPaint);
}

3.4 矩阵(Matrix)变换高级技巧

自定义矩阵变换

// 使用Matrix进行复杂变换
private void applyCustomMatrix(Canvas canvas) {
    Matrix matrix = new Matrix();
    
    // 1. 组合变换
    matrix.postTranslate(100, 100);   // 平移
    matrix.postRotate(45);            // 旋转(以原点为中心)
    matrix.postScale(1.5f, 1.5f);     // 缩放
    
    // 2. 自定义矩阵
    float[] values = {
        1, 0, 100,    // X轴变换
        0, 1, 100,    // Y轴变换
        0, 0, 1       // 透视变换
    };
    matrix.setValues(values);
    
    // 3. 应用矩阵到Canvas
    canvas.save();
    canvas.concat(matrix);
    
    // 绘制内容
    canvas.drawBitmap(bitmap, 0, 0, paint);
    
    canvas.restore();
}

// 3D透视效果
private void apply3DPerspective(Canvas canvas) {
    Matrix matrix = new Matrix();
    
    // 创建3D透视变换
    float[] src = {
        0, 0,          // 左上
        bitmap.getWidth(), 0,         // 右上
        bitmap.getWidth(), bitmap.getHeight(), // 右下
        0, bitmap.getHeight()         // 左下
    };
    
    float[] dst = {
        50, 50,        // 左上(向右下偏移)
        bitmap.getWidth() - 50, 0,    // 右上(向左上偏移)
        bitmap.getWidth(), bitmap.getHeight(), // 右下不变
        0, bitmap.getHeight() - 50    // 左下(向右上偏移)
    };
    
    // 计算透视变换矩阵
    matrix.setPolyToPoly(src, 0, dst, 0, 4);
    
    canvas.save();
    canvas.concat(matrix);
    canvas.drawBitmap(bitmap, 0, 0, paint);
    canvas.restore();
}

多点触控变换实现

// 多点触控变换(缩放、旋转、平移)
public class MultiTouchTransformer {
    private Matrix currentMatrix = new Matrix();
    private Matrix savedMatrix = new Matrix();
    
    private static final int NONE = 0;
    private static final int DRAG = 1;
    private static final int ZOOM = 2;
    private int mode = NONE;
    
    private PointF startPoint = new PointF();
    private float startDistance = 1f;
    private float startAngle = 0f;
    
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                mode = DRAG;
                startPoint.set(event.getX(), event.getY());
                savedMatrix.set(currentMatrix);
                break;
                
            case MotionEvent.ACTION_POINTER_DOWN:
                mode = ZOOM;
                startDistance = getDistance(event);
                startAngle = getAngle(event);
                savedMatrix.set(currentMatrix);
                break;
                
            case MotionEvent.ACTION_MOVE:
                if (mode == DRAG) {
                    currentMatrix.set(savedMatrix);
                    float dx = event.getX() - startPoint.x;
                    float dy = event.getY() - startPoint.y;
                    currentMatrix.postTranslate(dx, dy);
                } else if (mode == ZOOM && event.getPointerCount() == 2) {
                    float newDistance = getDistance(event);
                    float newAngle = getAngle(event);
                    
                    if (newDistance > 10f) {
                        float scale = newDistance / startDistance;
                        currentMatrix.set(savedMatrix);
                        currentMatrix.postScale(scale, scale, 
                            getMidPoint(event).x, getMidPoint(event).y);
                    }
                    
                    float rotate = newAngle - startAngle;
                    currentMatrix.postRotate(rotate, 
                        getMidPoint(event).x, getMidPoint(event).y);
                }
                break;
                
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_POINTER_UP:
                mode = NONE;
                break;
        }
        return true;
    }
    
    private float getDistance(MotionEvent event) {
        float dx = event.getX(0) - event.getX(1);
        float dy = event.getY(0) - event.getY(1);
        return (float) Math.sqrt(dx * dx + dy * dy);
    }
    
    private PointF getMidPoint(MotionEvent event) {
        return new PointF(
            (event.getX(0) + event.getX(1)) / 2,
            (event.getY(0) + event.getY(1)) / 2
        );
    }
    
    private float getAngle(MotionEvent event) {
        double delta_x = event.getX(0) - event.getX(1);
        double delta_y = event.getY(0) - event.getY(1);
        return (float) Math.toDegrees(Math.atan2(delta_y, delta_x));
    }
    
    public Matrix getCurrentMatrix() {
        return currentMatrix;
    }
}

四、Canvas性能优化

4.1 绘图性能瓶颈分析

过度绘制检测与优化

过度绘制是指同一像素被多次绘制的现象,会严重影响性能。

// 检测过度绘制的方法
public class DrawingOptimizationView extends View {
    private Paint debugPaint;
    private boolean showOverdraw = false;
    
    public DrawingOptimizationView(Context context) {
        super(context);
        init();
    }
    
    private void init() {
        // 开启过度绘制调试
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            // 红色:绘制了4次以上
            // 粉色:绘制了3次
            // 绿色:绘制了2次
            // 蓝色:绘制了1次
            // 没有颜色:没有绘制
            setLayerType(LAYER_TYPE_HARDWARE, null);
        }
        
        debugPaint = new Paint();
        debugPaint.setColor(Color.RED);
        debugPaint.setStyle(Paint.Style.FILL);
        debugPaint.setAlpha(50); // 半透明
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        
        if (showOverdraw) {
            // 使用半透明绘制来模拟过度绘制
            for (int i = 0; i < 5; i++) {
                canvas.drawRect(100 + i * 10, 100 + i * 10, 
                    300 + i * 10, 300 + i * 10, debugPaint);
            }
        }
        
        // 优化后的绘制代码
        optimizedDrawing(canvas);
    }
    
    private void optimizedDrawing(Canvas canvas) {
        // 1. 避免绘制不可见区域
        Rect visibleRect = new Rect();
        getLocalVisibleRect(visibleRect);
        
        // 2. 只绘制可见部分
        if (needDrawContent(visibleRect)) {
            drawContent(canvas, visibleRect);
        }
    }
    
    private boolean needDrawContent(Rect visibleRect) {
        // 检查内容是否在可见区域内
        return visibleRect.intersect(contentRect);
    }
}

内存占用分析

Canvas绘图中的内存问题主要来自Bitmap和频繁的对象创建。

// 内存优化示例
public class MemoryOptimizedView extends View {
    private Paint paint;
    private Bitmap cachedBitmap; // 缓存绘制结果
    private boolean isDirty = true; // 脏标记
    
    public MemoryOptimizedView(Context context) {
        super(context);
        init();
    }
    
    private void init() {
        // 重用Paint对象
        paint = new Paint();
        paint.setAntiAlias(true);
        
        // 设置合适的Bitmap配置
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inPreferredConfig = Bitmap.Config.ARGB_8888; // 高质量
        // options.inPreferredConfig = Bitmap.Config.RGB_565; // 低内存
        
        // 根据View大小调整Bitmap采样率
        options.inSampleSize = calculateSampleSize();
    }
    
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        
        // 释放旧的Bitmap
        if (cachedBitmap != null) {
            cachedBitmap.recycle();
            cachedBitmap = null;
        }
        
        // 创建新的缓存Bitmap
        if (w > 0 && h > 0) {
            cachedBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
            isDirty = true;
        }
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        
        // 如果内容已改变,重新绘制到缓存Bitmap
        if (isDirty && cachedBitmap != null) {
            Canvas cacheCanvas = new Canvas(cachedBitmap);
            drawToCache(cacheCanvas);
            isDirty = false;
        }
        
        // 从缓存Bitmap绘制到屏幕
        if (cachedBitmap != null && !cachedBitmap.isRecycled()) {
            canvas.drawBitmap(cachedBitmap, 0, 0, paint);
        }
    }
    
    private void drawToCache(Canvas canvas) {
        // 复杂的绘制操作
        canvas.drawColor(Color.WHITE);
        // ... 其他绘制操作
    }
    
    public void markDirty() {
        isDirty = true;
        invalidate();
    }
    
    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        // 释放资源
        if (cachedBitmap != null) {
            cachedBitmap.recycle();
            cachedBitmap = null;
        }
    }
}

4.2 高效绘图策略

脏矩形更新技术

只更新需要重绘的区域,可以显著提高性能。

// 脏矩形更新实现
public class DirtyRectView extends View {
    private Rect dirtyRect = new Rect();
    private boolean fullRedraw = true;
    
    @Override
    public void invalidate() {
        fullRedraw = true;
        super.invalidate();
    }
    
    @Override
    public void invalidate(Rect dirty) {
        if (fullRedraw) {
            super.invalidate();
        } else {
            // 合并脏矩形
            dirtyRect.union(dirty);
            super.invalidate(dirtyRect);
        }
    }
    
    @Override
    public void invalidate(int l, int t, int r, int b) {
        if (fullRedraw) {
            super.invalidate();
        } else {
            dirtyRect.union(l, t, r, b);
            super.invalidate(dirtyRect);
        }
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        if (fullRedraw) {
            // 完整重绘
            drawFull(canvas);
            fullRedraw = false;
            dirtyRect.setEmpty();
        } else if (!dirtyRect.isEmpty()) {
            // 部分重绘
            canvas.save();
            canvas.clipRect(dirtyRect);
            drawPartial(canvas, dirtyRect);
            canvas.restore();
            dirtyRect.setEmpty();
        }
    }
    
    private void drawFull(Canvas canvas) {
        // 完整绘制逻辑
        canvas.drawColor(Color.WHITE);
        // ... 绘制所有内容
    }
    
    private void drawPartial(Canvas canvas, Rect dirtyRect) {
        // 只绘制脏矩形区域内的内容
        // 需要实现智能的脏区域绘制逻辑
        Iterator<DrawableItem> iterator = items.iterator();
        while (iterator.hasNext()) {
            DrawableItem item = iterator.next();
            if (Rect.intersects(dirtyRect, item.getBounds())) {
                item.draw(canvas);
            }
        }
    }
}

离屏缓冲优化

// 双重缓冲技术
public class DoubleBufferedView extends View {
    private Bitmap bufferBitmap;
    private Canvas bufferCanvas;
    private final Object bufferLock = new Object();
    
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        
        synchronized (bufferLock) {
            if (bufferBitmap != null) {
                bufferBitmap.recycle();
            }
            
            if (w > 0 && h > 0) {
                bufferBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
                bufferCanvas = new Canvas(bufferBitmap);
            }
        }
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        
        synchronized (bufferLock) {
            if (bufferBitmap != null && !bufferBitmap.isRecycled()) {
                // 先绘制到缓冲Canvas
                drawToBuffer(bufferCanvas);
                
                // 然后将缓冲Bitmap绘制到屏幕Canvas
                canvas.drawBitmap(bufferBitmap, 0, 0, null);
            }
        }
    }
    
    private void drawToBuffer(Canvas canvas) {
        long startTime = System.currentTimeMillis();
        
        // 复杂的绘制操作
        canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
        
        // 绘制背景
        Paint bgPaint = new Paint();
        bgPaint.setShader(new LinearGradient(0, 0, getWidth(), getHeight(),
            Color.WHITE, Color.LTGRAY, Shader.TileMode.CLAMP));
        canvas.drawRect(0, 0, getWidth(), getHeight(), bgPaint);
        
        // 绘制其他内容...
        
        long drawTime = System.currentTimeMillis() - startTime;
        if (drawTime > 16) { // 超过16ms,警告可能掉帧
            Log.w("DoubleBufferedView", "Draw time too long: " + drawTime + "ms");
        }
    }
    
    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        
        synchronized (bufferLock) {
            if (bufferBitmap != null) {
                bufferBitmap.recycle();
                bufferBitmap = null;
                bufferCanvas = null;
            }
        }
    }
}

4.3 大图处理与分块绘制

// 大图分块绘制
public class LargeImageTileView extends View {
    private Bitmap largeBitmap;
    private Rect viewport = new Rect();
    private List<BitmapTile> visibleTiles = new ArrayList<>();
    
    class BitmapTile {
        Rect srcRect;
        Rect dstRect;
        Bitmap tileBitmap;
        
        BitmapTile(Rect src, Rect dst, Bitmap bitmap) {
            this.srcRect = src;
            this.dstRect = dst;
            this.tileBitmap = bitmap;
        }
    }
    
    public void setLargeBitmap(Bitmap bitmap) {
        this.largeBitmap = bitmap;
        calculateVisibleTiles();
        invalidate();
    }
    
    private void calculateVisibleTiles() {
        visibleTiles.clear();
        
        if (largeBitmap == null) return;
        
        int tileSize = 512; // 瓦片大小
        int bitmapWidth = largeBitmap.getWidth();
        int bitmapHeight = largeBitmap.getHeight();
        
        // 计算当前视口对应的位图区域
        Rect bitmapVisibleRect = viewportToBitmapRect(viewport);
        
        // 计算需要加载的瓦片
        int startX = (bitmapVisibleRect.left / tileSize) * tileSize;
        int startY = (bitmapVisibleRect.top / tileSize) * tileSize;
        
        for (int y = startY; y < bitmapVisibleRect.bottom; y += tileSize) {
            for (int x = startX; x < bitmapVisibleRect.right; x += tileSize) {
                Rect srcRect = new Rect(
                    x, y,
                    Math.min(x + tileSize, bitmapWidth),
                    Math.min(y + tileSize, bitmapHeight)
                );
                
                Rect dstRect = bitmapToViewportRect(srcRect);
                
                // 异步加载瓦片
                loadTileAsync(srcRect, dstRect);
            }
        }
    }
    
    private Rect viewportToBitmapRect(Rect viewport) {
        // 将视口坐标转换为位图坐标
        // 这里需要根据缩放比例和偏移量计算
        float scale = getCurrentScale();
        float offsetX = getOffsetX();
        float offsetY = getOffsetY();
        
        return new Rect(
            (int) ((viewport.left - offsetX) / scale),
            (int) ((viewport.top - offsetY) / scale),
            (int) ((viewport.right - offsetX) / scale),
            (int) ((viewport.bottom - offsetY) / scale)
        );
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        
        // 绘制可见的瓦片
        for (BitmapTile tile : visibleTiles) {
            if (tile.tileBitmap != null && !tile.tileBitmap.isRecycled()) {
                canvas.drawBitmap(tile.tileBitmap, tile.srcRect, tile.dstRect, null);
            } else {
                // 绘制占位符
                Paint placeholderPaint = new Paint();
                placeholderPaint.setColor(Color.LTGRAY);
                canvas.drawRect(tile.dstRect, placeholderPaint);
            }
        }
        
        // 绘制加载中的提示
        drawLoadingIndicators(canvas);
    }
    
    private void loadTileAsync(final Rect srcRect, final Rect dstRect) {
        new AsyncTask<Void, Void, Bitmap>() {
            @Override
            protected Bitmap doInBackground(Void... params) {
                // 从大图中提取瓦片
                return Bitmap.createBitmap(
                    largeBitmap,
                    srcRect.left, srcRect.top,
                    srcRect.width(), srcRect.height()
                );
            }
            
            @Override
            protected void onPostExecute(Bitmap tileBitmap) {
                visibleTiles.add(new BitmapTile(
                    new Rect(0, 0, tileBitmap.getWidth(), tileBitmap.getHeight()),
                    dstRect,
                    tileBitmap
                ));
                invalidate(dstRect);
            }
        }.execute();
    }
}

五、Canvas特效实现

5.1 2D图形特效

5.1.1 粒子系统实现

// 完整粒子系统实现
public class ParticleSystem {
    private List<Particle> particles = new ArrayList<>();
    private Random random = new Random();
    private Paint particlePaint = new Paint();
    
    class Particle {
        float x, y;           // 位置
        float vx, vy;         // 速度
        float ax, ay;         // 加速度
        float radius;         // 半径
        int color;            // 颜色
        float life;           // 生命周期 (0-1)
        float decay;          // 衰减速度
        float rotation;       // 旋转角度
        float rotationSpeed;  // 旋转速度
        
        void update(float dt) {
            // 更新速度
            vx += ax * dt;
            vy += ay * dt;
            
            // 更新位置
            x += vx * dt;
            y += vy * dt;
            
            // 更新生命周期
            life -= decay * dt;
            
            // 更新旋转
            rotation += rotationSpeed * dt;
            
            // 边界检测
            if (x < 0 || x > screenWidth) vx = -vx;
            if (y < 0 || y > screenHeight) vy = -vy;
        }
        
        boolean isAlive() {
            return life > 0;
        }
    }
    
    public void createExplosion(float centerX, float centerY, int count) {
        for (int i = 0; i < count; i++) {
            Particle p = new Particle();
            p.x = centerX;
            p.y = centerY;
            
            // 随机方向
            float angle = random.nextFloat() * (float) Math.PI * 2;
            float speed = 100 + random.nextFloat() * 200;
            p.vx = (float) Math.cos(angle) * speed;
            p.vy = (float) Math.sin(angle) * speed;
            
            // 重力
            p.ay = 300;
            
            // 随机大小和颜色
            p.radius = 2 + random.nextFloat() * 6;
            p.color = Color.argb(255,
                255, // 红色
                100 + random.nextInt(155), // 绿色
                random.nextInt(100)        // 蓝色
            );
            
            p.life = 1.0f;
            p.decay = 0.5f + random.nextFloat() * 1.0f;
            p.rotation = random.nextFloat() * 360;
            p.rotationSpeed = (random.nextFloat() - 0.5f) * 360;
            
            particles.add(p);
        }
    }
    
    public void createFirework(float x, float y) {
        int mainColor = getRandomColor();
        
        // 创建主粒子
        Particle main = new Particle();
        main.x = x;
        main.y = y;
        main.vy = -500; // 向上
        main.radius = 8;
        main.color = mainColor;
        main.life = 2.0f;
        main.decay = 0.3f;
        particles.add(main);
        
        // 主粒子爆炸
        new Handler().postDelayed(() -> {
            createExplosion(main.x, main.y, 50);
        }, 800);
    }
    
    public void update(float dt) {
        Iterator<Particle> iterator = particles.iterator();
        while (iterator.hasNext()) {
            Particle p = iterator.next();
            p.update(dt);
            if (!p.isAlive()) {
                iterator.remove();
            }
        }
    }
    
    public void draw(Canvas canvas) {
        for (Particle p : particles) {
            particlePaint.setColor(p.color);
            particlePaint.setAlpha((int) (p.life * 255));
            
            canvas.save();
            canvas.translate(p.x, p.y);
            canvas.rotate(p.rotation);
            
            // 绘制粒子(可以换成其他形状)
            canvas.drawCircle(0, 0, p.radius, particlePaint);
            
            // 绘制拖尾效果
            if (p.life > 0.3f) {
                particlePaint.setAlpha((int) (p.life * 128));
                for (int i = 1; i <= 3; i++) {
                    canvas.drawCircle(-p.vx * 0.02f * i, -p.vy * 0.02f * i,
                        p.radius * (1 - i * 0.2f), particlePaint);
                }
            }
            
            canvas.restore();
        }
    }
    
    private int getRandomColor() {
        int[] colors = {
            Color.RED, Color.YELLOW, Color.GREEN,
            Color.BLUE, Color.MAGENTA, Color.CYAN
        };
        return colors[random.nextInt(colors.length)];
    }
}

5.1.2 动态波形与流体效果

// 波形效果实现
public class WaveEffectView extends View {
    private float[] wavePoints;
    private Paint wavePaint = new Paint();
    private Path wavePath = new Path();
    private float waveSpeed = 0.05f;
    private float waveAmplitude = 50;
    private float waveFrequency = 0.02f;
    private float phase = 0;
    
    public WaveEffectView(Context context) {
        super(context);
        init();
    }
    
    private void init() {
        wavePaint.setColor(Color.BLUE);
        wavePaint.setStyle(Paint.Style.FILL);
        wavePaint.setAntiAlias(true);
        
        wavePoints = new float[getWidth()];
    }
    
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        wavePoints = new float[w];
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        
        updateWave();
        drawWave(canvas);
        
        // 持续动画
        phase += waveSpeed;
        invalidate();
    }
    
    private void updateWave() {
        int width = getWidth();
        int height = getHeight();
        
        // 计算波形点
        for (int i = 0; i < width; i++) {
            // 基础正弦波
            float baseWave = (float) Math.sin(i * waveFrequency + phase) * waveAmplitude;
            
            // 添加次级波
            float secondaryWave = (float) Math.sin(i * waveFrequency * 2 + phase * 1.7f) * 
                waveAmplitude * 0.3f;
            
            // 添加噪声
            float noise = (float) (Math.random() - 0.5) * 10;
            
            wavePoints[i] = height / 2f + baseWave + secondaryWave + noise;
        }
    }
    
    private void drawWave(Canvas canvas) {
        wavePath.reset();
        
        // 移动到起点
        wavePath.moveTo(0, wavePoints[0]);
        
        // 使用二次贝塞尔曲线平滑连接点
        for (int i = 1; i < wavePoints.length - 1; i++) {
            float x = i;
            float y = wavePoints[i];
            
            // 计算控制点
            float prevX = i - 1;
            float prevY = wavePoints[i - 1];
            float nextX = i + 1;
            float nextY = wavePoints[i + 1];
            
            float ctrlX1 = (prevX + x) / 2;
            float ctrlY1 = (prevY + y) / 2;
            float ctrlX2 = (x + nextX) / 2;
            float ctrlY2 = (y + nextY) / 2;
            
            wavePath.cubicTo(ctrlX1, ctrlY1, ctrlX2, ctrlY2, x, y);
        }
        
        // 闭合路径形成填充区域
        wavePath.lineTo(getWidth(), getHeight());
        wavePath.lineTo(0, getHeight());
        wavePath.close();
        
        // 创建渐变着色器
        Shader shader = new LinearGradient(0, getHeight() / 2f, 0, getHeight(),
            Color.argb(200, 0, 100, 255),
            Color.argb(100, 0, 200, 255),
            Shader.TileMode.CLAMP);
        wavePaint.setShader(shader);
        
        // 绘制波形
        canvas.drawPath(wavePath, wavePaint);
        
        // 绘制波浪线
        wavePaint.setShader(null);
        wavePaint.setStyle(Paint.Style.STROKE);
        wavePaint.setStrokeWidth(3);
        wavePaint.setColor(Color.WHITE);
        canvas.drawPath(wavePath, wavePaint);
        
        // 恢复填充模式
        wavePaint.setStyle(Paint.Style.FILL);
    }
}

// 水波纹点击效果
public class RippleView extends View {
    private List<Ripple> ripples = new ArrayList<>();
    private Paint ripplePaint = new Paint();
    
    class Ripple {
        float x, y;
        float radius;
        float maxRadius;
        float alpha;
        long startTime;
        
        Ripple(float x, float y, float maxRadius) {
            this.x = x;
            this.y = y;
            this.radius = 0;
            this.maxRadius = maxRadius;
            this.alpha = 1.0f;
            this.startTime = System.currentTimeMillis();
        }
        
        void update() {
            long currentTime = System.currentTimeMillis();
            float elapsed = (currentTime - startTime) / 1000f;
            
            // 半径扩展
            radius = Math.min(elapsed * 300, maxRadius);
            
            // 透明度衰减
            alpha = 1.0f - (radius / maxRadius);
            
            // 添加阻尼效果
            if (radius > maxRadius * 0.7f) {
                alpha *= 0.5f;
            }
        }
        
        boolean isFinished() {
            return radius >= maxRadius;
        }
    }
    
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            ripples.add(new Ripple(event.getX(), event.getY(), 
                Math.max(getWidth(), getHeight())));
            invalidate();
            return true;
        }
        return super.onTouchEvent(event);
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        
        ripplePaint.setStyle(Paint.Style.STROKE);
        ripplePaint.setAntiAlias(true);
        
        Iterator<Ripple> iterator = ripples.iterator();
        while (iterator.hasNext()) {
            Ripple ripple = iterator.next();
            ripple.update();
            
            if (ripple.isFinished()) {
                iterator.remove();
                continue;
            }
            
            // 绘制多个同心圆增强效果
            for (int i = 0; i < 3; i++) {
                float currentRadius = ripple.radius - i * 20;
                if (currentRadius <= 0) continue;
                
                float currentAlpha = ripple.alpha * (1 - i * 0.3f);
                ripplePaint.setColor(Color.argb(
                    (int) (currentAlpha * 255), 0, 150, 255));
                ripplePaint.setStrokeWidth(3 - i);
                
                canvas.drawCircle(ripple.x, ripple.y, currentRadius, ripplePaint);
            }
        }
        
        if (!ripples.isEmpty()) {
            invalidate();
        }
    }
}

5.2 文本特效

// 渐变文字特效
public class GradientTextView extends View {
    private String text = "Canvas绘图";
    private Paint textPaint = new Paint();
    private LinearGradient gradient;
    private float gradientAngle = 0;
    
    public GradientTextView(Context context) {
        super(context);
        init();
    }
    
    private void init() {
        textPaint.setTextSize(100);
        textPaint.setStyle(Paint.Style.FILL);
        textPaint.setTypeface(Typeface.DEFAULT_BOLD);
        textPaint.setAntiAlias(true);
    }
    
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        updateGradient();
    }
    
    private void updateGradient() {
        // 根据角度计算渐变方向
        float angleRad = (float) Math.toRadians(gradientAngle);
        float cos = (float) Math.cos(angleRad);
        float sin = (float) Math.sin(angleRad);
        
        float startX = getWidth() / 2f - cos * getWidth() / 2f;
        float startY = getHeight() / 2f - sin * getHeight() / 2f;
        float endX = getWidth() / 2f + cos * getWidth() / 2f;
        float endY = getHeight() / 2f + sin * getHeight() / 2f;
        
        gradient = new LinearGradient(
            startX, startY, endX, endY,
            new int[]{Color.RED, Color.YELLOW, Color.GREEN, Color.BLUE, Color.MAGENTA},
            new float[]{0, 0.25f, 0.5f, 0.75f, 1},
            Shader.TileMode.CLAMP
        );
        
        textPaint.setShader(gradient);
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        
        // 绘制描边文字
        Paint strokePaint = new Paint(textPaint);
        strokePaint.setShader(null);
        strokePaint.setStyle(Paint.Style.STROKE);
        strokePaint.setStrokeWidth(5);
        strokePaint.setColor(Color.BLACK);
        
        // 计算文字居中位置
        float textWidth = textPaint.measureText(text);
        float x = (getWidth() - textWidth) / 2;
        float y = getHeight() / 2f + 
            (Math.abs(textPaint.ascent()) - textPaint.descent()) / 2;
        
        // 先绘制描边
        canvas.drawText(text, x, y, strokePaint);
        // 再绘制填充
        canvas.drawText(text, x, y, textPaint);
        
        // 添加文字阴影
        canvas.save();
        canvas.translate(5, 5);
        textPaint.setAlpha(100);
        canvas.drawText(text, x, y, textPaint);
        textPaint.setAlpha(255);
        canvas.restore();
        
        // 更新渐变角度
        gradientAngle = (gradientAngle + 1) % 360;
        updateGradient();
        invalidate();
    }
}

// 打字机效果
public class TypewriterTextView extends View {
    private String fullText = "Hello, Canvas绘图高级技巧!";
    private StringBuilder currentText = new StringBuilder();
    private Paint textPaint = new Paint();
    private Handler handler = new Handler();
    private int currentIndex = 0;
    private long delay = 100; // 每个字符的延迟
    
    public TypewriterTextView(Context context) {
        super(context);
        init();
        startTyping();
    }
    
    private void init() {
        textPaint.setTextSize(50);
        textPaint.setColor(Color.BLACK);
        textPaint.setAntiAlias(true);
    }
    
    private void startTyping() {
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                if (currentIndex < fullText.length()) {
                    currentText.append(fullText.charAt(currentIndex));
                    currentIndex++;
                    invalidate();
                    
                    // 随机延迟,模拟真实打字
                    long nextDelay = delay + (long) (Math.random() * 50);
                    handler.postDelayed(this, nextDelay);
                }
            }
        }, delay);
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        
        // 绘制已打出的文字
        canvas.drawText(currentText.toString(), 50, 100, textPaint);
        
        // 绘制光标
        if (currentIndex < fullText.length()) {
            float cursorX = 50 + textPaint.measureText(currentText.toString());
            long time = System.currentTimeMillis();
            if ((time / 500) % 2 == 0) {
                canvas.drawLine(cursorX, 80, cursorX, 120, textPaint);
            }
        }
    }
}

5.3 图像特效

5.3.1 实时滤镜效果

// 实时滤镜处理器
public class ImageFilterProcessor {
    
    // 应用滤镜到Bitmap
    public static Bitmap applyFilter(Bitmap source, FilterType filterType) {
        Bitmap result = source.copy(source.getConfig(), true);
        int width = result.getWidth();
        int height = result.getHeight();
        
        int[] pixels = new int[width * height];
        result.getPixels(pixels, 0, width, 0, 0, width, height);
        
        switch (filterType) {
            case GRAYSCALE:
                applyGrayscale(pixels);
                break;
            case SEPIA:
                applySepia(pixels);
                break;
            case INVERT:
                applyInvert(pixels);
                break;
            case BRIGHTNESS:
                applyBrightness(pixels, 50);
                break;
            case CONTRAST:
                applyContrast(pixels, 1.5f);
                break;
            case BLUR:
                // 使用卷积矩阵模糊
                pixels = applyConvolution(pixels, width, height, getBlurKernel());
                break;
            case SHARPEN:
                pixels = applyConvolution(pixels, width, height, getSharpenKernel());
                break;
            case EDGE_DETECT:
                pixels = applyConvolution(pixels, width, height, getEdgeDetectKernel());
                break;
        }
        
        result.setPixels(pixels, 0, width, 0, 0, width, height);
        return result;
    }
    
    private static void applyGrayscale(int[] pixels) {
        for (int i = 0; i < pixels.length; i++) {
            int pixel = pixels[i];
            
            int a = Color.alpha(pixel);
            int r = Color.red(pixel);
            int g = Color.green(pixel);
            int b = Color.blue(pixel);
            
            // 灰度公式
            int gray = (int) (0.299 * r + 0.587 * g + 0.114 * b);
            
            pixels[i] = Color.argb(a, gray, gray, gray);
        }
    }
    
    private static void applySepia(int[] pixels) {
        for (int i = 0; i < pixels.length; i++) {
            int pixel = pixels[i];
            
            int a = Color.alpha(pixel);
            int r = Color.red(pixel);
            int g = Color.green(pixel);
            int b = Color.blue(pixel);
            
            // 怀旧色公式
            int tr = (int) (0.393 * r + 0.769 * g + 0.189 * b);
            int tg = (int) (0.349 * r + 0.686 * g + 0.168 * b);
            int tb = (int) (0.272 * r + 0.534 * g + 0.131 * b);
            
            r = Math.min(255, tr);
            g = Math.min(255, tg);
            b = Math.min(255, tb);
            
            pixels[i] = Color.argb(a, r, g, b);
        }
    }
    
    private static void applyInvert(int[] pixels) {
        for (int i = 0; i < pixels.length; i++) {
            int pixel = pixels[i];
            
            int a = Color.alpha(pixel);
            int r = 255 - Color.red(pixel);
            int g = 255 - Color.green(pixel);
            int b = 255 - Color.blue(pixel);
            
            pixels[i] = Color.argb(a, r, g, b);
        }
    }
    
    private static void applyBrightness(int[] pixels, int value) {
        for (int i = 0; i < pixels.length; i++) {
            int pixel = pixels[i];
            
            int a = Color.alpha(pixel);
            int r = clamp(Color.red(pixel) + value);
            int g = clamp(Color.green(pixel) + value);
            int b = clamp(Color.blue(pixel) + value);
            
            pixels[i] = Color.argb(a, r, g, b);
        }
    }
    
    private static void applyContrast(int[] pixels, float contrast) {
        float factor = (259 * (contrast + 255)) / (255 * (259 - contrast));
        
        for (int i = 0; i < pixels.length; i++) {
            int pixel = pixels[i];
            
            int a = Color.alpha(pixel);
            int r = clamp((int) (factor * (Color.red(pixel) - 128) + 128));
            int g = clamp((int) (factor * (Color.green(pixel) - 128) + 128));
            int b = clamp((int) (factor * (Color.blue(pixel) - 128) + 128));
            
            pixels[i] = Color.argb(a, r, g, b);
        }
    }
    
    // 卷积滤波
    private static int[] applyConvolution(int[] pixels, int width, int height, float[] kernel) {
        int[] result = new int[pixels.length];
        int kernelSize = (int) Math.sqrt(kernel.length);
        int kernelRadius = kernelSize / 2;
        
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                float r = 0, g = 0, b = 0;
                
                // 应用卷积核
                for (int ky = -kernelRadius; ky <= kernelRadius; ky++) {
                    for (int kx = -kernelRadius; kx <= kernelRadius; kx++) {
                        int pixelX = clamp(x + kx, 0, width - 1);
                        int pixelY = clamp(y + ky, 0, height - 1);
                        
                        int pixel = pixels[pixelY * width + pixelX];
                        float weight = kernel[(ky + kernelRadius) * kernelSize + (kx + kernelRadius)];
                        
                        r += Color.red(pixel) * weight;
                        g += Color.green(pixel) * weight;
                        b += Color.blue(pixel) * weight;
                    }
                }
                
                int a = Color.alpha(pixels[y * width + x]);
                result[y * width + x] = Color.argb(a, 
                    clamp((int) r), clamp((int) g), clamp((int) b));
            }
        }
        
        return result;
    }
    
    private static float[] getBlurKernel() {
        // 3x3 高斯模糊核
        return new float[] {
            1/16f, 2/16f, 1/16f,
            2/16f, 4/16f, 2/16f,
            1/16f, 2/16f, 1/16f
        };
    }
    
    private static float[] getSharpenKernel() {
        // 3x3 锐化核
        return new float[] {
            0, -1, 0,
            -1, 5, -1,
            0, -1, 0
        };
    }
    
    private static float[] getEdgeDetectKernel() {
        // Sobel边缘检测核
        return new float[] {
            -1, -1, -1,
            -1, 8, -1,
            -1, -1, -1
        };
    }
    
    private static int clamp(int value) {
        return Math.max(0, Math.min(255, value));
    }
    
    private static int clamp(int value, int min, int max) {
        return Math.max(min, Math.min(max, value));
    }
    
    enum FilterType {
        GRAYSCALE, SEPIA, INVERT, BRIGHTNESS, 
        CONTRAST, BLUR, SHARPEN, EDGE_DETECT
    }
}

六、Canvas动画与交互

6.1 自定义动画实现

// 物理动画系统
public class PhysicsAnimationView extends View {
    private ValueAnimator animator;
    private float animatedValue = 0;
    private Paint circlePaint = new Paint();
    private float circleX, circleY;
    private float circleRadius = 50;
    private float velocityY = 0;
    private float gravity = 980; // 像素/秒²
    private float damping = 0.8f; // 反弹阻尼
    private long lastUpdateTime;
    
    public PhysicsAnimationView(Context context) {
        super(context);
        init();
        startAnimation();
    }
    
    private void init() {
        circlePaint.setColor(Color.RED);
        circlePaint.setStyle(Paint.Style.FILL);
        circlePaint.setAntiAlias(true);
        
        circleX = getWidth() / 2f;
        circleY = circleRadius;
        
        lastUpdateTime = System.currentTimeMillis();
    }
    
    private void startAnimation() {
        animator = ValueAnimator.ofFloat(0, 1);
        animator.setDuration(Long.MAX_VALUE);
        animator.setInterpolator(null); // 不使用插值器,自己控制
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                updatePhysics();
                invalidate();
            }
        });
        animator.start();
    }
    
    private void updatePhysics() {
        long currentTime = System.currentTimeMillis();
        float deltaTime = (currentTime - lastUpdateTime) / 1000f; // 转换为秒
        lastUpdateTime = currentTime;
        
        // 应用重力
        velocityY += gravity * deltaTime;
        
        // 更新位置
        circleY += velocityY * deltaTime;
        
        // 边界碰撞检测
        if (circleY + circleRadius > getHeight()) {
            circleY = getHeight() - circleRadius;
            velocityY = -velocityY * damping; // 反弹
            
            // 如果速度太小,停止动画
            if (Math.abs(velocityY) < 50) {
                velocityY = 0;
                circleY = getHeight() - circleRadius;
            }
        }
        
        // 空气阻力
        velocityY *= 0.999f;
    }
    
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        circleX = w / 2f;
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        
        // 绘制地面
        Paint groundPaint = new Paint();
        groundPaint.setColor(Color.GRAY);
        canvas.drawRect(0, getHeight() - 10, getWidth(), getHeight(), groundPaint);
        
        // 绘制阴影
        Paint shadowPaint = new Paint();
        shadowPaint.setColor(Color.argb(100, 0, 0, 0));
        float shadowScale = 1 - (circleY / getHeight()) * 0.5f;
        canvas.drawOval(
            circleX - circleRadius * shadowScale,
            getHeight() - 10,
            circleX + circleRadius * shadowScale,
            getHeight() - 5,
            shadowPaint
        );
        
        // 绘制圆形
        canvas.drawCircle(circleX, circleY, circleRadius, circlePaint);
    }
    
    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (animator != null) {
            animator.cancel();
        }
    }
}

// 弹簧动画
public class SpringAnimationView extends View {
    private SpringAnimation springAnim;
    private float springValue = 0;
    private Paint paint = new Paint();
    
    public SpringAnimationView(Context context) {
        super(context);
        init();
        setupSpringAnimation();
    }
    
    private void init() {
        paint.setColor(Color.BLUE);
        paint.setStyle(Paint.Style.FILL);
        paint.setAntiAlias(true);
    }
    
    private void setupSpringAnimation() {
        // 创建弹簧动画
        springAnim = new SpringAnimation(this, "springValue", 0)
            .setSpring(new SpringForce()
                .setFinalPosition(1)
                .setDampingRatio(SpringForce.DAMPING_RATIO_HIGH_BOUNCY)
                .setStiffness(SpringForce.STIFFNESS_MEDIUM))
            .addUpdateListener((animation, value, velocity) -> {
                springValue = value;
                invalidate();
            });
        
        // 开始动画
        postDelayed(() -> springAnim.start(), 1000);
    }
    
    // 必须提供setter方法供SpringAnimation使用
    public void setSpringValue(float value) {
        this.springValue = value;
    }
    
    public float getSpringValue() {
        return springValue;
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        
        float centerX = getWidth() / 2f;
        float centerY = getHeight() / 2f;
        
        // 使用弹簧值控制大小和透明度
        float radius = 50 + springValue * 100;
        int alpha = (int) (100 + springValue * 155);
        
        paint.setAlpha(alpha);
        canvas.drawCircle(centerX, centerY, radius, paint);
        
        // 绘制弹簧轨迹
        if (springValue > 0) {
            paint.setStyle(Paint.Style.STROKE);
            paint.setStrokeWidth(2);
            paint.setAlpha(100);
            
            Path path = new Path();
            path.moveTo(centerX - radius, centerY);
            
            for (int i = 0; i < 10; i++) {
                float x = centerX - radius + i * (radius * 2 / 10);
                float y = centerY + (float) Math.sin(i * Math.PI) * 20 * springValue;
                path.lineTo(x, y);
            }
            
            canvas.drawPath(path, paint);
            paint.setStyle(Paint.Style.FILL);
        }
    }
}

6.2 触摸交互处理

// 高级绘图板实现
public class AdvancedDrawingView extends View {
    private Paint drawPaint = new Paint();
    private Paint previewPaint = new Paint();
    private Path currentPath = new Path();
    private List<DrawingPath> paths = new ArrayList<>();
    private float lastX, lastY;
    private boolean isDrawing = false;
    private Bitmap drawingBitmap; // 离屏缓冲
    private Canvas drawingCanvas;
    private int currentColor = Color.BLACK;
    private float currentStrokeWidth = 5;
    private BlurMaskFilter blurFilter;
    
    class DrawingPath {
        Path path;
        Paint paint;
        
        DrawingPath(Path path, Paint paint) {
            this.path = new Path(path);
            this.paint = new Paint(paint);
        }
        
        void draw(Canvas canvas) {
            canvas.drawPath(path, paint);
        }
    }
    
    public AdvancedDrawingView(Context context) {
        super(context);
        init();
    }
    
    private void init() {
        // 设置绘制画笔
        drawPaint.setColor(currentColor);
        drawPaint.setStyle(Paint.Style.STROKE);
        drawPaint.setStrokeWidth(currentStrokeWidth);
        drawPaint.setStrokeCap(Paint.Cap.ROUND);
        drawPaint.setStrokeJoin(Paint.Join.ROUND);
        drawPaint.setAntiAlias(true);
        
        // 设置预览画笔(半透明)
        previewPaint.set(drawPaint);
        previewPaint.setAlpha(128);
        
        // 创建模糊滤镜
        blurFilter = new BlurMaskFilter(10, BlurMaskFilter.Blur.NORMAL);
    }
    
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        
        // 创建离屏缓冲
        drawingBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        drawingCanvas = new Canvas(drawingBitmap);
        
        // 重绘所有路径到缓冲
        redrawAllToBuffer();
    }
    
    private void redrawAllToBuffer() {
        if (drawingCanvas == null) return;
        
        drawingCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
        for (DrawingPath path : paths) {
            path.draw(drawingCanvas);
        }
    }
    
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();
        
        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                startDrawing(x, y);
                return true;
                
            case MotionEvent.ACTION_MOVE:
                if (isDrawing) {
                    continueDrawing(x, y, event);
                }
                break;
                
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                if (isDrawing) {
                    finishDrawing();
                }
                break;
                
            case MotionEvent.ACTION_POINTER_DOWN:
                // 双指操作:调整笔刷大小
                if (event.getPointerCount() == 2) {
                    adjustBrushSize(event);
                }
                break;
        }
        
        return true;
    }
    
    private void startDrawing(float x, float y) {
        isDrawing = true;
        lastX = x;
        lastY = y;
        
        currentPath.reset();
        currentPath.moveTo(x, y);
    }
    
    private void continueDrawing(float x, float y, MotionEvent event) {
        // 使用历史点获得更平滑的曲线
        int historySize = event.getHistorySize();
        for (int i = 0; i < historySize; i++) {
            float historicalX = event.getHistoricalX(i);
            float historicalY = event.getHistoricalY(i);
            
            // 使用二次贝塞尔曲线平滑
            float midX = (lastX + historicalX) / 2;
            float midY = (lastY + historicalY) / 2;
            
            currentPath.quadTo(lastX, lastY, midX, midY);
            
            lastX = historicalX;
            lastY = historicalY;
        }
        
        // 添加最后一个点
        currentPath.lineTo(x, y);
        
        invalidate();
    }
    
    private void finishDrawing() {
        isDrawing = false;
        
        // 保存当前路径
        DrawingPath savedPath = new DrawingPath(currentPath, drawPaint);
        paths.add(savedPath);
        
        // 绘制到离屏缓冲
        savedPath.draw(drawingCanvas);
        
        currentPath.reset();
        invalidate();
    }
    
    private void adjustBrushSize(MotionEvent event) {
        float distance = getFingerDistance(event);
        float previousDistance = getPreviousFingerDistance(event);
        
        if (previousDistance > 0) {
            float scale = distance / previousDistance;
            currentStrokeWidth *= scale;
            currentStrokeWidth = Math.max(1, Math.min(100, currentStrokeWidth));
            
            drawPaint.setStrokeWidth(currentStrokeWidth);
            previewPaint.setStrokeWidth(currentStrokeWidth);
            
            invalidate();
        }
    }
    
    private float getFingerDistance(MotionEvent event) {
        float dx = event.getX(0) - event.getX(1);
        float dy = event.getY(0) - event.getY(1);
        return (float) Math.sqrt(dx * dx + dy * dy);
    }
    
    private float getPreviousFingerDistance(MotionEvent event) {
        if (event.getHistorySize() > 0) {
            float dx = event.getHistoricalX(0, 0) - event.getHistoricalX(1, 0);
            float dy = event.getHistoricalY(0, 0) - event.getHistoricalY(1, 0);
            return (float) Math.sqrt(dx * dx + dy * dy);
        }
        return -1;
    }
    
    public void setBrushColor(int color) {
        currentColor = color;
        drawPaint.setColor(color);
        previewPaint.setColor(color);
    }
    
    public void setBrushMode(BrushMode mode) {
        switch (mode) {
            case NORMAL:
                drawPaint.setMaskFilter(null);
                drawPaint.setXfermode(null);
                break;
            case BLUR:
                drawPaint.setMaskFilter(blurFilter);
                break;
            case ERASER:
                drawPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
                break;
        }
        previewPaint.set(drawPaint);
        previewPaint.setAlpha(128);
    }
    
    public void undo() {
        if (!paths.isEmpty()) {
            paths.remove(paths.size() - 1);
            redrawAllToBuffer();
            invalidate();
        }
    }
    
    public void clear() {
        paths.clear();
        redrawAllToBuffer();
        invalidate();
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        
        // 绘制背景
        canvas.drawColor(Color.WHITE);
        
        // 绘制网格背景(可选)
        drawGridBackground(canvas);
        
        // 绘制已保存的路径(从离屏缓冲)
        if (drawingBitmap != null) {
            canvas.drawBitmap(drawingBitmap, 0, 0, null);
        }
        
        // 绘制当前路径(预览)
        if (isDrawing) {
            canvas.drawPath(currentPath, previewPaint);
        }
        
        // 绘制笔刷预览
        drawBrushPreview(canvas);
    }
    
    private void drawGridBackground(Canvas canvas) {
        Paint gridPaint = new Paint();
        gridPaint.setColor(Color.LTGRAY);
        gridPaint.setStrokeWidth(1);
        gridPaint.setAlpha(50);
        
        int gridSize = 20;
        int width = getWidth();
        int height = getHeight();
        
        // 绘制垂直线
        for (int x = 0; x < width; x += gridSize) {
            canvas.drawLine(x, 0, x, height, gridPaint);
        }
        
        // 绘制水平线
        for (int y = 0; y < height; y += gridSize) {
            canvas.drawLine(0, y, width, y, gridPaint);
        }
    }
    
    private void drawBrushPreview(Canvas canvas) {
        float previewX = getWidth() - 100;
        float previewY = 100;
        
        Paint previewBgPaint = new Paint();
        previewBgPaint.setColor(Color.argb(200, 255, 255, 255));
        canvas.drawCircle(previewX, previewY, currentStrokeWidth + 20, previewBgPaint);
        
        canvas.drawCircle(previewX, previewY, currentStrokeWidth, drawPaint);
    }
    
    enum BrushMode {
        NORMAL, BLUR, ERASER
    }
}

七、Canvas与现代Android图形技术结合

7.1 Canvas与OpenGL ES协同

// 在SurfaceView中混合使用Canvas和OpenGL ES
public class HybridSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
    private SurfaceHolder holder;
    private RenderThread renderThread;
    private EGLHelper eglHelper;
    private boolean isRunning = false;
    
    // Canvas绘制内容
    private Bitmap canvasBitmap;
    private Paint canvasPaint = new Paint();
    
    public HybridSurfaceView(Context context) {
        super(context);
        init();
    }
    
    private void init() {
        holder = getHolder();
        holder.addCallback(this);
        
        canvasPaint.setColor(Color.RED);
        canvasPaint.setStyle(Paint.Style.FILL);
        canvasPaint.setTextSize(50);
    }
    
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        // 初始化EGL环境
        eglHelper = new EGLHelper();
        eglHelper.init(holder.getSurface());
        
        // 创建渲染线程
        isRunning = true;
        renderThread = new RenderThread();
        renderThread.start();
    }
    
    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        // 更新Canvas Bitmap大小
        if (canvasBitmap != null) {
            canvasBitmap.recycle();
        }
        canvasBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        
        // 通知渲染线程尺寸变化
        synchronized (this) {
            notify();
        }
    }
    
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        isRunning = false;
        try {
            renderThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        eglHelper.cleanup();
        if (canvasBitmap != null) {
            canvasBitmap.recycle();
        }
    }
    
    private class RenderThread extends Thread {
        @Override
        public void run() {
            while (isRunning) {
                renderFrame();
                
                // 控制帧率
                try {
                    Thread.sleep(16); // ~60 FPS
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        
        private void renderFrame() {
            // 绑定OpenGL ES上下文
            eglHelper.makeCurrent();
            
            // 清除颜色缓冲区
            GLES20.glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
            GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
            
            // 1. 使用OpenGL ES绘制3D内容
            drawOpenGLContent();
            
            // 2. 使用Canvas绘制2D UI
            drawCanvasContent();
            
            // 交换缓冲区
            eglHelper.swapBuffers();
        }
        
        private void drawOpenGLContent() {
            // OpenGL ES绘制代码...
            // 例如:绘制一个旋转的立方体
            
            // 设置视图和投影矩阵
            Matrix.setLookAtM(viewMatrix, 0, 
                0, 0, -5,  // 相机位置
                0, 0, 0,   // 观察点
                0, 1, 0);  // 上方向
            
            float ratio = (float) getWidth() / getHeight();
            Matrix.frustumM(projectionMatrix, 0, 
                -ratio, ratio, -1, 1, 3, 7);
            
            // 旋转立方体
            long time = System.currentTimeMillis() % 10000;
            float angle = (360.0f / 10000.0f) * ((int) time);
            Matrix.setRotateM(modelMatrix, 0, angle, 0, 1, 0);
            
            // 组合MVP矩阵
            Matrix.multiplyMM(mvpMatrix, 0, viewMatrix, 0, modelMatrix, 0);
            Matrix.multiplyMM(mvpMatrix, 0, projectionMatrix, 0, mvpMatrix, 0);
            
            // 绘制立方体...
        }
        
        private void drawCanvasContent() {
            // 在Canvas Bitmap上绘制
            Canvas canvas = new Canvas(canvasBitmap);
            canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
            
            // 绘制2D UI元素
            canvas.drawText("FPS: " + calculateFPS(), 50, 100, canvasPaint);
            canvas.drawCircle(100, 200, 50, canvasPaint);
            
            // 将Canvas内容作为纹理上传到OpenGL
            uploadBitmapAsTexture(canvasBitmap);
            
            // 使用OpenGL绘制这个纹理
            drawTextureQuad();
        }
        
        private void uploadBitmapAsTexture(Bitmap bitmap) {
            int[] textures = new int[1];
            GLES20.glGenTextures(1, textures, 0);
            
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
            GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, 
                GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
            GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, 
                GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
            
            GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
        }
    }
    
    private float calculateFPS() {
        // 计算帧率逻辑...
        return 60.0f;
    }
}

// EGL辅助类
class EGLHelper {
    private EGLDisplay eglDisplay;
    private EGLContext eglContext;
    private EGLSurface eglSurface;
    
    public void init(Surface surface) {
        // 初始化EGL显示
        eglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
        int[] version = new int[2];
        EGL14.eglInitialize(eglDisplay, version, 0, version, 1);
        
        // 选择配置
        int[] configAttribs = {
            EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
            EGL14.EGL_RED_SIZE, 8,
            EGL14.EGL_GREEN_SIZE, 8,
            EGL14.EGL_BLUE_SIZE, 8,
            EGL14.EGL_ALPHA_SIZE, 8,
            EGL14.EGL_DEPTH_SIZE, 16,
            EGL14.EGL_STENCIL_SIZE, 0,
            EGL14.EGL_NONE
        };
        
        EGLConfig[] configs = new EGLConfig[1];
        int[] numConfigs = new int[1];
        EGL14.eglChooseConfig(eglDisplay, configAttribs, 0, 
            configs, 0, configs.length, numConfigs, 0);
        
        // 创建上下文
        int[] contextAttribs = {
            EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
            EGL14.EGL_NONE
        };
        eglContext = EGL14.eglCreateContext(eglDisplay, configs[0], 
            EGL14.EGL_NO_CONTEXT, contextAttribs, 0);
        
        // 创建表面
        int[] surfaceAttribs = {EGL14.EGL_NONE};
        eglSurface = EGL14.eglCreateWindowSurface(eglDisplay, configs[0], 
            surface, surfaceAttribs, 0);
    }
    
    public void makeCurrent() {
        EGL14.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
    }
    
    public void swapBuffers() {
        EGL14.eglSwapBuffers(eglDisplay, eglSurface);
    }
    
    public void cleanup() {
        EGL14.eglDestroySurface(eglDisplay, eglSurface);
        EGL14.eglDestroyContext(eglDisplay, eglContext);
        EGL14.eglTerminate(eglDisplay);
    }
}

7.2 Jetpack Compose中的Canvas

// Compose中的Canvas使用
@Composable
fun AdvancedComposeCanvas() {
    var rotation by remember { mutableStateOf(0f) }
    var scale by remember { mutableStateOf(1f) }
    var offset by remember { mutableStateOf(Offset.Zero) }
    
    // 自动旋转动画
    LaunchedEffect(Unit) {
        while (true) {
            rotation += 1f
            delay(16) // 60 FPS
        }
    }
    
    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(Color.White)
            .pointerInput(Unit) {
                detectTransformGestures(
                    onGesture = { centroid, pan, gestureScale, gestureRotate ->
                        scale *= gestureScale
                        rotation += gestureRotate
                        offset += pan
                    }
                )
            }
    ) {
        Canvas(
            modifier = Modifier
                .fillMaxSize()
                .graphicsLayer {
                    rotationZ = rotation
                    scaleX = scale
                    scaleY = scale
                    translationX = offset.x
                    translationY = offset.y
                }
        ) {
            val canvasWidth = size.width
            val canvasHeight = size.height
            
            // 1. 绘制渐变背景
            drawRect(
                brush = Brush.verticalGradient(
                    colors = listOf(Color.Cyan, Color.Blue),
                    startY = 0f,
                    endY = canvasHeight
                ),
                size = size
            )
            
            // 2. 绘制粒子系统
            drawParticles(canvasWidth, canvasHeight)
            
            // 3. 绘制复杂路径
            val path = Path().apply {
                moveTo(canvasWidth * 0.2f, canvasHeight * 0.5f)
                cubicTo(
                    canvasWidth * 0.4f, canvasHeight * 0.2f,
                    canvasWidth * 0.6f, canvasHeight * 0.8f,
                    canvasWidth * 0.8f, canvasHeight * 0.5f
                )
            }
            
            drawPath(
                path = path,
                color = Color.Red,
                style = Stroke(width = 5f)
            )
            
            // 4. 绘制文字沿路径
            drawContext.canvas.nativeCanvas.drawTextOnPath(
                "Compose Canvas",
                path.asAndroidPath(),
                0f,
                0f,
                android.graphics.Paint().apply {
                    color = android.graphics.Color.WHITE
                    textSize = 40f
                    isAntiAlias = true
                }
            )
            
            // 5. 使用DrawScope的高级功能
            drawCircle(
                color = Color.Yellow,
                radius = 50f,
                center = Offset(canvasWidth / 2, canvasHeight / 2),
                blendMode = BlendMode.Screen
            )
            
            // 6. 绘制阴影效果
            drawCircle(
                color = Color.Magenta,
                radius = 40f,
                center = Offset(canvasWidth * 0.3f, canvasHeight * 0.3f),
                style = Stroke(width = 10f)
            )
            
            // 添加阴影
            drawCircle(
                color = Color.Black.copy(alpha = 0.3f),
                radius = 40f,
                center = Offset(canvasWidth * 0.3f + 5f, canvasHeight * 0.3f + 5f),
                style = Stroke(width = 10f)
            )
        }
    }
}

@Composable
fun DrawScope.drawParticles(width: Float, height: Float) {
    val currentTime = System.currentTimeMillis()
    val particleCount = 100
    
    repeat(particleCount) { i ->
        // 使用伪随机生成粒子位置
        val seed = (i * 1000L + currentTime / 1000) % 10000
        val random = Random(seed)
        
        val x = (random.nextFloat() * width)
        val y = (random.nextFloat() * height)
        
        // 基于时间变化的粒子大小
        val timeFactor = (currentTime % 2000) / 2000f
        val size = 10f + 5f * sin(timeFactor * 2 * PI.toFloat() + i).absoluteValue
        
        // 基于位置的粒子颜色
        val hue = (x / width + y / height) * 180f
        val color = Color.hsv(hue, 0.8f, 0.9f)
        
        drawCircle(
            color = color,
            radius = size,
            center = Offset(x, y),
            alpha = 0.7f
        )
        
        // 绘制粒子拖尾
        drawCircle(
            color = color.copy(alpha = 0.3f),
            radius = size * 1.5f,
            center = Offset(x, y)
        )
    }
}

7.3 硬件加速最佳实践

// 硬件加速优化示例
public class HardwareAcceleratedView extends View {
    private Paint paint = new Paint();
    private Bitmap hardwareLayerBitmap;
    private boolean useHardwareLayer = true;
    
    public HardwareAcceleratedView(Context context) {
        super(context);
        init();
    }
    
    private void init() {
        paint.setColor(Color.BLUE);
        paint.setStyle(Paint.Style.FILL);
        paint.setAntiAlias(true);
        
        // 根据API级别选择最佳策略
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            // Android 5.0+ 使用RenderThread
            configureForRenderThread();
        }
    }
    
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private void configureForRenderThread() {
        // 启用渲染线程分离
        setLayerType(LAYER_TYPE_HARDWARE, null);
        
        // 设置Z轴高度,启用硬件层
        setTranslationZ(1);
        
        // 配置Outline以启用圆角硬件加速
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            setOutlineProvider(new ViewOutlineProvider() {
                @Override
                public void getOutline(View view, Outline outline) {
                    outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), 20);
                }
            });
            setClipToOutline(true);
        }
    }
    
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        
        if (useHardwareLayer) {
            // 为硬件层创建Bitmap
            hardwareLayerBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas(hardwareLayerBitmap);
            drawToHardwareLayer(canvas);
        }
    }
    
    private void drawToHardwareLayer(Canvas canvas) {
        // 在硬件层上绘制静态内容
        canvas.drawColor(Color.WHITE);
        
        // 使用硬件加速友好的绘制操作
        Path path = new Path();
        path.addCircle(getWidth()/2f, getHeight()/2f, 100, Path.Direction.CW);
        
        Paint layerPaint = new Paint(paint);
        layerPaint.setShader(new LinearGradient(
            0, 0, getWidth(), getHeight(),
            Color.RED, Color.BLUE, Shader.TileMode.CLAMP
        ));
        
        canvas.drawPath(path, layerPaint);
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        
        if (useHardwareLayer && hardwareLayerBitmap != null) {
            // 绘制硬件层
            canvas.drawBitmap(hardwareLayerBitmap, 0, 0, paint);
            
            // 在硬件层上叠加动态内容
            drawDynamicContent(canvas);
        } else {
            // 软件绘制
            drawSoftwareContent(canvas);
        }
    }
    
    private void drawDynamicContent(Canvas canvas) {
        // 只绘制动态变化的内容
        long currentTime = System.currentTimeMillis();
        float angle = (currentTime % 2000) / 2000f * 360;
        
        canvas.save();
        canvas.rotate(angle, getWidth()/2f, getHeight()/2f);
        canvas.drawRect(getWidth()/2f - 50, getHeight()/2f - 50,
            getWidth()/2f + 50, getHeight()/2f + 50, paint);
        canvas.restore();
    }
    
    private void drawSoftwareContent(Canvas canvas) {
        // 完整的软件绘制
        canvas.drawColor(Color.WHITE);
        
        // 避免使用硬件加速不支持的操作
        // paint.setShadowLayer(10, 0, 0, Color.BLACK); // 这个在硬件加速下可能有问题
        
        // 使用替代方案
        drawShadowManually(canvas);
        
        Path path = new Path();
        path.addCircle(getWidth()/2f, getHeight()/2f, 100, Path.Direction.CW);
        canvas.drawPath(path, paint);
    }
    
    private void drawShadowManually(Canvas canvas) {
        // 手动实现阴影效果
        Paint shadowPaint = new Paint();
        shadowPaint.setColor(Color.argb(100, 0, 0, 0));
        
        for (int i = 0; i < 10; i++) {
            shadowPaint.setAlpha(100 - i * 10);
            canvas.drawCircle(
                getWidth()/2f + i * 0.5f,
                getHeight()/2f + i * 0.5f,
                100 + i,
                shadowPaint
            );
        }
    }
    
    // 性能分析辅助方法
    public void startPerformanceProfiling() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            // 启用绘制时间追踪
            setOnDrawListener(new OnDrawListener() {
                long lastDrawTime = 0;
                int frameCount = 0;
                
                @Override
                public void onDraw(View view, long drawTime) {
                    frameCount++;
                    
                    if (lastDrawTime == 0) {
                        lastDrawTime = drawTime;
                    }
                    
                    long elapsed = drawTime - lastDrawTime;
                    if (elapsed >= 1000) { // 每秒钟
                        float fps = frameCount * 1000f / elapsed;
                        Log.d("Performance", "FPS: " + fps);
                        
                        frameCount = 0;
                        lastDrawTime = drawTime;
                    }
                    
                    // 检查绘制时间是否过长
                    long drawDuration = System.currentTimeMillis() - (drawTime / 1000000);
                    if (drawDuration > 16) { // 超过16ms可能掉帧
                        Log.w("Performance", "Long draw: " + drawDuration + "ms");
                    }
                }
            });
        }
    }
    
    // 优化建议
    public void applyOptimizations() {
        // 1. 避免在onDraw中创建对象
        // 2. 使用硬件层缓存静态内容
        // 3. 减少过度绘制
        // 4. 使用合适的Bitmap配置
        // 5. 避免使用硬件加速不支持的操作
        
        setLayerType(useHardwareLayer ? 
            LAYER_TYPE_HARDWARE : LAYER_TYPE_SOFTWARE, null);
        
        // 启用裁剪以减少过度绘制
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            setClipBounds(new Rect(0, 0, getWidth(), getHeight()));
        }
        
        // 如果View不透明,可以设置
        setWillNotDraw(false);
        
        // 对于静态View,可以设置
        // setWillNotCacheDrawing(false);
    }
}

八、综合实战案例

8.1 自定义复杂控件实现

仪表盘控件

public class DashboardView extends View {
    private Paint arcPaint = new Paint();
    private Paint scalePaint = new Paint();
    private Paint needlePaint = new Paint();
    private Paint textPaint = new Paint();
    private Paint valuePaint = new Paint();
    
    private float currentValue = 0;
    private float maxValue = 100;
    private float minValue = 0;
    private int divisions = 10;
    private int subdivisions = 5;
    
    private float startAngle = 150;
    private float sweepAngle = 240;
    
    private ValueAnimator needleAnimator;
    
    public DashboardView(Context context) {
        super(context);
        init();
    }
    
    private void init() {
        // 圆弧画笔
        arcPaint.setStyle(Paint.Style.STROKE);
        arcPaint.setStrokeWidth(20);
        arcPaint.setAntiAlias(true);
        arcPaint.setStrokeCap(Paint.Cap.ROUND);
        
        // 刻度画笔
        scalePaint.setStyle(Paint.Style.STROKE);
        scalePaint.setStrokeWidth(3);
        scalePaint.setColor(Color.BLACK);
        scalePaint.setAntiAlias(true);
        
        // 指针画笔
        needlePaint.setStyle(Paint.Style.FILL_AND_STROKE);
        needlePaint.setStrokeWidth(5);
        needlePaint.setColor(Color.RED);
        needlePaint.setAntiAlias(true);
        
        // 文字画笔
        textPaint.setTextSize(30);
        textPaint.setColor(Color.BLACK);
        textPaint.setTextAlign(Paint.Align.CENTER);
        textPaint.setAntiAlias(true);
        
        // 数值画笔
        valuePaint.setTextSize(50);
        valuePaint.setColor(Color.BLUE);
        valuePaint.setTextAlign(Paint.Align.CENTER);
        valuePaint.setAntiAlias(true);
        valuePaint.setTypeface(Typeface.DEFAULT_BOLD);
    }
    
    public void setValue(float value, boolean animate) {
        if (animate) {
            animateNeedle(value);
        } else {
            currentValue = value;
            invalidate();
        }
    }
    
    private void animateNeedle(float targetValue) {
        if (needleAnimator != null && needleAnimator.isRunning()) {
            needleAnimator.cancel();
        }
        
        needleAnimator = ValueAnimator.ofFloat(currentValue, targetValue);
        needleAnimator.setDuration(1000);
        needleAnimator.setInterpolator(new OvershootInterpolator(0.5f));
        needleAnimator.addUpdateListener(animation -> {
            currentValue = (float) animation.getAnimatedValue();
            invalidate();
        });
        needleAnimator.start();
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        
        int width = getWidth();
        int height = getHeight();
        int size = Math.min(width, height);
        int centerX = width / 2;
        int centerY = height / 2;
        int radius = size / 2 - 40;
        
        // 1. 绘制背景圆弧
        drawBackgroundArc(canvas, centerX, centerY, radius);
        
        // 2. 绘制渐变圆弧(当前值)
        drawValueArc(canvas, centerX, centerY, radius);
        
        // 3. 绘制刻度
        drawScales(canvas, centerX, centerY, radius);
        
        // 4. 绘制刻度文字
        drawScaleText(canvas, centerX, centerY, radius);
        
        // 5. 绘制指针
        drawNeedle(canvas, centerX, centerY, radius);
        
        // 6. 绘制中心圆
        drawCenterCircle(canvas, centerX, centerY);
        
        // 7. 绘制当前数值
        drawCurrentValue(canvas, centerX, centerY);
    }
    
    private void drawBackgroundArc(Canvas canvas, int centerX, int centerY, int radius) {
        RectF arcRect = new RectF(
            centerX - radius,
            centerY - radius,
            centerX + radius,
            centerY + radius
        );
        
        // 绘制背景圆弧
        arcPaint.setColor(Color.LTGRAY);
        arcPaint.setAlpha(100);
        canvas.drawArc(arcRect, startAngle, sweepAngle, false, arcPaint);
        
        // 绘制圆弧边框
        arcPaint.setColor(Color.DKGRAY);
        arcPaint.setAlpha(255);
        arcPaint.setStrokeWidth(2);
        canvas.drawArc(arcRect, startAngle, sweepAngle, false, arcPaint);
        
        arcPaint.setStrokeWidth(20); // 恢复宽度
    }
    
    private void drawValueArc(Canvas canvas, int centerX, int centerY, int radius) {
        RectF arcRect = new RectF(
            centerX - radius,
            centerY - radius,
            centerX + radius,
            centerY + radius
        );
        
        // 计算当前角度
        float valueRatio = (currentValue - minValue) / (maxValue - minValue);
        float currentSweep = sweepAngle * valueRatio;
        
        // 创建渐变着色器
        Shader shader = new SweepGradient(centerX, centerY,
            new int[]{Color.GREEN, Color.YELLOW, Color.RED},
            new float[]{0, 0.5f, 1.0f}
        );
        
        // 旋转渐变以匹配起始角度
        Matrix matrix = new Matrix();
        matrix.postRotate(startAngle - 90, centerX, centerY);
        shader.setLocalMatrix(matrix);
        
        arcPaint.setShader(shader);
        arcPaint.setStrokeCap(Paint.Cap.ROUND);
        
        canvas.drawArc(arcRect, startAngle, currentSweep, false, arcPaint);
        
        arcPaint.setShader(null); // 清除着色器
    }
    
    private void drawScales(Canvas canvas, int centerX, int centerY, int radius) {
        int scaleLength = 20;
        int majorScaleLength = 30;
        
        float angleStep = sweepAngle / (divisions * subdivisions);
        
        for (int i = 0; i <= divisions * subdivisions; i++) {
            float angle = startAngle + i * angleStep;
            float radian = (float) Math.toRadians(angle);
            
            boolean isMajorScale = (i % subdivisions == 0);
            int length = isMajorScale ? majorScaleLength : scaleLength;
            scalePaint.setStrokeWidth(isMajorScale ? 4 : 2);
            
            float startX = centerX + (radius - length) * (float) Math.cos(radian);
            float startY = centerY + (radius - length) * (float) Math.sin(radian);
            float endX = centerX + radius * (float) Math.cos(radian);
            float endY = centerY + radius * (float) Math.sin(radian);
            
            canvas.drawLine(startX, startY, endX, endY, scalePaint);
        }
    }
    
    private void drawScaleText(Canvas canvas, int centerX, int centerY, int radius) {
        int textOffset = 60;
        
        for (int i = 0; i <= divisions; i++) {
            float value = minValue + (maxValue - minValue) * i / divisions;
            float angle = startAngle + sweepAngle * i / divisions;
            float radian = (float) Math.toRadians(angle);
            
            float textX = centerX + (radius + textOffset) * (float) Math.cos(radian);
            float textY = centerY + (radius + textOffset) * (float) Math.sin(radian);
            
            // 调整文本位置使其居中
            String text = String.valueOf((int) value);
            float textWidth = textPaint.measureText(text);
            
            canvas.drawText(text, textX - textWidth / 2, textY + 10, textPaint);
        }
    }
    
    private void drawNeedle(Canvas canvas, int centerX, int centerY, int radius) {
        float valueRatio = (currentValue - minValue) / (maxValue - minValue);
        float angle = startAngle + sweepAngle * valueRatio;
        float radian = (float) Math.toRadians(angle);
        
        int needleLength = radius - 40;
        
        // 绘制指针
        float endX = centerX + needleLength * (float) Math.cos(radian);
        float endY = centerY + needleLength * (float) Math.sin(radian);
        
        needlePaint.setColor(Color.RED);
        canvas.drawLine(centerX, centerY, endX, endY, needlePaint);
        
        // 绘制指针尾部
        float tailLength = 20;
        float tailAngle = angle + 180;
        float tailRadian = (float) Math.toRadians(tailAngle);
        float tailX = centerX + tailLength * (float) Math.cos(tailRadian);
        float tailY = centerY + tailLength * (float) Math.sin(tailRadian);
        
        needlePaint.setColor(Color.DKGRAY);
        canvas.drawLine(centerX, centerY, tailX, tailY, needlePaint);
        
        // 绘制指针头(三角形)
        drawNeedleHead(canvas, endX, endY, angle);
    }
    
    private void drawNeedleHead(Canvas canvas, float x, float y, float angle) {
        canvas.save();
        canvas.translate(x, y);
        canvas.rotate(angle);
        
        Path triangle = new Path();
        triangle.moveTo(0, 0);
        triangle.lineTo(-15, -10);
        triangle.lineTo(-15, 10);
        triangle.close();
        
        needlePaint.setColor(Color.RED);
        canvas.drawPath(triangle, needlePaint);
        
        // 绘制边框
        needlePaint.setStyle(Paint.Style.STROKE);
        needlePaint.setColor(Color.DKGRAY);
        needlePaint.setStrokeWidth(2);
        canvas.drawPath(triangle, needlePaint);
        
        needlePaint.setStyle(Paint.Style.FILL_AND_STROKE);
        canvas.restore();
    }
    
    private void drawCenterCircle(Canvas canvas, int centerX, int centerY) {
        Paint centerPaint = new Paint();
        centerPaint.setStyle(Paint.Style.FILL);
        
        // 创建径向渐变
        Shader shader = new RadialGradient(
            centerX, centerY, 15,
            Color.WHITE, Color.DKGRAY, Shader.TileMode.CLAMP
        );
        centerPaint.setShader(shader);
        
        canvas.drawCircle(centerX, centerY, 15, centerPaint);
        
        // 绘制边框
        centerPaint.setShader(null);
        centerPaint.setStyle(Paint.Style.STROKE);
        centerPaint.setStrokeWidth(2);
        centerPaint.setColor(Color.BLACK);
        canvas.drawCircle(centerX, centerY, 15, centerPaint);
    }
    
    private void drawCurrentValue(Canvas canvas, int centerX, int centerY) {
        String valueText = String.format("%.1f", currentValue);
        String unitText = "km/h";
        
        canvas.drawText(valueText, centerX, centerY + 100, valuePaint);
        
        textPaint.setTextSize(20);
        canvas.drawText(unitText, centerX, centerY + 130, textPaint);
        textPaint.setTextSize(30); // 恢复大小
    }
}

九、调试与测试

9.1 Canvas绘图调试技巧

// Canvas调试工具类
public class CanvasDebugHelper {
    
    // 绘制视图边界和padding/margin
    public static void drawViewBounds(Canvas canvas, View view) {
        Paint boundsPaint = new Paint();
        boundsPaint.setStyle(Paint.Style.STROKE);
        boundsPaint.setStrokeWidth(2);
        
        // 绘制视图边界(红色)
        boundsPaint.setColor(Color.RED);
        canvas.drawRect(0, 0, view.getWidth(), view.getHeight(), boundsPaint);
        
        // 绘制padding区域(蓝色)
        boundsPaint.setColor(Color.BLUE);
        boundsPaint.setAlpha(100);
        canvas.drawRect(
            view.getPaddingLeft(),
            view.getPaddingTop(),
            view.getWidth() - view.getPaddingRight(),
            view.getHeight() - view.getPaddingBottom(),
            boundsPaint
        );
        
        // 绘制文本基准线(绿色)
        if (view instanceof TextView) {
            TextView textView = (TextView) view;
            Paint baselinePaint = new Paint();
            baselinePaint.setColor(Color.GREEN);
            baselinePaint.setStrokeWidth(1);
            
            Layout layout = textView.getLayout();
            if (layout != null) {
                int lineCount = layout.getLineCount();
                for (int i = 0; i < lineCount; i++) {
                    float baseline = layout.getLineBaseline(i);
                    canvas.drawLine(0, baseline, view.getWidth(), baseline, baselinePaint);
                }
            }
        }
    }
    
    // 绘制绘制时间
    public static void drawRenderTime(Canvas canvas, long renderTime) {
        Paint timePaint = new Paint();
        timePaint.setColor(Color.WHITE);
        timePaint.setTextSize(30);
        timePaint.setTypeface(Typeface.MONOSPACE);
        
        String timeText = String.format("Draw: %.2fms", renderTime / 1000000.0);
        
        // 背景
        Paint bgPaint = new Paint();
        bgPaint.setColor(Color.argb(200, 0, 0, 0));
        Rect textBounds = new Rect();
        timePaint.getTextBounds(timeText, 0, timeText.length(), textBounds);
        
        int padding = 10;
        canvas.drawRect(
            10 - padding,
            10 - padding,
            10 + textBounds.width() + padding,
            10 + textBounds.height() + padding,
            bgPaint
        );
        
        // 文字
        canvas.drawText(timeText, 10, 10 + textBounds.height(), timePaint);
        
        // 帧率指示器
        if (renderTime > 16000000) { // 超过16ms
            timePaint.setColor(Color.RED);
        } else if (renderTime > 8000000) { // 8-16ms
            timePaint.setColor(Color.YELLOW);
        } else {
            timePaint.setColor(Color.GREEN);
        }
        
        canvas.drawCircle(20 + textBounds.width() + padding + 20, 
            10 + textBounds.height() / 2, 10, timePaint);
    }
    
    // 检测过度绘制
    public static void highlightOverdraw(Canvas canvas, View view) {
        // 这个方法应该在开启过度绘制调试后使用
        Paint overdrawPaint = new Paint();
        overdrawPaint.setStyle(Paint.Style.FILL);
        
        // 根据过度绘制次数设置不同颜色
        // 这需要实际检测,这里只是示例
        overdrawPaint.setColor(Color.argb(50, 255, 0, 0)); // 半透明红色
        
        // 绘制全屏半透明层来模拟过度绘制
        canvas.drawRect(0, 0, view.getWidth(), view.getHeight(), overdrawPaint);
        
        // 在实际应用中,需要更精确的检测
        // 可以考虑使用以下方法:
        // 1. 使用getWindowVisibility()检测
        // 2. 使用ViewTreeObserver监听绘制
        // 3. 使用性能分析工具
    }
    
    // 绘制坐标网格
    public static void drawCoordinateGrid(Canvas canvas, View view) {
        Paint gridPaint = new Paint();
        gridPaint.setColor(Color.LTGRAY);
        gridPaint.setStrokeWidth(1);
        gridPaint.setAlpha(100);
        
        int gridSize = 50; // 网格大小
        
        // 绘制垂直线
        for (int x = 0; x < view.getWidth(); x += gridSize) {
            canvas.drawLine(x, 0, x, view.getHeight(), gridPaint);
        }
        
        // 绘制水平线
        for (int y = 0; y < view.getHeight(); y += gridSize) {
            canvas.drawLine(0, y, view.getWidth(), y, gridPaint);
        }
        
        // 绘制坐标轴
        Paint axisPaint = new Paint();
        axisPaint.setColor(Color.BLUE);
        axisPaint.setStrokeWidth(2);
        
        // X轴
        canvas.drawLine(0, view.getHeight() / 2, 
            view.getWidth(), view.getHeight() / 2, axisPaint);
        // Y轴
        canvas.drawLine(view.getWidth() / 2, 0, 
            view.getWidth() / 2, view.getHeight(), axisPaint);
        
        // 绘制坐标标签
        Paint labelPaint = new Paint();
        labelPaint.setColor(Color.BLACK);
        labelPaint.setTextSize(20);
        
        // 原点
        canvas.drawText("(0,0)", view.getWidth() / 2 + 10, 
            view.getHeight() / 2 - 10, labelPaint);
    }
}

// 性能监控View
public class PerformanceMonitorView extends View {
    private List<Long> drawTimes = new ArrayList<>();
    private static final int MAX_SAMPLES = 100;
    private Paint paint = new Paint();
    private float averageDrawTime = 0;
    private float maxDrawTime = 0;
    private float fps = 0;
    
    public PerformanceMonitorView(Context context) {
        super(context);
        init();
    }
    
    private void init() {
        paint.setColor(Color.WHITE);
        paint.setTextSize(30);
        paint.setTypeface(Typeface.MONOSPACE);
        paint.setAntiAlias(true);
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        
        long startTime = System.nanoTime();
        
        // 绘制性能图表
        drawPerformanceChart(canvas);
        
        long endTime = System.nanoTime();
        long drawTime = endTime - startTime;
        
        // 记录绘制时间
        drawTimes.add(drawTime);
        if (drawTimes.size() > MAX_SAMPLES) {
            drawTimes.remove(0);
        }
        
        // 计算统计数据
        calculateStatistics();
        
        // 绘制统计数据
        drawStatistics(canvas);
    }
    
    private void drawPerformanceChart(Canvas canvas) {
        if (drawTimes.isEmpty()) return;
        
        int width = getWidth();
        int height = getHeight();
        
        // 绘制背景
        Paint bgPaint = new Paint();
        bgPaint.setColor(Color.argb(200, 0, 0, 0));
        canvas.drawRect(0, 0, width, height, bgPaint);
        
        // 绘制图表
        Paint chartPaint = new Paint();
        chartPaint.setStyle(Paint.Style.STROKE);
        chartPaint.setStrokeWidth(2);
        
        float maxTime = 0;
        for (Long time : drawTimes) {
            if (time > maxTime) maxTime = time;
        }
        
        float xStep = (float) width / (drawTimes.size() - 1);
        float yScale = (float) height / maxTime;
        
        Path chartPath = new Path();
        chartPath.moveTo(0, height - drawTimes.get(0) * yScale);
        
        for (int i = 1; i < drawTimes.size(); i++) {
            float x = i * xStep;
            float y = height - drawTimes.get(i) * yScale;
            chartPath.lineTo(x, y);
        }
        
        // 根据绘制时间设置颜色
        if (maxTime > 16000000) { // 超过16ms
            chartPaint.setColor(Color.RED);
        } else if (maxTime > 8000000) { // 8-16ms
            chartPaint.setColor(Color.YELLOW);
        } else {
            chartPaint.setColor(Color.GREEN);
        }
        
        canvas.drawPath(chartPath, chartPaint);
        
        // 绘制阈值线(16ms = 60fps)
        Paint thresholdPaint = new Paint();
        thresholdPaint.setColor(Color.WHITE);
        thresholdPaint.setStrokeWidth(1);
        thresholdPaint.setAlpha(100);
        
        float thresholdY = height - 16000000 * yScale;
        canvas.drawLine(0, thresholdY, width, thresholdY, thresholdPaint);
        
        // 绘制阈值标签
        canvas.drawText("16ms", width - 100, thresholdY - 10, paint);
    }
    
    private void calculateStatistics() {
        if (drawTimes.isEmpty()) return;
        
        long total = 0;
        maxDrawTime = 0;
        
        for (Long time : drawTimes) {
            total += time;
            if (time > maxDrawTime) {
                maxDrawTime = time;
            }
        }
        
        averageDrawTime = total / (float) drawTimes.size();
        
        // 计算FPS(基于平均绘制时间)
        if (averageDrawTime > 0) {
            fps = 1000000000f / averageDrawTime;
        }
    }
    
    private void drawStatistics(Canvas canvas) {
        String stats = String.format(
            "Avg: %.2fms\nMax: %.2fms\nFPS: %.1f\nSamples: %d",
            averageDrawTime / 1000000f,
            maxDrawTime / 1000000f,
            fps,
            drawTimes.size()
        );
        
        // 绘制背景
        Paint bgPaint = new Paint();
        bgPaint.setColor(Color.argb(200, 0, 0, 0));
        Rect textBounds = new Rect();
        paint.getTextBounds(stats, 0, stats.length(), textBounds);
        
        int padding = 10;
        canvas.drawRect(
            10 - padding,
            10 - padding,
            10 + textBounds.width() + padding,
            10 + textBounds.height() + padding,
            bgPaint
        );
        
        // 绘制统计文本
        String[] lines = stats.split("\n");
        float y = 10 + paint.getTextSize();
        for (String line : lines) {
            canvas.drawText(line, 10, y, paint);
            y += paint.getTextSize() * 1.2f;
        }
    }
    
    public void reset() {
        drawTimes.clear();
        invalidate();
    }
}

十、总结与展望

通过本文的深入探讨,我们全面了解了Android Canvas绘图从基础到高级的各个方面。从核心组件到高级技巧,从性能优化到特效实现,Canvas为Android开发者提供了强大的2D图形绘制能力。

关键要点回顾:

  1. Canvas基础:掌握Canvas、Paint、Path、Bitmap等核心组件的使用是绘图的基础
  2. 高级技巧:着色器、颜色过滤器、混合模式、矩阵变换等高级功能可以实现复杂视觉效果
  3. 性能优化:合理使用离屏缓冲、脏矩形更新、硬件加速等技术可以显著提升绘图性能
  4. 特效实现:粒子系统、波形效果、滤镜处理等特效可以大大增强用户体验
  5. 现代集成:Canvas与OpenGL ES、Jetpack Compose等现代技术的结合是未来发展方向

实际开发建议:

  1. 性能优先:始终关注绘图性能,避免过度绘制和不必要的对象创建
  2. 代码复用:将常用的绘图逻辑封装成可复用的组件
  3. 测试充分:在不同设备和Android版本上测试绘图效果和性能
  4. 保持学习:关注Android图形技术的发展,及时学习新的API和最佳实践

未来展望:

随着Android图形技术的不断发展,Canvas也在持续进化。未来我们可以期待:

  1. 更好的硬件加速支持:更高效的GPU利用和更少的限制
  2. 更强大的API:更丰富的图形效果和更简单的实现方式
  3. 与3D图形更好的集成:Canvas与OpenGL ES、Vulkan等3D图形API的无缝结合
  4. 跨平台支持:Canvas API在跨平台框架中的更好支持

Canvas作为Android 2D图形绘制的核心,虽然已经有十多年的历史,但依然充满活力。掌握Canvas的高级技巧,不仅可以帮助你实现复杂的UI效果,还能为学习更高级的图形技术打下坚实基础。


希望本文能成为你在Android绘图开发中的实用指南,帮助你在实际项目中解决具体问题,创造出令人惊艳的视觉效果。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐