You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android拍照后如何自动将图片居中裁剪至指定尺寸

Android拍照后自动居中裁剪并覆盖原文件的实现方案

核心思路

通过Bitmap原生API实现居中裁剪逻辑:

  • 计算原图的中心裁剪区域(以短边为基准取正方形)
  • 将裁剪区域缩放至指定目标尺寸
  • 覆盖保存到原文件路径
  • 通知媒体库更新文件信息

步骤1:添加图片裁剪工具类

新建ImageCropper.java文件,封装裁剪与保存逻辑:

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.util.Log;

import java.io.FileOutputStream;
import java.io.IOException;

public class ImageCropper {

    // 居中裁剪并缩放至指定尺寸,覆盖原文件
    public static boolean cropAndSaveCenter(String imagePath, int targetWidth, int targetHeight) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(imagePath, options);

        int originalWidth = options.outWidth;
        int originalHeight = options.outHeight;

        // 计算中心裁剪区域:取短边长度作为裁剪尺寸
        int cropSize = Math.min(originalWidth, originalHeight);
        int cropX = (originalWidth - cropSize) / 2;
        int cropY = (originalHeight - cropSize) / 2;

        // 计算采样率,避免加载超大图导致OOM
        options.inJustDecodeBounds = false;
        options.inSampleSize = calculateInSampleSize(options, cropSize, cropSize);
        Bitmap originalBitmap = BitmapFactory.decodeFile(imagePath, options);

        if (originalBitmap == null) {
            Log.e("ImageCropper", "解码原图失败");
            return false;
        }

        // 裁剪中心区域
        Bitmap croppedBitmap = Bitmap.createBitmap(
                originalBitmap,
                cropX / options.inSampleSize,
                cropY / options.inSampleSize,
                cropSize / options.inSampleSize,
                cropSize / options.inSampleSize
        );
        originalBitmap.recycle(); // 释放原图内存

        // 缩放至目标尺寸
        Matrix matrix = new Matrix();
        float scaleX = (float) targetWidth / croppedBitmap.getWidth();
        float scaleY = (float) targetHeight / croppedBitmap.getHeight();
        matrix.postScale(scaleX, scaleY);
        Bitmap scaledBitmap = Bitmap.createBitmap(croppedBitmap, 0, 0, croppedBitmap.getWidth(), croppedBitmap.getHeight(), matrix, true);
        croppedBitmap.recycle(); // 释放裁剪图内存

        // 覆盖保存原文件
        try (FileOutputStream fos = new FileOutputStream(imagePath)) {
            scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 90, fos);
            scaledBitmap.recycle();
            return true;
        } catch (IOException e) {
            Log.e("ImageCropper", "保存裁剪图失败", e);
            scaledBitmap.recycle();
            return false;
        }
    }

    // 计算图片采样率,降低内存占用
    private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {
            final int halfHeight = height / 2;
            final int halfWidth = width / 2;

            while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) {
                inSampleSize *= 2;
            }
        }
        return inSampleSize;
    }
}

步骤2:修改MainActivity的拍照回调

在onActivityResult中调用裁剪工具类,处理完成后更新UI与媒体库:

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if(requestCode == CAMERA_REQUEST_CODE){
        if(resultCode == Activity.RESULT_OK){
            File f = new File(currentPhotoPath);
            
            // 执行居中裁剪,目标尺寸456x456
            boolean cropSuccess = ImageCropper.cropAndSaveCenter(currentPhotoPath, 456, 456);
            
            if (cropSuccess) {
                selectedImage.setImageURI(Uri.fromFile(f));
                Log.d("tag", "裁剪完成,文件路径:" + Uri.fromFile(f));
                
                // 通知媒体库更新文件索引
                Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
                Uri contentUri = Uri.fromFile(f);
                mediaScanIntent.setData(contentUri);
                this.sendBroadcast(mediaScanIntent);
            } else {
                Toast.makeText(this, "图片裁剪失败", Toast.LENGTH_SHORT).show();
                // 裁剪失败时显示原图
                selectedImage.setImageURI(Uri.fromFile(f));
            }
        }
    }
}

关键注意事项

  • 内存优化:通过inSampleSize采样率避免加载高分辨率原图导致内存溢出
  • 权限兼容:Android 10及以上使用公共目录需保持WRITE_EXTERNAL_STORAGE权限申请
  • 文件备份:若需保留原图,可在裁剪前将原文件复制到其他路径
  • 异常处理:代码包含基础错误捕获,实际开发可根据需求补充重试或提示逻辑

内容的提问来源于stack exchange,提问作者cherry blossom

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 16:00:20