You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android中通过URI获取竖拍图片真实宽高异常的解决方法求助

如何通过URI稳健获取图片真实宽高(解决竖屏照片宽高互换问题)

这个问题我之前也碰到过,核心原因是图片的Exif方向标记在搞鬼!

问题根源

手机竖屏拍摄时,相机的传感器其实还是保持横置状态记录像素数据,但会在图片的Exif元数据里添加一个Orientation标记(比如常见的ORIENTATION_ROTATE_90,代表需要顺时针旋转90度才能正确显示)。而你用的BitmapFactory.decodeStream方法只会读取图片原始像素的宽高,不会自动根据Exif方向调整,所以拿到的是传感器记录的原始尺寸,和图片查看器里显示的“视觉真实宽高”刚好互换了。

稳健解决方案:结合Exif方向调整宽高

要获取和图片查看器一致的真实宽高,需要两步:读取Exif方向标记,再根据标记调整原始宽高。

1. 读取图片的Exif方向

下面的方法兼容Android 7.0及以上(直接通过InputStream读取Exif)和低版本(需要先将URI转为文件路径):

private int getExifOrientation(Context context, Uri imageUri) {
    int orientation = ExifInterface.ORIENTATION_NORMAL;
    try {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            // Android 7.0+ 直接通过InputStream读取Exif
            try (InputStream inputStream = context.getContentResolver().openInputStream(imageUri)) {
                ExifInterface exif = new ExifInterface(inputStream);
                orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
            }
        } else {
            // 低版本需要先将content:// URI转为文件路径
            String imagePath = getPathFromUri(context, imageUri);
            if (imagePath != null) {
                ExifInterface exif = new ExifInterface(imagePath);
                orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    return orientation;
}

// 辅助方法:将content:// URI转换为文件路径(适配低版本)
private String getPathFromUri(Context context, Uri uri) {
    String path = null;
    if (DocumentsContract.isDocumentUri(context, uri)) {
        String docId = DocumentsContract.getDocumentId(uri);
        if ("com.android.providers.media.documents".equals(uri.getAuthority())) {
            String id = docId.split(":")[1];
            String selection = MediaStore.Images.Media._ID + "=" + id;
            path = queryImagePath(context, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection);
        } else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())) {
            Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.parseLong(docId));
            path = queryImagePath(context, contentUri, null);
        }
    } else if ("content".equalsIgnoreCase(uri.getScheme())) {
        path = queryImagePath(context, uri, null);
    } else if ("file".equalsIgnoreCase(uri.getScheme())) {
        path = uri.getPath();
    }
    return path;
}

private String queryImagePath(Context context, Uri uri, String selection) {
    String path = null;
    Cursor cursor = context.getContentResolver().query(uri, null, selection, null, null);
    if (cursor != null) {
        if (cursor.moveToFirst()) {
            path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
        }
        cursor.close();
    }
    return path;
}

2. 根据Exif方向调整宽高

拿到方向标记后,判断哪些情况需要互换宽高:

// 1. 先获取图片原始像素的宽高
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmapOptions.inJustDecodeBounds = true;
try (InputStream inputStream = context.getContentResolver().openInputStream(imageUri)) {
    BitmapFactory.decodeStream(inputStream, null, bitmapOptions);
} catch (FileNotFoundException e) {
    // 处理文件未找到异常
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}
int originalWidth = bitmapOptions.outWidth;
int originalHeight = bitmapOptions.outHeight;

// 2. 获取Exif方向
int exifOrientation = getExifOrientation(context, imageUri);

// 3. 根据方向调整得到真实视觉宽高
int realWidth, realHeight;
switch (exifOrientation) {
    case ExifInterface.ORIENTATION_ROTATE_90:
    case ExifInterface.ORIENTATION_ROTATE_270:
        // 这两个方向下,视觉宽高和原始像素宽高互换
        realWidth = originalHeight;
        realHeight = originalWidth;
        break;
    default:
        // 其他方向(正常、翻转等)不需要互换
        realWidth = originalWidth;
        realHeight = originalHeight;
        break;
}

// 现在realWidth和realHeight就是图片查看器中显示的真实宽高

额外提示

如果之后需要加载并显示图片,记得同样要根据Exif方向旋转Bitmap,否则图片会显示异常。可以用Matrix来处理旋转:

Matrix matrix = new Matrix();
switch (exifOrientation) {
    case ExifInterface.ORIENTATION_ROTATE_90:
        matrix.postRotate(90);
        break;
    case ExifInterface.ORIENTATION_ROTATE_180:
        matrix.postRotate(180);
        break;
    case ExifInterface.ORIENTATION_ROTATE_270:
        matrix.postRotate(270);
        break;
}
Bitmap rotatedBitmap = Bitmap.createBitmap(originalBitmap, 0, 0, originalBitmap.getWidth(), originalBitmap.getHeight(), matrix, true);

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

火山引擎 最新活动