安卓拍照或选择的图片自动旋转处理
在安卓开发中,经常使用相机或是选择照片的功能,但是在部分手机上拍照或选择图片后,发现图片被自动旋转了。那我们怎么处理呢?相信这个不难,很多人都能轻易想到。下面是如果是H5调用原生相机获取照片,需要原生将图片存储后继续返回Uri路径。
处理步骤如下:
- 根据拍照或选择照片的Uri获取图片Path
- 根据图片Path获取其EXIF信息,包括图片旋转的角度
- 将URI转换成Bitmap对象
- 对Bitmap对象做旋转处理
- 将旋转的Bitmap存储,然后获取存储后的Uri
1、根据Uri获取图片绝对路径
/**
* 根据Uri返回文件绝对路径
* 兼容了file:///开头的 和 content://开头的情况
* @param context
* @param uri
* @return
*/
public static String getRealFilePathFromUri(final Context context, final Uri uri) {
if (null == uri) {
return null;
}
final String scheme = uri.getScheme();
String data = null;
if (scheme == null) {
data = uri.getPath();
} else if (ContentResolver.SCHEME_FILE.equalsIgnoreCase(scheme)) {
data = uri.getPath();
} else if (ContentResolver.SCHEME_CONTENT.equalsIgnoreCase(scheme)) {
Cursor cursor = context.getContentResolver().query(uri, new String[]{MediaStore.Images.ImageColumns.DATA}, null, null, null);
if (null != cursor) {
if (cursor.moveToFirst()) {
int index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
if (index > -1) {
data = cursor.getString(index);
}
}
cursor.close();
}
}
return data;
}
2、根据图片Path获取其EXIF信息,包括图片旋转的角度
/**
* 读取图片的旋转的角度
*
* @param path
* 图片绝对路径
* @return 图片的旋转角度
*/
private int getBitmapDegree(String path) {
int degree = 0;//被旋转的角度
try {
// 从指定路径下读取图片,并获取其EXIF信息
ExifInterface exifInterface = new ExifInterface(path);
// 获取图片的旋转信息
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
degree = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
degree = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
degree = 270;
break;
}
} catch (IOException e) {
e.printStackTrace();
}
return degree;
}
3、将URI转换成Bitmap对象
a) 获取图片的宽高
public static int[] getImageWidthHeight(Context context, Uri uri) {
BitmapFactory.Options options = new BitmapFactory.Options();
//最关键在此,把options.inJustDecodeBounds = true;
//这里再decodeFile(),返回的bitmap为空,但此时调用options.outHeight时,已经包含了图片的高了
options.inJustDecodeBounds = true;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
try {
ParcelFileDescriptor parcelFileDescriptor = context.getContentResolver().openFileDescriptor(uri, "r");
FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
parcelFileDescriptor.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
} else {
Bitmap bitmap = BitmapFactory.decodeFile(FileUtil.getRealFilePathFromUri(context, uri), options); // 此时返回的bitmap为null
}
//options.outHeight为原始图片的高
return new int[]{options.outWidth, options.outHeight};
}
b)获取Bitmap图片
/**
* 图片等比例压缩
*
* @param uri
* @param reqWidth 期望的宽
* @param reqHeight 期望的高
* @return
*/
public static Bitmap decodeSampledBitmap(Context context, Uri uri, int reqWidth,
int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
options.inPreferredConfig = Bitmap.Config.RGB_565;
//bitmap is null
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
try {
ParcelFileDescriptor parcelFileDescriptor = context.getContentResolver().openFileDescriptor(uri, "r");
FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
// Calculate inSampleSize
int inSampleSize = calculateInSampleSize(options, reqWidth,
reqHeight);
Log.i(TAG, "decodeSampledBitmap: inSampleSize = " + inSampleSize);
options.inSampleSize = inSampleSize;
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
} else {
Bitmap bitmap = BitmapFactory.decodeFile(FileUtil.getRealFilePathFromUri(context, uri), options);
// Calculate inSampleSize
int inSampleSize = calculateInSampleSize(options, reqWidth,
reqHeight);
Log.i(TAG, "decodeSampledBitmap: inSampleSize = " + inSampleSize);
options.inSampleSize = inSampleSize;
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(FileUtil.getRealFilePathFromUri(context, uri), options);
}
return null;
}
4 、对Bitmap对象做旋转处理
/**
* 将图片按照某个角度进行旋转
* @param bm 需要旋转的图片
* @param degree 旋转角度
* @return 旋转后的图片
*/
private Bitmap rotateBitmapByDegree(Bitmap bm, int degree) {
Bitmap returnBm = null;
// 根据旋转角度,生成旋转矩阵
Matrix matrix = new Matrix();
matrix.postRotate(degree);
try {
// 将原始图片按照旋转矩阵进行旋转,并得到新的图片
returnBm = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true);
} catch (OutOfMemoryError e) {
}
if (returnBm == null) {
returnBm = bm;
}
if (bm != returnBm) {
bm.recycle();
}
return returnBm;
}
5、将旋转的Bitmap存储,然后获取存储后的Uri
private Uri saveBitmapToFile(Uri singleUri,Bitmap bitmap){
try {
String path = singleUri.toString();
String fileName = path.substring(singleUri.toString().lastIndexOf("/"))+".JPEG";
File filesDir = context.getExternalFilesDir(Environment.DIRECTORY_DCIM);
File file = new File(filesDir,fileName);
FileOutputStream fos = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG,100,fos);
fos.flush();
fos.close();
return Uri.fromFile(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
将图片角度测量和旋转转图片已经储封装工具类如下:
package com.bobfintech.module_webview.util;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.net.Uri;
import android.os.Environment;
import android.text.TextUtils;
import androidx.appcompat.app.AppCompatActivity;
import androidx.exifinterface.media.ExifInterface;
import com.bobfintech.by_base.utils.BitmapUtil;
import com.bobfintech.by_base.utils.FileUtil;
import com.bobfintech.by_lib.utils.LogUtils;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* author : wdx
* time : 2021/1/4
* 计算图片角度
**/
public class BitmapDegreeHandle {
private static BitmapDegreeHandle instance;
private Context context;
private BitmapHandlerListener mListener;
private Thread mHandleThead;
private BitmapDegreeHandle(Context context){
this.context = context;
}
public static BitmapDegreeHandle init(Context context){
if (instance == null){
synchronized (BitmapDegreeHandle.class){
if (instance == null){
instance = new BitmapDegreeHandle(context);
}
}
}
return instance;
}
public BitmapDegreeHandle build(BitmapHandlerListener handle){
this.mListener = handle;
return instance;
}
public void handle(final Uri[]uris){
mHandleThead = new Thread(new Runnable() {
@Override
public void run() {
instance.unifyAllUriHandle(uris);
}
});
mHandleThead.start();
}
/**
* 读取图片的旋转的角度
*
* @param path
* 图片绝对路径
* @return 图片的旋转角度
*/
private int getBitmapDegree(String path) {
int degree = 0;//被旋转的角度
try {
// 从指定路径下读取图片,并获取其EXIF信息
ExifInterface exifInterface = new ExifInterface(path);
// 获取图片的旋转信息
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
degree = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
degree = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
degree = 270;
break;
}
} catch (IOException e) {
e.printStackTrace();
}
return degree;
}
/**
* 将图片按照某个角度进行旋转
* @param bm 需要旋转的图片
* @param degree 旋转角度
* @return 旋转后的图片
*/
private Bitmap rotateBitmapByDegree(Bitmap bm, int degree) {
Bitmap returnBm = null;
// 根据旋转角度,生成旋转矩阵
Matrix matrix = new Matrix();
matrix.postRotate(degree);
try {
// 将原始图片按照旋转矩阵进行旋转,并得到新的图片
returnBm = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true);
} catch (OutOfMemoryError e) {
}
if (returnBm == null) {
returnBm = bm;
}
if (bm != returnBm) {
bm.recycle();
}
return returnBm;
}
private Uri saveBitmapToFile(Uri singleUri,Bitmap bitmap){
try {
String path = singleUri.toString();
String fileName = path.substring(singleUri.toString().lastIndexOf("/"))+".JPEG";
File filesDir = context.getExternalFilesDir(Environment.DIRECTORY_DCIM);
File file = new File(filesDir,fileName);
FileOutputStream fos = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG,100,fos);
fos.flush();
fos.close();
return Uri.fromFile(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private Uri singleUriHandle(Uri singleUri){
String path = FileUtil.getRealFilePathFromUri(context, singleUri);
LogUtils.i("evan", "*********path******* " + path);
if (TextUtils.isEmpty(path)) {
return singleUri;
}
int degree = getBitmapDegree(path);
LogUtils.i("evan", "*********degree******* " + degree);
if (degree ==0){
return singleUri;
}
int[] imageWidthHeight = BitmapUtil.getImageWidthHeight(context, singleUri);
int w = imageWidthHeight[0];
int h = imageWidthHeight[1];
Bitmap bitmap = BitmapUtil.decodeSampledBitmap(context, singleUri, w, h);
if (bitmap == null) {
return singleUri;
}
Bitmap resutBitmap = rotateBitmapByDegree(bitmap, degree);
Uri resultUri = saveBitmapToFile(singleUri, resutBitmap);
LogUtils.i("evan", "*********resultUri******* " + resultUri);
if (resultUri == null){
return singleUri;
}
return resultUri;
}
private void unifyAllUriHandle(final Uri[] results ){
final Uri[] mUri = new Uri[results.length];
try {
for(int i=0;i<mUri.length;i++){
Uri uri = singleUriHandle(results[i]);
mUri[i] = uri;
}
}catch (Exception e){
e.printStackTrace();
}finally {
((AppCompatActivity)context).runOnUiThread(new Runnable() {
@Override
public void run() {
mListener.onSuccess(mUri);
}
});
if (mHandleThead !=null){
mHandleThead =null;
}
}
}
public interface BitmapHandlerListener{
void onSuccess(Uri[]results);
}
}
在打开相机或打开相册获取Uri后调用如下:
Uri[] results = pathList.toArray(new Uri[pathList.size()]);
BitmapDegreeHandle.init(this).build(new BitmapDegreeHandle.BitmapHandlerListener() {
@Override
public void onSuccess(Uri[] results) {
if (!isFinishing()){
uploadMessage.onReceiveValue(results);
}
uploadMessage = null;
}
}).handle(results);