Android开发中Canvas如何处理屏幕方向旋转适配问题
Android Canvas 屏幕旋转内容移出可视区域修复方案
问题核心原因
- 初始化的缓存Bitmap为正方形,边长取屏幕长宽最大值,屏幕旋转后自定义View的可视区域尺寸发生变化,始终从(0,0)坐标绘制Bitmap会导致Bitmap超出View边界的部分被直接裁切。
- 未正确处理屏幕旋转的配置变更,默认旋转时Activity会重建,若没有做内容转储,已绘制内容会直接丢失。
- 初始化Bitmap的时机在View构造函数中,此时View未完成测量,拿到的屏幕尺寸和实际View绘制区域尺寸可能存在偏差。
具体修复步骤
- 调整绘制逻辑,增加居中偏移计算
修改onDraw方法,绘制Bitmap和临时路径时增加居中偏移,保证画布内容始终对齐当前View的中心:
override fun onDraw(canvas: Canvas) { super.onDraw(canvas) canvas.apply { drawColor(0) // 计算偏移量,让Bitmap中心和当前View中心重合 val offsetX = (width - mBitmap.width) / 2f val offsetY = (height - mBitmap.height) / 2f drawBitmap(mBitmap, offsetX, offsetY, mBitmapPaint) // 正在绘制的临时路径也要加相同偏移,保证绘制位置和缓存内容对齐 drawPath(mPath, offsetX, offsetY, mPaint) } }
- 拦截配置变更,避免旋转时重建清空内容
在AndroidManifest.xml中对应承载画布的Activity节点添加配置拦截属性:
<activity android:name=".YourCanvasActivity" android:configChanges="orientation|screenSize|keyboardHidden" android:exported="false"> </activity>
- 把Bitmap初始化和扩容逻辑移到
onSizeChanged回调中
不要在View的init块中直接创建Bitmap,onSizeChanged会在View首次测量完成、旋转导致尺寸变化时触发,在这里做初始化和扩容,同时保留旧Bitmap的绘制内容:
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { super.onSizeChanged(w, h, oldw, oldh) // 首次初始化 if (!::mBitmap.isInitialized) { val initSize = max(w, h) createBitmap(initSize, initSize) return } // 尺寸超过现有Bitmap大小时再扩容,避免内容丢失 if (w > mBitmap.width || h > mBitmap.height) { val newW = max(w, mBitmap.width) val newH = max(h, mBitmap.height) val newBitmap = Bitmap.createBitmap(newW, newH, Bitmap.Config.ARGB_8888) val tempCanvas = Canvas(newBitmap) // 将旧内容绘制到新Bitmap上 tempCanvas.drawBitmap(mBitmap, 0f, 0f, null) // 回收旧Bitmap,替换引用 mBitmap.recycle() mBitmap = newBitmap mCanvas.setBitmap(newBitmap) } }
把原来init块中调用createBitmap()的代码删掉即可。
注意点
- 所有通过
mCanvas绘制到缓存Bitmap的坐标,不需要加偏移量,偏移只在onDraw把缓存绘制到屏幕Canvas时处理,避免出现坐标偏移叠加的问题。 - 如果需要支持分屏、自由窗口等可变尺寸场景,这套逻辑也能自动适配,不需要额外修改。
内容的提问来源于stack exchange,提问作者oyeraghib
相关产品推荐
相关产品推荐

