日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android进阶之路 - 解决部分手机拍照之后图片被旋转的问题

發布時間:2024/3/13 Android 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android进阶之路 - 解决部分手机拍照之后图片被旋转的问题 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

這幾天犯了一個錯誤,初期想著甩鍋給后臺的… 但還好及時發現了是自身的問題~

關聯文章

  • Android基礎進階 - 調用拍照、獲取圖片(基礎)
  • Android基礎進階 - 獲取、調用相冊內圖片(基礎)
  • Android進階之路 - 拍照、相冊選圖
  • Android進階之路 - 解決部分手機拍照之后圖片被旋轉的問題
  • Android進階之路 - Uri、Path、File、Bitmap 轉換方式

Hint:底部擴展了騰訊某個項目內用到的ImageUtil,可以很好的解決圖片旋轉、壓縮、保存的問題

      • 拍照、相冊、圖片回傳
      • 野生 PhotoBitmapUtils
      • 騰訊 ImageUtil

整篇思考

  • 產生問題:通過拍照或相冊回傳的照片,直接傳入后臺,在用于展示的時候,照片角度出現問題,均不規則的旋轉了90度,從而導致體驗效果變差!

  • 問題思考:后臺一般都是你怎么存的,它怎么給你拿出來!所以在這里就可以發現問題本身是由我們前端造成的!

  • 覆蓋范圍:圖片被旋轉的情況并不是百分百出現在每個機型上,只是會出現在某一部分機型之上,但是本著兼容的原則,我們只有逐個處理

  • 解決方法:找了大約五篇博文左右,我找到了最懶也是最簡單有效的方法,主要來自此篇文章,之所以沒有轉載而且署名原創的原因是因為更全面記錄自己遇到的問題、場景、思路~

拍照、相冊、圖片回傳

主要代碼部分,已全局進行了注釋 (os:我想不會有比我還笨的人,加油把~)

  • 拍照
/*** 拍照*/private void takePhoto() {//拍照時存儲的照片路徑,最好提為全局變量,因為成功回傳后需要使用到~String photoPath = Environment.getExternalStorageDirectory().getPath() + "/picture_dow_" + SystemClock.currentThreadTimeMillis() + ".jpg";Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);intent.putExtra(MediaStore.EXTRA_OUTPUT, FileProvider.getUriForFile(InfoActivity.this, "com.bakheet.garage.fileprovider", sdcardTempFile));} else {intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(sdcardTempFile));}startActivityForResult(intent, 1);}
  • 相冊選取
/*** 從相冊選取照片*/private void selectFromGallery() {Intent intent = new Intent();intent.setAction(Intent.ACTION_PICK);intent.setType("image/*");startActivityForResult(intent, 2);}
  • 重寫onActivityResult(此時照片已經回傳,我們在這里進行圖片處理)
@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);switch (requestCode) {//拍照回傳case 1:if (resultCode == RESULT_OK && sdcardTempFile.exists()) {//主要通過PhotoBitmapUtils內的amendRotatePhoto方法保存我們拍照時的照片狀態String newPath = PhotoBitmapUtils.amendRotatePhoto(photoPath, this);//此時的圖片file就是正確的了~final File newSdcardTempFile = new File(newPath);//這個方法是用于傳入后臺的,可忽略!!!uploadImage(newSdcardTempFile);}break;//相冊回傳case 2:ContentResolver contentResolver = getContentResolver();if (data != null) {//圖片的UUri uri = data.getData();if (uri!=null){//使用工具類獲取絕對路徑String path = ToolUtil.getFilePathFromContentUri(uri, contentResolver);if (resultCode == RESULT_OK && sdcardTempFile.exists()) {//主要通過PhotoBitmapUtils內的amendRotatePhoto方法保存我們的照片狀態String newPath = PhotoBitmapUtils.amendRotatePhoto(photoPath, this);//此時的圖片file就是正確的了~final File newSdcardTempFile = new File(newPath);//這個方法是用于傳入后臺的,可忽略!!!uploadImage(newSdcardTempFile);}}}break;default:break;}}

野生 PhotoBitmapUtils

(主要工具類):

/*** 集合一些圖片工具** Created by zhuwentao on 2016-07-22.*/ public class PhotoBitmapUtils {/*** 存放拍攝圖片的文件夾*/private static final String FILES_NAME = "/MyPhoto";/*** 獲取的時間格式*/public static final String TIME_STYLE = "yyyyMMddHHmmss";/*** 圖片種類*/public static final String IMAGE_TYPE = ".png";// 防止實例化private PhotoBitmapUtils() {}/*** 獲取手機可存儲路徑** @param context 上下文* @return 手機可存儲路徑*/private static String getPhoneRootPath(Context context) {// 是否有SD卡if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)|| !Environment.isExternalStorageRemovable()) {// 獲取SD卡根目錄return context.getExternalCacheDir().getPath();} else {// 獲取apk包下的緩存路徑return context.getCacheDir().getPath();}}/*** 使用當前系統時間作為上傳圖片的名稱** @return 存儲的根路徑+圖片名稱*/public static String getPhotoFileName(Context context) {File file = new File(getPhoneRootPath(context) + FILES_NAME);// 判斷文件是否已經存在,不存在則創建if (!file.exists()) {file.mkdirs();}// 設置圖片文件名稱SimpleDateFormat format = new SimpleDateFormat(TIME_STYLE, Locale.getDefault());Date date = new Date(System.currentTimeMillis());String time = format.format(date);String photoName = "/" + time + IMAGE_TYPE;return file + photoName;}/*** 保存Bitmap圖片在SD卡中* 如果沒有SD卡則存在手機中** @param mbitmap 需要保存的Bitmap圖片* @return 保存成功時返回圖片的路徑,失敗時返回null*/public static String savePhotoToSD(Bitmap mbitmap, Context context) {FileOutputStream outStream = null;String fileName = getPhotoFileName(context);try {outStream = new FileOutputStream(fileName);// 把數據寫入文件,100表示不壓縮mbitmap.compress(Bitmap.CompressFormat.PNG, 100, outStream);return fileName;} catch (Exception e) {e.printStackTrace();return null;} finally {try {if (outStream != null) {// 記得要關閉流!outStream.close();}if (mbitmap != null) {mbitmap.recycle();}} catch (Exception e) {e.printStackTrace();}}}/*** 把原圖按1/10的比例壓縮** @param path 原圖的路徑* @return 壓縮后的圖片*/public static Bitmap getCompressPhoto(String path) {BitmapFactory.Options options = new BitmapFactory.Options();options.inJustDecodeBounds = false;options.inSampleSize = 10; // 圖片的大小設置為原來的十分之一Bitmap bmp = BitmapFactory.decodeFile(path, options);options = null;return bmp;}/*** 處理旋轉后的圖片* @param originpath 原圖路徑* @param context 上下文* @return 返回修復完畢后的圖片路徑*/public static String amendRotatePhoto(String originpath, Context context) {// 取得圖片旋轉角度int angle = readPictureDegree(originpath);// 把原圖壓縮后得到Bitmap對象Bitmap bmp = getCompressPhoto(originpath);;// 修復圖片被旋轉的角度Bitmap bitmap = rotaingImageView(angle, bmp);// 保存修復后的圖片并返回保存后的圖片路徑return savePhotoToSD(bitmap, context);}/*** 讀取照片旋轉角度** @param path 照片路徑* @return 角度*/public static int readPictureDegree(String path) {int degree = 0;try {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 angle 被旋轉角度* @param bitmap 圖片對象* @return 旋轉后的圖片*/public static Bitmap rotaingImageView(int angle, Bitmap bitmap) {Bitmap returnBm = null;// 根據旋轉角度,生成旋轉矩陣Matrix matrix = new Matrix();matrix.postRotate(angle);try {// 將原始圖片按照旋轉矩陣進行旋轉,并得到新的圖片returnBm = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);} catch (OutOfMemoryError e) {}if (returnBm == null) {returnBm = bitmap;}if (bitmap != returnBm) {bitmap.recycle();}return returnBm;} }

騰訊 ImageUtil

擴展 - 騰訊部分項目用到的ImageUtil

ImageUtil

package com.tencent.qcloud.tuicore.util;import android.content.Context; import android.content.SharedPreferences; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RectF; import android.media.ExifInterface; import android.net.Uri; import android.text.TextUtils; import android.util.Log;import com.tencent.qcloud.tuicore.TUIConfig; import com.tencent.qcloud.tuicore.TUILogin;import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream;public class ImageUtil {public final static String SP_IMAGE = "_conversation_group_face";/*** @param outFile 圖片的目錄路徑* @param bitmap* @return*/public static File storeBitmap(File outFile, Bitmap bitmap) {// 檢測是否達到存放文件的上限if (!outFile.exists() || outFile.isDirectory()) {outFile.getParentFile().mkdirs();}FileOutputStream fOut = null;try {outFile.deleteOnExit();outFile.createNewFile();fOut = new FileOutputStream(outFile);bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fOut);fOut.flush();} catch (IOException e1) {outFile.deleteOnExit();} finally {if (null != fOut) {try {fOut.close();} catch (IOException e) {e.printStackTrace();outFile.deleteOnExit();}}}return outFile;}public static Bitmap getBitmapFormPath(Uri uri) {Bitmap bitmap = null;try {InputStream input = TUIConfig.getAppContext().getContentResolver().openInputStream(uri);BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options();onlyBoundsOptions.inJustDecodeBounds = true;onlyBoundsOptions.inDither = true;//optionalonlyBoundsOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optionalBitmapFactory.decodeStream(input, null, onlyBoundsOptions);input.close();int originalWidth = onlyBoundsOptions.outWidth;int originalHeight = onlyBoundsOptions.outHeight;if ((originalWidth == -1) || (originalHeight == -1))return null;//圖片分辨率以480x800為標準float hh = 800f;//這里設置高度為800ffloat ww = 480f;//這里設置寬度為480fint degree = getBitmapDegree(uri);if (degree == 90 || degree == 270) {hh = 480;ww = 800;}//縮放比。由于是固定比例縮放,只用高或者寬其中一個數據進行計算即可int be = 1;//be=1表示不縮放if (originalWidth > originalHeight && originalWidth > ww) {//如果寬度大的話根據寬度固定大小縮放be = (int) (originalWidth / ww);} else if (originalWidth < originalHeight && originalHeight > hh) {//如果高度高的話根據寬度固定大小縮放be = (int) (originalHeight / hh);}if (be <= 0)be = 1;//比例壓縮BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();bitmapOptions.inSampleSize = be;//設置縮放比例bitmapOptions.inDither = true;//optionalbitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optionalinput = TUIConfig.getAppContext().getContentResolver().openInputStream(uri);bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions);input.close();compressImage(bitmap);bitmap = rotateBitmapByDegree(bitmap, degree);} catch (Exception e) {e.printStackTrace();}return bitmap;//再進行質量壓縮}public static Bitmap getBitmapFormPath(String path) {if (TextUtils.isEmpty(path)) {return null;}return getBitmapFormPath(Uri.fromFile(new File(path)));}public static Bitmap compressImage(Bitmap image) {ByteArrayOutputStream baos = new ByteArrayOutputStream();image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//質量壓縮方法,這里100表示不壓縮,把壓縮后的數據存放到baos中int options = 100;while (baos.toByteArray().length / 1024 > 100) { //循環判斷如果壓縮后圖片是否大于100kb,大于繼續壓縮baos.reset();//重置baos即清空baos//第一個參數 :圖片格式 ,第二個參數: 圖片質量,100為最高,0為最差 ,第三個參數:保存壓縮后的數據的流image.compress(Bitmap.CompressFormat.JPEG, options, baos);//這里壓縮options%,把壓縮后的數據存放到baos中options -= 10;//每次都減少10}ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//把壓縮后的數據baos存放到ByteArrayInputStream中Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);//把ByteArrayInputStream數據生成圖片return bitmap;}/*** 讀取圖片的旋轉的角度*/public static int getBitmapDegree(Uri uri) {int degree = 0;try {// 從指定路徑下讀取圖片,并獲取其EXIF信息ExifInterface exifInterface = new ExifInterface(FileUtil.getPathFromUri(uri));// 獲取圖片的旋轉信息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;}/*** 讀取圖片的旋轉的角度*/public static int getBitmapDegree(String fileName) {int degree = 0;try {// 從指定路徑下讀取圖片,并獲取其EXIF信息ExifInterface exifInterface = new ExifInterface(fileName);// 獲取圖片的旋轉信息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 旋轉后的圖片*/public static 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;}public static int[] getImageSize(String path) {int size[] = new int[2];try {BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options();onlyBoundsOptions.inJustDecodeBounds = true;BitmapFactory.decodeFile(path, onlyBoundsOptions);int originalWidth = onlyBoundsOptions.outWidth;int originalHeight = onlyBoundsOptions.outHeight;//size[0] = originalWidth;//size[1] = originalHeight;int degree = getBitmapDegree(path);if (degree == 0) {size[0] = originalWidth;size[1] = originalHeight;} else {//圖片分辨率以480x800為標準float hh = 800f;//這里設置高度為800ffloat ww = 480f;//這里設置寬度為480fif (degree == 90 || degree == 270) {hh = 480;ww = 800;}//縮放比。由于是固定比例縮放,只用高或者寬其中一個數據進行計算即可int be = 1;//be=1表示不縮放if (originalWidth > originalHeight && originalWidth > ww) {//如果寬度大的話根據寬度固定大小縮放be = (int) (originalWidth / ww);} else if (originalWidth < originalHeight && originalHeight > hh) {//如果高度高的話根據寬度固定大小縮放be = (int) (originalHeight / hh);}if (be <= 0)be = 1;BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();bitmapOptions.inSampleSize = be;//設置縮放比例bitmapOptions.inDither = true;//optionalbitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optionalBitmap bitmap = BitmapFactory.decodeFile(path, bitmapOptions);bitmap = rotateBitmapByDegree(bitmap, degree);size[0] = bitmap.getWidth();size[1] = bitmap.getHeight();}} catch (Exception e) {e.printStackTrace();}return size;}// 圖片文件先在本地做旋轉,返回旋轉之后的圖片文件路徑public static String getImagePathAfterRotate(final Uri uri) {try {InputStream is = TUIConfig.getAppContext().getContentResolver().openInputStream(uri);Bitmap originBitmap = BitmapFactory.decodeStream(is, null, null);int degree = ImageUtil.getBitmapDegree(uri);if (degree == 0) {return FileUtil.getPathFromUri(uri);} else {Bitmap newBitmap = ImageUtil.rotateBitmapByDegree(originBitmap, degree);String oldName = FileUtil.getFileName(TUIConfig.getAppContext(), uri);File newImageFile = FileUtil.generateFileName(oldName, FileUtil.getDocumentCacheDir(TUIConfig.getAppContext()));if (newImageFile == null) {return FileUtil.getPathFromUri(uri);}ImageUtil.storeBitmap(newImageFile, newBitmap);newBitmap.recycle();return newImageFile.getAbsolutePath();}} catch (FileNotFoundException e) {return FileUtil.getPathFromUri(uri);}}/*** 轉換圖片成圓形** @param bitmap 傳入Bitmap對象* @return*/public static Bitmap toRoundBitmap(Bitmap bitmap) {int width = bitmap.getWidth();int height = bitmap.getHeight();float roundPx;float left, top, right, bottom, dst_left, dst_top, dst_right, dst_bottom;if (width <= height) {roundPx = width / 2;left = 0;top = 0;right = width;bottom = width;height = width;dst_left = 0;dst_top = 0;dst_right = width;dst_bottom = width;} else {roundPx = height / 2;float clip = (width - height) / 2;left = clip;right = width - clip;top = 0;bottom = height;width = height;dst_left = 0;dst_top = 0;dst_right = height;dst_bottom = height;}Bitmap output = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);Canvas canvas = new Canvas(output);final int color = 0xff424242;final Paint paint = new Paint();final Rect src = new Rect((int) left, (int) top, (int) right, (int) bottom);final Rect dst = new Rect((int) dst_left, (int) dst_top, (int) dst_right, (int) dst_bottom);final RectF rectF = new RectF(dst);paint.setAntiAlias(true);// 設置畫筆無鋸齒canvas.drawARGB(0, 0, 0, 0); // 填充整個Canvaspaint.setColor(color);// 以下有兩種方法畫圓,drawRounRect和drawCircle// canvas.drawRoundRect(rectF, roundPx, roundPx, paint);// 畫圓角矩形,第一個參數為圖形顯示區域,第二個參數和第三個參數分別是水平圓角半徑和垂直圓角半徑。canvas.drawCircle(roundPx, roundPx, roundPx, paint);paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));// 設置兩張圖片相交時的模式canvas.drawBitmap(bitmap, src, dst, paint); //以Mode.SRC_IN模式合并bitmap和已經draw了的Circlereturn output;}/*** 根據圖片 UUID 和 類型得到圖片文件路徑** @param uuid 圖片 UUID* @param imageType 圖片類型 V2TIMImageElem.V2TIM_IMAGE_TYPE_THUMB , V2TIMImageElem.V2TIM_IMAGE_TYPE_ORIGIN ,* V2TIMImageElem.V2TIM_IMAGE_TYPE_LARGE* @return 圖片文件路徑*/public static String generateImagePath(String uuid, int imageType) {return TUIConfig.getImageDownloadDir() + uuid + "_" + imageType;}public static String getGroupConversationAvatar(String groupId) {SharedPreferences sp = TUIConfig.getAppContext().getSharedPreferences(TUILogin.getSdkAppId() + SP_IMAGE, Context.MODE_PRIVATE);final String savedIcon = sp.getString(groupId, "");if (!TextUtils.isEmpty(savedIcon) && new File(savedIcon).isFile() && new File(savedIcon).exists()) {return savedIcon;}return "";}public static void setGroupConversationAvatar(String conversationId, String url) {SharedPreferences sp = TUIConfig.getAppContext().getSharedPreferences(TUILogin.getSdkAppId() + SP_IMAGE, Context.MODE_PRIVATE);SharedPreferences.Editor editor = sp.edit();editor.putString(conversationId, url);boolean success = editor.commit();if (!success) {Log.e("ImageUtil", "setGroupConversationAvatar failed , id : " + conversationId + " , url : " + url);}} }

總結

以上是生活随笔為你收集整理的Android进阶之路 - 解决部分手机拍照之后图片被旋转的问题的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。