多变换处理Bitmap后GridView显示全黑问题求助
你的图片显示为黑色主要由以下几个问题导致,逐一修复即可解决:
1. 对比度调整计算错误导致全黑
在adjustContrast方法中,当前的对比度值计算会导致当contrastValue为0时,颜色矩阵的RGB通道系数全为0,直接将图片转为黑色。
错误代码:
int contrastValue = contrastSeekBar.getProgress() - (MAX_CONTRAST_VALUE / 2); // ... colorMatrix.set(new float[]{ value / 100f, 0, 0, 0, 0, 0, value / 100f, 0, 0, 0, 0, 0, value / 100f, 0, 0, 0, 0, 0, 1, 0 });
当contrastValue为0时,所有RGB系数都是0,像素值被清零,图片变黑。
修复方案:
对比度的正确范围应该是0到2(1为正常对比度),所以需要将计算后的数值转换为基于1的倍数:
private Bitmap adjustContrast(Bitmap image, float value) { Bitmap adjustedImage = Bitmap.createBitmap(image.getWidth(), image.getHeight(), image.getConfig()); Canvas canvas = new Canvas(adjustedImage); // 将value(-100~100)转换为0~2的对比度系数,1为正常 float contrastFactor = 1 + (value / 100f); ColorMatrix colorMatrix = new ColorMatrix(); colorMatrix.set(new float[]{ contrastFactor, 0, 0, 0, 0, 0, contrastFactor, 0, 0, 0, 0, 0, contrastFactor, 0, 0, 0, 0, 0, 1, 0 }); Paint paint = new Paint(); paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix)); canvas.drawBitmap(image, 0, 0, paint); // 回收原Bitmap避免内存泄漏 image.recycle(); return adjustedImage; }
2. 裁剪百分比可能为0导致空Bitmap
cropPercentage = random.nextInt(MAX_CROP_PERCENTAGE)会生成0到MAX_CROP_PERCENTAGE-1的随机数,当值为0时,cropSize为0,创建的Bitmap是0x0的空图,显示为黑色。
修复方案:
确保裁剪百分比至少为一个最小值(比如50%):
// 生成50%到MAX_CROP_PERCENTAGE的随机裁剪比例 int cropPercentage = 50 + random.nextInt(MAX_CROP_PERCENTAGE - 50);
或者直接限制最小值:
int cropPercentage = random.nextInt(MAX_CROP_PERCENTAGE); // 确保裁剪比例不低于10% cropPercentage = Math.max(cropPercentage, 10);
3. 色相调整方法错误
原代码中连续调用colorMatrix.setRotate()三次,每次调用都会覆盖整个矩阵,最终只有最后一次对蓝色通道的旋转生效,这不是正确的色相调整方式,会导致颜色异常甚至黑色。
修复方案:
使用正确的色相旋转矩阵(基于YIQ颜色空间转换):
private Bitmap adjustHue(Bitmap image, float hue) { Bitmap adjustedImage = Bitmap.createBitmap(image.getWidth(), image.getHeight(), image.getConfig()); Canvas canvas = new Canvas(adjustedImage); hue = hue % 360.0f; hue = (float) (hue * Math.PI / 180.0); float cosVal = (float) Math.cos(hue); float sinVal = (float) Math.sin(hue); float lumR = 0.213f; float lumG = 0.715f; float lumB = 0.072f; ColorMatrix colorMatrix = new ColorMatrix(); colorMatrix.set(new float[]{ lumR + cosVal * (1 - lumR) + sinVal * (-lumR), lumG + cosVal * (-lumG) + sinVal * (-lumG), lumB + cosVal * (-lumB) + sinVal * (1 - lumB), 0, 0, lumR + cosVal * (-lumR) + sinVal * (0.143f), lumG + cosVal * (1 - lumG) + sinVal * (0.140f), lumB + cosVal * (-lumB) + sinVal * (-0.283f), 0, 0, lumR + cosVal * (-lumR) + sinVal * (-(1 - lumR)), lumG + cosVal * (-lumG) + sinVal * (lumG), lumB + cosVal * (1 - lumB) + sinVal * (lumB), 0, 0, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 0f, 0f, 1f }); Paint paint = new Paint(); paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix)); canvas.drawBitmap(image, 0, 0, paint); image.recycle(); return adjustedImage; }
4. 通过Intent传递Bitmap易导致数据损坏
Bitmap是重量级对象,Intent的Parcelable传输有大小限制(通常约1MB),大Bitmap会被截断或丢失,导致接收端显示黑色。
修复方案:
方案一:使用临时文件存储Bitmap
在applyRandomTransformations中将处理后的Bitmap保存到应用缓存目录,然后传递文件路径列表:
// 保存Bitmap到临时文件 private String saveBitmapToTempFile(Bitmap bitmap) throws IOException { File cacheDir = getCacheDir(); File tempFile = File.createTempFile("edited_image", ".png", cacheDir); FileOutputStream fos = new FileOutputStream(tempFile); bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos); fos.close(); bitmap.recycle(); return tempFile.getAbsolutePath(); } // 修改applyRandomTransformations private void applyRandomTransformations() { Random random = new Random(); List<String> editedImagePaths = new ArrayList<>(); // 存储文件路径 for (Uri imageUri : originalImageUris) { try { Bitmap originalImage = MediaStore.Images.Media.getBitmap(getContentResolver(), imageUri); if (originalImage.getWidth() <= 0) continue; // ... 原有变换代码 ... // 替换editedImages.add(finalImage)为: String filePath = saveBitmapToTempFile(finalImage); editedImagePaths.add(filePath); } catch (IOException e) { e.printStackTrace(); } } } // 修改displayEditedImages private void displayEditedImages(List<String> editedImagePaths) { if (!editedImagePaths.isEmpty()) { Intent intent = new Intent(MainActivity.this, EditedImagesActivity.class); intent.putStringArrayListExtra("editedImagePaths", new ArrayList<>(editedImagePaths)); startActivity(intent); } }
在EditedImagesActivity中读取文件路径并加载Bitmap:
List<String> paths = getIntent().getStringArrayListExtra("editedImagePaths"); // 加载Bitmap时: Bitmap bitmap = BitmapFactory.decodeFile(path);
方案二:使用全局单例持有Bitmap
创建一个单例类存储处理后的Bitmap列表,注意在使用后及时清理避免内存泄漏:
public class ImageCache { private static ImageCache instance; private List<Bitmap> editedImages; private ImageCache() {} public static ImageCache getInstance() { if (instance == null) { instance = new ImageCache(); } return instance; } public void setEditedImages(List<Bitmap> images) { this.editedImages = images; } public List<Bitmap> getEditedImages() { return editedImages; } public void clear() { if (editedImages != null) { for (Bitmap bmp : editedImages) { if (!bmp.isRecycled()) { bmp.recycle(); } } editedImages.clear(); } } }
在applyRandomTransformations中设置:
ImageCache.getInstance().setEditedImages(editedImages);
在EditedImagesActivity中获取:
List<Bitmap> editedImages = ImageCache.getInstance().getEditedImages(); // 在Activity销毁时清理 @Override protected void onDestroy() { super.onDestroy(); ImageCache.getInstance().clear(); }
5. 回收中间Bitmap避免内存溢出
每次变换都会生成新的Bitmap,原Bitmap未回收会导致内存不足,系统可能回收Bitmap资源导致显示黑色。
修复方案:
在每个变换方法中回收输入的Bitmap:
// 示例:rotateImage方法修改 private Bitmap rotateImage(Bitmap image, float angle) { Matrix matrix = new Matrix(); matrix.postRotate(angle); Bitmap rotatedImage = Bitmap.createBitmap(image, 0, 0, image.getWidth(), image.getHeight(), matrix, true); image.recycle(); // 回收原Bitmap return rotatedImage; }
同理,在crop、contrast、hue、saturation方法中都添加image.recycle();。
内容的提问来源于stack exchange,提问作者Alex

