如何提取相机拍照后对应SquareImageView遮罩区域的Bitmap?
解决SquareImageView遮罩区域Bitmap提取问题
嘿,我来帮你搞定这个方形区域提取的问题!你前面几步都走通了,卡在裁剪对应区域真的太常见了——毕竟相机拍照生成的Bitmap,和屏幕上预览、遮罩的尺寸比例往往不一样,直接按遮罩的坐标裁剪肯定不对,得做一套坐标转换才行。
核心思路
问题的根源在于:
- 相机默认以横屏输出图像,而我们的界面可能是竖屏的,导致Bitmap的宽高和屏幕显示的宽高是颠倒的
- 遮罩View的坐标是相对于屏幕的,需要先转换成相对于预览View的坐标,再缩放匹配Bitmap的分辨率
修改后的代码实现
把你的jpegCallback改成下面这样,注释里已经写清了每一步的作用:
Camera.PictureCallback jpegCallback = new Camera.PictureCallback() { @SuppressWarnings("deprecation") public void onPictureTaken(byte[] data, Camera camera) { // 1. 将拍照字节数组转为原始Bitmap Bitmap originalBitmap = BitmapFactory.decodeByteArray(data, 0, data.length); if (originalBitmap == null) { camera.startPreview(); return; } // 2. 获取遮罩View和预览View的实例 View maskView = findViewById(R.id.mask_square); View previewView = findViewById(R.id.your_preview_view_id); // 替换成你的预览View的ID // 3. 获取遮罩View在屏幕上的绝对位置 int[] maskLocation = new int[2]; maskView.getLocationOnScreen(maskLocation); int maskScreenLeft = maskLocation[0]; int maskScreenTop = maskLocation[1]; int maskWidth = maskView.getWidth(); int maskHeight = maskView.getHeight(); // 4. 获取预览View在屏幕上的绝对位置,计算遮罩相对于预览View的坐标 int[] previewLocation = new int[2]; previewView.getLocationOnScreen(previewLocation); int maskRelativeLeft = maskScreenLeft - previewLocation[0]; int maskRelativeTop = maskScreenTop - previewLocation[1]; // 5. 处理屏幕旋转,修正Bitmap的宽高逻辑(相机默认横屏输出) int screenRotation = getWindowManager().getDefaultDisplay().getRotation(); boolean isPortrait = screenRotation == Surface.ROTATION_0 || screenRotation == Surface.ROTATION_180; float bitmapRealWidth = isPortrait ? originalBitmap.getHeight() : originalBitmap.getWidth(); float bitmapRealHeight = isPortrait ? originalBitmap.getWidth() : originalBitmap.getHeight(); // 6. 计算预览View和Bitmap的缩放比例 float scaleX = bitmapRealWidth / previewView.getWidth(); float scaleY = bitmapRealHeight / previewView.getHeight(); // 7. 将遮罩的相对坐标转换为Bitmap上的坐标 int cropLeft = Math.round(maskRelativeLeft * scaleX); int cropTop = Math.round(maskRelativeTop * scaleY); int cropWidth = Math.round(maskWidth * scaleX); int cropHeight = Math.round(maskHeight * scaleY); // 8. 检查裁剪区域是否越界,避免抛出异常 cropLeft = Math.max(0, cropLeft); cropTop = Math.max(0, cropTop); cropWidth = Math.min(cropWidth, (int) bitmapRealWidth - cropLeft); cropHeight = Math.min(cropHeight, (int) bitmapRealHeight - cropTop); // 9. 裁剪Bitmap Bitmap croppedBitmap = Bitmap.createBitmap(originalBitmap, cropLeft, cropTop, cropWidth, cropHeight); // 10. 如果是竖屏,将裁剪后的Bitmap旋转回正确方向 if (isPortrait) { Matrix matrix = new Matrix(); matrix.postRotate(90); Bitmap rotatedBitmap = Bitmap.createBitmap(croppedBitmap, 0, 0, croppedBitmap.getWidth(), croppedBitmap.getHeight(), matrix, true); croppedBitmap.recycle(); // 回收中间Bitmap croppedBitmap = rotatedBitmap; } // 现在croppedBitmap就是遮罩对应的图像区域了,你可以用来显示或保存 // 示例:设置到SquareImageView SquareImageView squareImageView = findViewById(R.id.your_square_image_view_id); squareImageView.setImageBitmap(croppedBitmap); // 11. 回收不用的Bitmap,避免内存泄漏 originalBitmap.recycle(); // 记得在合适的时机回收croppedBitmap,比如页面销毁时 // 重启相机预览 camera.startPreview(); } };
关键注意事项
- 替换代码中的
your_preview_view_id和your_square_image_view_id为你实际的View ID - 如果你的预览是用
TextureView实现的,处理逻辑完全一样,只要获取它的宽高和位置即可 - 务必检查裁剪区域的边界,否则
Bitmap.createBitmap会抛出IllegalArgumentException - 记得及时回收Bitmap,避免内存溢出
内容的提问来源于stack exchange,提问作者Xtazer
相关产品推荐
相关产品推荐

