Canvas异常问题:Bitmap在真机上自行旋转90度
Hey there! That’s a super common Android gotcha—real devices often respect EXIF orientation metadata that emulators ignore, leading to unexpected rotation when working with Bitmaps and Canvas. Let’s break down why this happens and how to fix it:
Why This Occurs
When you get a Bitmap (e.g., from a camera, gallery, or another screen), it might have an EXIF tag specifying its intended orientation (like 90 degrees clockwise). Emulators sometimes skip processing this tag, so your code works as expected there. But real devices apply this orientation, making it look like the Bitmap rotated on its own when you use it with Canvas.
Your current code copies and scales the Bitmap but doesn’t adjust the pixel data to match the EXIF orientation—so the underlying pixel data stays in the original (rotated) state, and the device’s system applies the EXIF rotation on top, causing the mismatch.
Step 1: Read the EXIF Orientation
First, grab the orientation value from the original image’s EXIF data. If you have the file path of the source image, use this helper method:
int getExifOrientation(String imagePath) { ExifInterface exif = null; try { exif = new ExifInterface(imagePath); } catch (IOException e) { e.printStackTrace(); } if (exif == null) return ExifInterface.ORIENTATION_NORMAL; return exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); }
If you don’t have the file path, make sure the previous page passes both the Bitmap and its orientation (since Bitmaps don’t store EXIF data directly).
Step 2: Rotate the Bitmap to Match the Orientation
Use this helper method to adjust the Bitmap’s pixel data to match the EXIF orientation:
Bitmap rotateBitmap(Bitmap bitmap, int orientation) { Matrix matrix = new Matrix(); switch (orientation) { case ExifInterface.ORIENTATION_NORMAL: return bitmap; case ExifInterface.ORIENTATION_FLIP_HORIZONTAL: matrix.setScale(-1, 1); break; case ExifInterface.ORIENTATION_ROTATE_180: matrix.setRotate(180); break; case ExifInterface.ORIENTATION_FLIP_VERTICAL: matrix.setRotate(180); matrix.postScale(-1, 1); break; case ExifInterface.ORIENTATION_TRANSPOSE: matrix.setRotate(90); matrix.postScale(-1, 1); break; case ExifInterface.ORIENTATION_ROTATE_90: matrix.setRotate(90); break; case ExifInterface.ORIENTATION_TRANSVERSE: matrix.setRotate(-90); matrix.postScale(-1, 1); break; case ExifInterface.ORIENTATION_ROTATE_270: matrix.setRotate(-90); break; default: return bitmap; } try { Bitmap rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); bitmap.recycle(); // Free up memory from the original bitmap return rotatedBitmap; } catch (OutOfMemoryError e) { e.printStackTrace(); return bitmap; } }
Step 3: Update Your Canvas Logic
Modify your code to apply the rotation before creating the Canvas:
// Get orientation from the original image (adjust this to your source) int orientation = getExifOrientation(originalImagePath); // Copy and scale the bitmap as before mBitmap = backgroundPicture.copy(Bitmap.Config.ARGB_8888, true); mBitmap = Bitmap.createScaledBitmap(mBitmap, width, height, true); // Rotate to correct orientation mBitmap = rotateBitmap(mBitmap, orientation); // Now create your Canvas with the properly oriented bitmap mCanvas = new Canvas(mBitmap);
Alternative: Adjust the Canvas Matrix (Memory-Friendly)
If you want to avoid creating a new Bitmap, adjust the Canvas’s matrix to compensate for the orientation. This way, all your drawing operations will automatically align correctly:
// Get orientation int orientation = getExifOrientation(originalImagePath); // Create Canvas with the original scaled bitmap mCanvas = new Canvas(mBitmap); // Apply rotation to the Canvas Matrix canvasMatrix = new Matrix(); switch (orientation) { case ExifInterface.ORIENTATION_ROTATE_90: canvasMatrix.postRotate(90, mBitmap.getWidth()/2, mBitmap.getHeight()/2); break; case ExifInterface.ORIENTATION_ROTATE_270: canvasMatrix.postRotate(-90, mBitmap.getWidth()/2, mBitmap.getHeight()/2); break; // Add other orientation cases if needed } mCanvas.setMatrix(canvasMatrix);
Why Emulators Work
Emulators often skip processing EXIF orientation metadata, so they display the Bitmap’s raw pixel data directly. Real devices, however, respect these tags, leading to the rotation mismatch you’re seeing.
内容的提问来源于stack exchange,提问作者Matthieu Meunier

