While developing a new update for Android application (Notebook With Categories), we faced the following issue: when a user makes a photo (snapshot) under landscape mode, the app takes this picture in portrait under grid view which is not good for image preview. This error was not common, it occurred only on some devices. We managed to solve that issue via adding a Rotate Image button. We took some research and found out that there were no clear steps described by someone else explaining how to fix that, so here we are happy to share how we handled that. In short, this article will explain how to rotate a bitmap in Android. By the end, you will understand how to rotate the image in Java. One thing should be noted – due to a canvas restrictions, it is not really possible to rotate the image by an angle other than 90 degrees, not even 45.
Step 1. Create a new screen. The screen has a flat color background, Upload Image button and Rotate Image button (button which Rotates the image by 90 degrees). Rotate Image button will rotate Image on Button Click. By the button click, we imply the stroke into a center of the button.
Create MainActivity.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity" > <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginTop="5dp" android:orientation="horizontal" > <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Load Image" android:id="@+id/btn_loadImg" android:layout_weight="1" /> <Button android:id="@+id/btn_RotateImg" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" android:enabled="false" android:text="Rotate Image" /> </LinearLayout> <ScrollView android:id="@+id/scrollView1" android:layout_width="match_parent" android:layout_height="wrap_content" > <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center" android:orientation="vertical" > <ImageView android:id="@+id/imageView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:background="@drawable/ic_launcher" /> </LinearLayout> </ScrollView> </LinearLayout> |
Step 2. Add permission to Manifest file for writing to external storage:
1 |
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> |
Step 3. Edit MainActivity.java file.
Add the function of the screen button for taking image from gallery:
1 2 3 4 5 6 |
public void loadBtnClick(View viev){ Intent intent = new Intent(); intent.setType("image/*"); intent.setAction(Intent.ACTION_GET_CONTENT); startActivityForResult(Intent.createChooser(intent, "Complete action using"), PICK_FROM_FILE); } |
Here we retrieve image path:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
protected void onActivityResult (int requestCode,int resultCode,Intent data){ if (resultCode != RESULT_OK) return; String path = ""; if (requestCode == PICK_FROM_FILE) { Uri mImageCaptureUri = data.getData(); path = getRealPathFromURI (mImageCaptureUri); if (path == null) { path = mImageCaptureUri.getPath(); //from File Manager } Log.e("path", path); pathImage = path; imageView.setImageBitmap(getBitmap(path)) ; } } |
Let’s look at the screen size in order to make the image fit the screen size:
1 2 3 4 5 |
DisplayMetrics displaymetrics; displaymetrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(displaymetrics); int screenWidth = displaymetrics.widthPixels; int screenHeight = displaymetrics.heightPixels; |
Image processing to define image mode (landscape or portrait):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
@SuppressLint("NewApi") private Bitmap getBitmap(String path) { Log.e("inside of", "getBitmap = "+path); try { Bitmap b = null; BitmapFactory.Options o = new BitmapFactory.Options(); o.inJustDecodeBounds = true; Matrix matrix = new Matrix(); ExifInterface exifReader = new ExifInterface(path); int orientation = exifReader.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1); int rotate = 0; if (orientation ==ExifInterface.ORIENTATION_NORMAL) { // Do nothing. The original image is fine. } else if (orientation == ExifInterface.ORIENTATION_ROTATE_90) { rotate = 90; } else if (orientation == ExifInterface.ORIENTATION_ROTATE_180) { rotate = 180; } else if (orientation == ExifInterface.ORIENTATION_ROTATE_270) { rotate = 270; } matrix.postRotate(rotate); try { b = loadBitmap(path, rotate, screenWidth, screenHeight); btn_RotateImg.setEnabled(true); } catch (OutOfMemoryError e) { btn_RotateImg.setEnabled(false); } System.gc(); return b; } catch (Exception e) { Log.e("my tag", e.getMessage(), e); return null; } } |
Insert the image from image file, set orientation and size of screen:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
public static Bitmap loadBitmap(String path, int orientation, final int targetWidth, final int targetHeight) { Bitmap bitmap = null; try { final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(path, options); int sourceWidth, sourceHeight; if (orientation == 90 || orientation == 270) { sourceWidth = options.outHeight; sourceHeight = options.outWidth; } else { sourceWidth = options.outWidth; sourceHeight = options.outHeight; } if (sourceWidth > targetWidth || sourceHeight > targetHeight) { float widthRatio = (float)sourceWidth / (float)targetWidth; float heightRatio = (float)sourceHeight / (float)targetHeight; float maxRatio = Math.max(widthRatio, heightRatio); options.inJustDecodeBounds = false; options.inSampleSize = (int)maxRatio; bitmap = BitmapFactory.decodeFile(path, options); } else { bitmap = BitmapFactory.decodeFile(path); } if (orientation > 0) { Matrix matrix = new Matrix(); matrix.postRotate(orientation); bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); } sourceWidth = bitmap.getWidth(); sourceHeight = bitmap.getHeight(); if (sourceWidth != targetWidth || sourceHeight != targetHeight) { float widthRatio = (float)sourceWidth / (float)targetWidth; float heightRatio = (float)sourceHeight / (float)targetHeight; float maxRatio = Math.max(widthRatio, heightRatio); sourceWidth = (int)((float)sourceWidth / maxRatio); sourceHeight = (int)((float)sourceHeight / maxRatio); bitmap = Bitmap.createScaledBitmap(bitmap, sourceWidth, sourceHeight, true); } } catch (Exception e) { } return bitmap; } |
Set event function for Android rotate button:
1 2 3 |
public void rotateBtnclick(View viev){ rotateImage (pathImage); } |
Look at the orientation of image and rotate the image for 90 degrees and then again upload it to ImageView:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
public void rotateImage(String path){ File file = new File(path); ExifInterface exifInterface = null; try { exifInterface = new ExifInterface(file.getPath()); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); if ( (orientation == ExifInterface.ORIENTATION_NORMAL) | (orientation == 0) ) { exifInterface.setAttribute(ExifInterface.TAG_ORIENTATION, ""+ExifInterface.ORIENTATION_ROTATE_90); } else if (orientation == ExifInterface.ORIENTATION_ROTATE_90) { exifInterface.setAttribute(ExifInterface.TAG_ORIENTATION, ""+ExifInterface.ORIENTATION_ROTATE_180); } else if (orientation == ExifInterface.ORIENTATION_ROTATE_180) { exifInterface.setAttribute(ExifInterface.TAG_ORIENTATION, ""+ExifInterface.ORIENTATION_ROTATE_270); } else if (orientation == ExifInterface.ORIENTATION_ROTATE_270) { exifInterface.setAttribute(ExifInterface.TAG_ORIENTATION, ""+ExifInterface.ORIENTATION_NORMAL); } try { exifInterface.saveAttributes(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } imageView.setImageBitmap(getBitmap(path)) ; } |
The repeated button presses will cyclically change the image orientation from 0 degrees to 90 degrees to 180 degrees to 270 degrees and to 0 degrees again. By the use of the RotateAnimation method, it is also possible to implement rotate animation.
Source code. Source code is available on github.
Read also:
• How to Choose a Mobile Development Platform?