You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android屏幕旋转时自定义ImageView Canvas绘制内容后置、多次旋转丢失问题

自定义ImageView屏幕旋转绘制异常解决方案

现有代码核心问题

  • 滥用静态修饰符修饰drawPath、paintColor,View旋转重建时多实例共享状态导致逻辑错乱
  • onSizeChanged方法每次旋转直接创建空白canvasBitmap,原有绘制内容被直接覆盖
  • 路径使用View绝对坐标存储,未与底层图片做相对坐标绑定,旋转后View宽高互换导致绘制偏移
  • 冗余逻辑setVariables中重复调用setupDrawing()重置画笔参数,导致绘制状态丢失
  • onDraw中存在无用的旋转、save/restore逻辑,绘制顺序混乱导致内容被覆盖

修复步骤

1. 移除错误的静态修饰符

静态变量会导致多个View实例共享状态,旋转重建后状态冲突,直接删除相关变量的static修饰:

//drawing path
CustomPath drawPath;
//initial color
int paintColor = 0xFF000000;

2. 优化onSizeChanged保留原有绘制内容

旋转后View尺寸变化时,不要直接创建空白Bitmap,将原有绘制内容缩放适配新尺寸:

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    Bitmap oldBitmap = canvasBitmap;
    // 创建新尺寸的Bitmap
    canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
    drawCanvas = new Canvas(canvasBitmap);
    if (oldBitmap != null && !oldBitmap.isRecycled()) {
        // 把旧的绘制内容缩放后绘制到新Bitmap上
        Matrix scaleMatrix = new Matrix();
        scaleMatrix.postScale((float)w / oldBitmap.getWidth(), (float)h / oldBitmap.getHeight());
        drawCanvas.drawBitmap(oldBitmap, scaleMatrix, canvasPaint);
        oldBitmap.recycle();
    }
    heightB = h;
    wieghtB = w;
}

3. 绑定绘制内容与图片相对坐标(解决偏移问题)

将触摸坐标转换为底层图片的比例坐标(取值范围0~1),不管View尺寸怎么变,绘制内容永远对应图片的同一位置:

// 新增方法:把View触摸坐标转为图片相对比例坐标
private float[] convertToImageRelative(float touchX, float touchY) {
    float[] point = new float[2];
    Matrix inverseMatrix = new Matrix();
    // 获取ImageView图片变换矩阵的反转矩阵,计算触摸点对应图片的原始坐标
    getImageMatrix().invert(inverseMatrix);
    point[0] = touchX;
    point[1] = touchY;
    inverseMatrix.mapPoints(point);
    // 转换为0~1的比例值,适配所有尺寸场景
    Drawable imgDrawable = getDrawable();
    if (imgDrawable != null) {
        point[0] = point[0] / imgDrawable.getIntrinsicWidth();
        point[1] = point[1] / imgDrawable.getIntrinsicHeight();
    }
    return point;
}

// 新增方法:把图片相对比例坐标转回当前View的实际绘制坐标
private float[] convertToViewCoordinate(float relativeX, float relativeY) {
    float[] point = new float[2];
    Drawable imgDrawable = getDrawable();
    if (imgDrawable != null) {
        point[0] = relativeX * imgDrawable.getIntrinsicWidth();
        point[1] = relativeY * imgDrawable.getIntrinsicHeight();
        getImageMatrix().mapPoints(point);
    }
    return point;
}

触摸事件和路径存储全部基于比例坐标处理,绘制时再转换为当前View的实际坐标即可。

4. 清理冗余逻辑修复onDraw

删除cc相关的无用逻辑,统一绘制顺序保证内容层级正确:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas); // 先绘制底层图片
    canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint); // 再绘制已完成的绘制内容
    canvas.drawPath(drawPath, drawPaint); // 最后绘制正在编辑的路径
}

同时删除setVariables方法中重复调用的setupDrawing(),避免画笔状态被意外重置。

5. 可选优化(避免Activity重建)

如果业务场景允许,直接在Manifest对应的Activity节点添加配置,旋转时不重建Activity,从根源避免状态丢失问题:

<activity
    android:name=".YourTargetActivity"
    android:configChanges="orientation|screenSize|screenLayout" />

内容的提问来源于stack exchange,提问作者RR_Pradhan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 20:39:03