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




