Canvas中圆形内绘制Bitmap并填充外部颜色功能失效求助
解决圆形区域显示Bitmap、外部填充背景色的问题
我来帮你搞定这个问题!你遇到的核心问题是PorterDuffXfermode混合顺序错误加上硬件加速对Xfermode的限制,还有废弃的DrawingCache使用导致的显示异常。咱们一步步来修正:
问题根源拆解
- 硬件加速限制:你设置了
LAYER_TYPE_HARDWARE,但像SRC_OUT这类PorterDuff混合模式在硬件加速下需要使用离屏缓冲(saveLayer)才能正常工作,直接绘制会导致混合效果失效,出现白色圆形。 - 绘制顺序颠倒:当前先画全屏背景,再用
SRC_OUT画圆形,这会把圆形区域的背景挖掉,但你需要的是圆形内显示放大Bitmap、外部显示背景色,顺序完全搞反了。 - 废弃API使用:
buildDrawingCache()和getDrawingCache()在API 28+已经被废弃,不仅有性能问题,还可能导致Bitmap获取异常。
修正后的完整代码
class ImageMagnifier2 extends AppCompatImageView { private PointF zoomPos = new PointF(0, 0); private Matrix matrix = new Matrix(); private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); private Paint paintBackground = new Paint(Paint.ANTI_ALIAS_FLAG); private boolean zooming = false; private float sizeOfMagnifier = 200f; // 可根据需求调整放大镜大小 public ImageMagnifier2(final Context context) { super(context); init(); } public ImageMagnifier2(Context context, AttributeSet attrs) { super(context, attrs); init(); } public ImageMagnifier2(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } @Override public boolean dispatchTouchEvent(MotionEvent me) { // 保留你的手势逻辑,比如更新zoomPos和zooming状态 return super.dispatchTouchEvent(me); } private void init() { setWillNotDraw(false); // 移除硬件加速设置,或在混合时使用saveLayer兼容硬件加速 // setLayerType(LAYER_TYPE_HARDWARE, null); // 初始化背景画笔 paintBackground.setColor(Color.parseColor("#F227aae0")); paintBackground.setStyle(Paint.Style.FILL); // 初始化放大镜画笔,用SRC_IN模式只保留圆形内的Bitmap paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); } @Override protected void onDraw(Canvas canvas) { if (!zooming) { // 非放大状态直接绘制原View super.onDraw(canvas); return; } // 获取当前View的Bitmap(替代废弃的DrawingCache) Bitmap bitmap = getViewBitmap(); if (bitmap == null) { super.onDraw(canvas); return; } // 创建BitmapShader实现放大效果 Shader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); matrix.reset(); // 以触摸点为中心放大2倍 matrix.postScale(2f, 2f, zoomPos.x, zoomPos.y); shader.setLocalMatrix(matrix); paint.setShader(shader); // 用离屏缓冲确保Xfermode在硬件加速下生效 int saveCount = canvas.saveLayer(0, 0, canvas.getWidth(), canvas.getHeight(), null); // 先绘制全屏背景色 canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), paintBackground); // 再绘制放大的圆形区域,SRC_IN模式会只保留圆形内的Bitmap内容 canvas.drawCircle(zoomPos.x, zoomPos.y, sizeOfMagnifier - 5, paint); // 恢复画布,应用混合效果 canvas.restoreToCount(saveCount); // 回收Bitmap避免内存泄漏 if (!bitmap.isRecycled()) { bitmap.recycle(); } } // 获取当前View的Bitmap(替代废弃的DrawingCache) private Bitmap getViewBitmap() { Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); super.draw(canvas); return bitmap; } // 对外提供方法设置放大状态和位置 public void setZooming(boolean zooming, PointF zoomPos) { this.zooming = zooming; this.zoomPos.set(zoomPos); invalidate(); } }
关键修改说明
- 调整绘制顺序与Xfermode:
- 改用
SRC_IN模式,先绘制全屏背景,再绘制圆形区域的放大Bitmap,混合后只会保留圆形内的Bitmap,外部显示背景色,完全符合你的需求。
- 改用
- 解决硬件加速问题:
- 使用
canvas.saveLayer()创建离屏缓冲,确保PorterDuff混合模式在硬件加速下正常工作(也可以直接移除硬件加速设置,软件渲染下混合模式更稳定)。
- 使用
- 替换废弃API:
- 用自定义的
getViewBitmap()方法替代DrawingCache,通过创建新Canvas绘制View内容来获取Bitmap,更可靠且兼容高版本API。
- 用自定义的
- 完善构造方法:
- 补充了带AttributeSet的构造方法,确保在XML布局中使用时能正常初始化。
这样修改后,你就能实现圆形区域显示放大的Bitmap,外部填充指定背景色的效果了!
内容的提问来源于stack exchange,提问作者Shruti
相关产品推荐
相关产品推荐

