Android自定义Bitmap放大镜触摸点与显示内容偏移问题求解
Bitmap放大镜偏移问题排查与解决方案
核心错误原因
你的偏移问题主要来自3个错误点:
- 没有做触摸坐标到Bitmap实际像素坐标的转换:ImageView默认会根据scaleType对图片做缩放、裁剪,你直接用触摸的View坐标计算偏移,没有映射到原始Bitmap的对应位置,必然出现偏移。
- Shader矩阵计算逻辑错误:
preTranslate参数里混入了放大镜绘制位置mMagnifierDestPoint,而Shader的偏移只和要放大的Bitmap区域有关,和放大镜在View上的绘制位置无关,这部分参数错误直接导致放大区域偏移。 - 代码笔误:X轴偏移计算时错误使用
marginRight做减法,和Y轴逻辑不一致,额外引入X轴偏移。
修复步骤
- 第一步:添加触摸坐标到Bitmap坐标的映射方法,排除ImageView scaleType、padding的影响:
private fun touchToBitmapCoordinate(touchX: Float, touchY: Float): PointF { val targetDrawable = drawable ?: return PointF(0f, 0f) val imgMatrix = imageMatrix val inverseMatrix = Matrix() // 反转ImageView的绘制矩阵,把触摸点映射回Bitmap原始坐标 imgMatrix.invert(inverseMatrix) val coord = floatArrayOf(touchX - paddingLeft, touchY - paddingTop) inverseMatrix.mapPoints(coord) // 限制坐标不超出Bitmap边界 coord[0] = coord[0].coerceIn(0f, targetDrawable.intrinsicWidth.toFloat()) coord[1] = coord[1].coerceIn(0f, targetDrawable.intrinsicHeight.toFloat()) return PointF(coord[0], coord[1]) }
- 第二步:修正onDraw方法中的矩阵计算逻辑,删除错误的放大镜位置参数:
if (mShouldDrawMagnifier) { mMagnifierMatrix.reset() val bitmapPoint = touchToBitmapCoordinate(mTouchPoint.x, mTouchPoint.y) // 先平移到要放大的区域中心,再做缩放 mMagnifierMatrix.setTranslate( -bitmapPoint.x * MAGNIFICATION + mMagnifierRadius, -bitmapPoint.y * MAGNIFICATION + mMagnifierRadius ) mMagnifierMatrix.postScale(MAGNIFICATION, MAGNIFICATION) // 原有透明度、放大镜边框、十字线绘制逻辑保持不变 ... }
- 第三步:修正放大镜位置计算逻辑,避免屏幕宽高、状态栏高度引入的偏移:
@SuppressLint("WrongConstant") private fun setMagnifierPoints(xTouch: Float, yTouch: Float) { mTouchPoint.x = xTouch mTouchPoint.y = yTouch val halfWidth = width / 2f // 切换放大镜左右位置的阈值,避免放大镜挡手 val breakPoint = if (mMagnifierLeftSide) halfWidth - 15f else halfWidth + 15f if ((mMagnifierLeftSide && xTouch < breakPoint) || (!mMagnifierLeftSide && xTouch > breakPoint)) { mCurrentMagnifierAlpha = 0 mMagnifierLeftSide = !mMagnifierLeftSide } // 基于触摸点计算放大镜位置,不要用全局屏幕坐标 if (mMagnifierLeftSide) { mMagnifierDestPoint.x = xTouch - mMagnifierRadius - mMagnifierOffsetX } else { mMagnifierDestPoint.x = xTouch + mMagnifierRadius + mMagnifierOffsetX } mMagnifierDestPoint.y = yTouch - mMagnifierRadius - mMagnifierOffsetY // 限制放大镜不超出当前View的显示边界 mMagnifierDestPoint.x = mMagnifierDestPoint.x.coerceIn(mMagnifierRadius, width - mMagnifierRadius) mMagnifierDestPoint.y = mMagnifierDestPoint.y.coerceIn(mMagnifierRadius, height - mMagnifierRadius) // 原有顶部裁切逻辑可根据需求保留 ... }
可选排查项
如果修复后还有轻微偏移,可检查BitmapShader初始化逻辑,确认传入的是原始未缩放的Bitmap,没有提前做裁剪或者压缩。
内容的提问来源于stack exchange,提问作者Peace
相关产品推荐
相关产品推荐

