關於Android手機拍照預覽、剪裁介面出現照片九十度旋轉的問題
案場還原:
最近做的專案,測試機小米6X及本人的努比亞Z11測試拍照環節均正常,但在領導的三星手機及Oppo FindX上就出現了奇葩現象,拍照完預覽照片、剪裁照片出現了九十度的旋轉,如果這時候你用模擬器,比如Genymotion也能發現此問題,預覽及剪裁出現旋轉。
原因排查:
通過搜尋大量牆裡牆內資料,原因大概總結為以下幾點,自我理解,若有不對,還望指正:
- Android原生系統設定的拍照介面是Landscape也就是橫屏模式,因此即使你呼叫拍照的Activity是豎屏模式,拍照畫面也是豎屏模式,但到了預覽和剪裁就預設切換成了橫屏的畫面。
- 可能是某些手機廠商深度定製系統的結果,比如三星
- 有網友也反應可能與Activity啟動模式有關,建議設定當前啟動模式為Standard,但我不是這個原因,因為預設模式就是Standard。
對於以上原因,可是嘗試在配置檔案中設定:
<activity
android:name=".activity.ProfileActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:exported ="true"
android:launchMode="standard"
android:screenOrientation="portrait" />
But,這些對我來說都不起作用
4 .程式碼問題
解決:
強制將旋轉後的照片轉回來
最重要的也就是從ExifInterface這個多媒體相關類中讀取照片被旋轉的角度,然後使用Matrix強制轉回來。程式碼如下
/**
* 獲取原始圖片的角度(***解決三星手機拍照後圖片是橫著的問題***)
* 讀取照片exif資訊中的旋轉角度
*
* @param path 照片路徑
* @return角度
*/
private int getPictureDegree(String path) {
int degree = 0;
/**
* 方法一
*/
// boolean hasRotation = false;
// String[] orientationColumn = {MediaStore.Images.Media.ORIENTATION};
// Cursor cursor = context.getContentResolver().query(cameraUri, orientationColumn, null, null, null);
// Log.e(TAG, "cursor: " + cursor + " " );
// if (cursor.getCount() != 1) {
// cursor.close();
// return -1;
// }
// if (cursor.moveToFirst()){
// degree = cursor.getInt(cursor.getColumnIndex(orientationColumn[0]));
// hasRotation = true;
// }
// cursor.close();
// cursor = null;
// Log.e(TAG, "getPictureDegree: " + degree );
/**
* 方法二
*/
try {
ExifInterface exifInterface = new ExifInterface(path);
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
Log.e(TAG, "原圖被旋轉角度: ========== " + orientation );
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();
}
/**
* 方法三
*/
// int degree = 0;
// android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
// android.hardware.Camera.getCameraInfo(0, info);
// degree = info.orientation;
// Log.e(TAG, "CameraInfo: " + info.orientation );
return degree;
}
然後轉回來:
Matrix matrix = new Matrix();
int degree = getPictureDegree(cameraImagePath);
matrix.postRotate(degree); /*翻轉90度*/
這裡需要說明一下獲取照片被旋轉角度getPictureDegree這個方法,有三種獲取角度的方式
- 使用資料庫Cursor讀取資訊
context.getContentResolver().query(cameraUri, orientationColumn, null, null, null)
但我讀不到String[] orientationColumn = {MediaStore.Images.Media.ORIENTATION};
相關的資訊,報空指標異常,因此放棄。 - 直接讀取硬體攝像頭
CameraInfo
相關資訊再進行旋轉,這個方法也有硬傷,在正常的測試機上預覽剪裁本來不會旋轉,結果你用你用攝像機豎屏拍照,得到的info.orientation
是九十度,然後進行旋轉,本來正常的拍照也被顛倒了,因此同樣放棄該方式。 - 使用ExifInterface讀取,關於該類的詳細說明請百度。國內多數開發者遇見此坑都推薦運用此方式讀取旋轉角度,但我遇見一個更奇葩的問題,通過
int orientation=exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
讀取到的角度永遠為0!!!!!!!!
此問題搞得我焦頭爛額,欲哭無淚。經過一番Google,Stack Overflow上的開發者建議切換為第一種Cursor讀取資訊,但我剛才也講了,方法報空指標,有待商榷。
現在所有的問題都集中在一點,明明旋轉了的照片讀取到的旋轉角度卻是0度!!
好吧,一天兩天過去,終於找到了一種思路
還別說,之前我還真的是在讀取角度資訊之前做了圖片壓縮處理,然後才進行旋轉操作
/**
* 旋轉
* @return
*/
private Bitmap toturn() {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
// 簡單的壓縮
options.inSampleSize = 4; //把原圖按1/4的比例壓縮
options.inJustDecodeBounds = false; // 壓縮完後便可以將inJustDecodeBounds設定為false
// 把流轉化為Bitmap圖片
Bitmap bitmap = BitmapFactory.decodeFile(cameraImagePath, options);
Matrix matrix = new Matrix();
int degree = getPictureDegree(cameraImagePath);
matrix.postRotate(degree); /*翻轉90度*/
int width = bitmap.getWidth();
int height = bitmap.getHeight();
Bitmap returnBm = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
if (returnBm == null) {
returnBm = bitmap;
}
if (bitmap != returnBm) {
bitmap.recycle();
}
Log.e(TAG, "returnBm: " + returnBm );
return returnBm;
}
好吧,皇天不負有心人,先旋轉再壓縮之後,執行,終於解決了,在這也要感謝連結的博主。
有時候我們找bug,就跟買房一樣,大海撈針,也是看運氣。
附上我所有的拍照相關程式碼
private Uri cameraUri;
// 裁剪後圖片的寬(X)和高(Y),480 X 480的正方形。
private static int output_X = 480;
private static int output_Y = 480;
private Uri imageUriCrop; //剪裁後的圖片uri
/**
* 拍照
*/
public void fromCamera(){
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
File dir = new File(ConstantManager.cameraPath);//建立資料夾
if (!dir.exists()) dir.mkdirs();
Intent openCameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
cameraFile = new File(dir, String.valueOf(System.currentTimeMillis()) + ".jpg");//指定照片路徑
if (!cameraFile.exists()) {
try {
cameraFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
cameraImagePath = cameraFile.getPath();
if (Build.VERSION.SDK_INT >= 24) {
cameraUri = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", cameraFile);
} else {
cameraUri = Uri.fromFile(cameraFile);
}
// Log.e(TAG, "cameraUri: " + cameraUri );
openCameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, cameraUri);
openCameraIntent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 0);
((Activity) context).startActivityForResult(openCameraIntent, 0x008);
} else {
view.showMsg("沒有儲存卡");
}
}
/**
* 圖片剪裁
*
* @param system
* @param in
*/
public void crop(boolean system, Intent in,@Nullable File cameraFile) {
Intent intent = new Intent("com.android.camera.action.CROP");
//設定資料來源
Uri uri;
if (system) { //從系統相簿選擇
if (in != null && in.getData() != null) {
uri = createSingleCropUri("crop_image.jpg"); //剪裁後的相簿圖片名
intent.setDataAndType(in.getData(), "image/*");
} else {
return;
}
} else { //從相機拍照選擇
if (Build.VERSION.SDK_INT >= 24) {
//新增這一句表示對目標應用臨時授權該Uri所代表的檔案
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
/**
* Android 7.0要求設定資料來源使用ContentUri,輸出使用正常的Uri
*/
Uri contentUri = getImageContentUri(context,cameraFile);
intent.setDataAndType(contentUri,"image/*"); //設定剪裁後的圖片資料來源
uri = createSingleCropUri(cameraFile.getName()); //剪裁後的照片名
} else {
intent.setDataAndType(Uri.fromFile(cameraFile), "image/*"); //設定剪裁後的圖片資料來源
uri = createSingleCropUri(cameraFile.getName()); //剪裁後的照片名
}
}
Log.e(TAG, "crop: " + uri );
intent.putExtra("crop", "true");// 設定裁剪
intent.putExtra("aspectX", 1);// aspectX , aspectY :寬高的比例,為浮點數則不會固定裁剪框比例
intent.putExtra("aspectY", 1);
intent.putExtra("outputX", output_X);// 定義輸出圖片大小,不定義則按裁剪框大小定義,但裁剪耗時會加長
intent.putExtra("outputY", output_Y);
intent.putExtra("return-data", true);
intent.putExtra("scale", true);//保留比例
intent.putExtra("return-data", false);//是否返回data
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);//輸出路徑,剪裁後的圖片
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());//編碼格式
intent.putExtra("noFaceDetection", true);
((Activity) context).startActivityForResult(intent, 0x009);
}
private Uri createSingleCropUri(String fileName) {
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
File dir = new File(ConstantManager.cameraPath);//建立資料夾
if (!dir.exists()) dir.mkdirs();
File cropFile = new File(dir, fileName);//指定裁剪路徑,多圖情況下裁剪路徑不能唯一,否則檔案會被覆蓋
imageUriCrop = Uri.fromFile(cropFile);
}
return imageUriCrop;
}
/**
* 安卓7.0裁剪根據檔案路徑獲取uri
*/
public static Uri getImageContentUri(Context context, File imageFile) {
String filePath = imageFile.getAbsolutePath();
Cursor cursor = context.getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
new String[]{MediaStore.Images.Media._ID},
MediaStore.Images.Media.DATA + "=? ",
new String[]{filePath}, null);
if (cursor != null && cursor.moveToFirst()) {
int id = cursor.getInt(cursor
.getColumnIndex(MediaStore.MediaColumns._ID));
Uri baseUri = Uri.parse("content://media/external/images/media");
return Uri.withAppendedPath(baseUri, "" + id);
} else {
if (imageFile.exists()) {
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DATA, filePath);
return context.getContentResolver().insert(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
} else {
return null;
}
}
}
/**
* 獲取原始圖片的角度(***解決三星手機拍照後圖片是橫著的問題***)
* 讀取照片exif資訊中的旋轉角度
*
* @param path 照片路徑
* @return角度
*/
private int getPictureDegree(String path) {
int degree = 0;
/**
* 方法一
*/
// boolean hasRotation = false;
// String[] orientationColumn = {MediaStore.Images.Media.ORIENTATION};
// Cursor cursor = context.getContentResolver().query(cameraUri, orientationColumn, null, null, null);
// Log.e(TAG, "cursor: " + cursor + " " );
// if (cursor.getCount() != 1) {
// cursor.close();
// return -1;
// }
// if (cursor.moveToFirst()){
// degree = cursor.getInt(cursor.getColumnIndex(orientationColumn[0]));
// hasRotation = true;
// }
// cursor.close();
// cursor = null;
// Log.e(TAG, "getPictureDegree: " + degree );
/**
* 方法二
*/
try {
ExifInterface exifInterface = new ExifInterface(path);
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
Log.e(TAG, "原圖被旋轉角度: ========== " + orientation );
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();
}
/**
* 方法三
*/
// int degree = 0;
// android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
// android.hardware.Camera.getCameraInfo(0, info);
// degree = info.orientation;
// Log.e(TAG, "CameraInfo: " + info.orientation );
return degree;
}
/**
* 旋轉
* @return
*/
private Bitmap toturn() {
Matrix matrix = new Matrix();
int degree = getPictureDegree(cameraImagePath);
matrix.postRotate(degree); /*翻轉90度*/
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
// 簡單的壓縮
options.inSampleSize = 4; //把原圖按1/4的比例壓縮
options.inJustDecodeBounds = false; // 壓縮完後便可以將inJustDecodeBounds設定為false
// 把流轉化為Bitmap圖片
Bitmap bitmap = BitmapFactory.decodeFile(cameraImagePath, options);
int width = bitmap.getWidth();
int height = bitmap.getHeight();
Bitmap returnBm = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
// if (returnBm == null) {
// returnBm = bitmap;
// }
// if (bitmap != returnBm) {
// bitmap.recycle();
// }
// Log.e(TAG, "returnBm: " + returnBm );
return returnBm;
}
/**
* 儲存旋轉後的Bitmap圖片在SD卡中
* 如果沒有SD卡則存在手機中
* @return 儲存成功時返回圖片的路徑,失敗時返回null
*/
public String savePhotoToSD() {
Bitmap mbitmap = toturn();
FileOutputStream outStream = null;
String fileName = String.valueOf(System.currentTimeMillis()) + ".jpg";
File file = new File(ConstantManager.cameraPath );
String filePath = file + "/" +fileName;
// 判斷檔案是否已經存在,不存在則建立
if ( !file.exists() ) {
file.mkdirs();
}
try {
outStream = new FileOutputStream(filePath);
// 把資料寫入檔案,100表示不壓縮
mbitmap.compress(Bitmap.CompressFormat.JPEG, 100, outStream);
Log.e(TAG, "旋轉處理後的圖片: " + filePath );
return filePath;
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
try {
if (outStream != null) {
outStream.close();
}
if (mbitmap != null) {
mbitmap.recycle();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Activity中回撥處理:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.e(TAG, "resultCode: " + resultCode);
if (resultCode == this.RESULT_OK) {
Log.e(TAG, "requestcode: " + requestCode);
switch (requestCode) {
case 0x007:
presenter.crop(true, data, null);
break;
case 0x008:
File imageFile = new File(presenter.savePhotoToSD());
presenter.crop(false, data, imageFile);
break;
case 0x009:
presenter.uploadImage(userId, entity);
break;
default:
break;
}
}
}
相關推薦
關於Android手機拍照預覽、剪裁介面出現照片九十度旋轉的問題
案場還原: 最近做的專案,測試機小米6X及本人的努比亞Z11測試拍照環節均正常,但在領導的三星手機及Oppo FindX上就出現了奇葩現象,拍照完預覽照片、剪裁照片出現了九十度的旋轉,如果這時候你用模擬器,比如Genymotion也能發現此問題,預覽及剪裁出現
Android自定義相機實現拍照、預覽、顯示、上傳
自定義相機拍照並存放到本地,可以預覽,用okHttp上傳到伺服器 用法 1.點選登入進入到拍照頁面 2.拍照後進入到上傳介面,需要在Constant中修改BASE_URL為自己伺服器圖片上傳地
Android多媒體技術(一)Camera實時視訊採集預覽、拍照、JPEG圖片方向的處理
Camera實時視訊採集預覽、拍照、JPEG圖片方向的處理 作者: 蔣東國 時間: 2017年1月12日 星期四
Android 相機1 之Camera1的最簡單的使用(預覽、拍照、變焦、特效)
Android兩個相機的API的個人總結 API1的方法較少、命名規則等都比較簡單,如果是針對目前市面上的手機,API1是足夠而且使用起來非常方便,尤其是它的setParameter方法,相較於API2的要自己去填key和value來說,它不僅很容易能找到相機
Android Camera2教程之開啟相機、開啟預覽、實現PreviewCallback、拍照
Android API 21新增了Camera2,這與之前的camera架構完全不同,使用起來也比較複雜,但是功能變得很強大。
android 手機拍照、相簿選擇照片並顯示
關鍵程式碼: public class MainActivity extends AppCompatActivity implements View.OnClickListener { private Button btn1; private Uri imageUri;
手機+PC雙屏顯示:android端即時預覽PC端修改的程式碼
前言 如何讓手機充當第二個顯示器,用來隨時預覽PC端的程式碼?前一陣子寫程式碼時,一直在琢磨這個問題。 因為辦公室電腦配置低下,且只配備一個17寸顯示器,每當反覆除錯預覽網頁時,都要儲存,重新整理。用過 brackets即使預覽功能,總是不太習慣。於是就想
根據介面獲取拍照預覽方向以及圖片儲存旋轉角度
//根據當前介面顯示方向設定攝像頭預覽方向 public static int cameraDisplayOrientation(Activity activity, int cameraId) { android.hardware.Camera.CameraIn
android 仿微信多圖選擇器(帶預覽、照相功能)
實現了單選、多選 、拍照 、預覽 等功能;先上圖: 程式碼結構 下面不如正題: 一、新增依賴、許可權 1)新增以下依賴 dependencies { compile fileTree(dir: 'libs', include: ['*.jar'])
Android手機拍照或從本地相簿選取圖片設定頭像。適配小米、華為、7.0
1,讓使用者通過選擇本地相簿之類的圖片庫中已有的影象,裁剪後作為頭像。 2,讓使用者啟動手機的相機拍照,拍完照片後裁剪,然後作為頭像。 程式碼如下 MainActivity.Java檔案: package portrait.bala.port
html5+exif.js+canvas實現手機端照片上傳預覽、壓縮、旋轉功能
html5+canvas進行移動端手機照片上傳時,發現ios手機上傳豎拍照片會逆時針旋轉90度,橫拍照片無此問題;Android手機沒這個問題。 因此解決這個問題的思路是:獲取到照片拍攝的方向角,對非橫拍的ios照片進行角度旋轉修正。 利用exif.js讀取照片的拍攝資訊
Vue 實現圖片預覽、裁剪並獲取被裁剪區域的base64(無元件)
前言 最近公司專案需要用到圖片裁剪技術,便著手寫了一個,可以說是用Vue實現的原生裁剪,畢竟也只是去操作dom,不過vue有個黑魔法,ref屬性,使用的方法和原生dom一模一樣但是更節省dom操作時的消耗 裁剪思路 這邊大致介紹一下裁剪圖片的思路
Android手機系統版本號、IMEI、手機廠商、手機型號
獲取手機IMEI號 /** * 獲取手機IMEI號 * <p> * 需要動態許可權: android.permission.READ_PHONE_STATE */ public st
android使用webview預覽png,pdf,doc,xls,txt,等檔案
最近有專案有一個需求,就是線上直接預覽pdf,doc,xls,txt等檔案,ios的webview比較強大,可以直接解析地址,然後預覽。但是android的webview就比較差強人意了。當然,開啟各種型別的檔案,我麼可以使用intent來做,但是這個明顯跟我們的需求不一致啊
小程式中圖片點選預覽、長按識別圖中二維碼的問題
通過自己的測試以及各類部落格資料的查詢,總結如下: 1.小程式中的圖片不能識別除小程式碼以外的二維碼 2.並且僅在 wx.previewImage 中支援長按識別 官方文件(wx.previewImage元件) html程式碼(這裡我就簡單的添加了一張圖片做測
基於“formData批量上傳的多種實現” 的多圖片預覽、上傳的多種實現 formData批量上傳的多種實現
前言 圖片上傳是web專案常見的需求,我基於之前的部落格的程式碼(請戳:formData批量上傳的多種實現)裡的第三種方法實現多圖片的預覽、上傳,並且支援三種方式新增圖片到上傳列表:選擇圖片、複製貼上圖片、滑鼠拖拽圖片,同時支援從上傳列表中移除圖片(點選“X”號) 效果演示 選擇
vue中圖片轉換為base64上傳、預覽、刪除
<template> <div class="com-upload-img"> <div class="img_group"> <div class="img_box" v-if="al
檢查是否是圖片、限制上傳畫素、圖片上傳時預覽、點選預覽圖在新頁面檢視原圖、上傳圖片
在做網站後臺管理時,涉及到了圖片的上傳,經過多次改動後,上傳圖片的一系列步驟如下 1.在點選瀏覽按鈕時彈出框只出現圖片格式的檔案,可在input type=file中通過accept=".jpg,.png,.jpeg" 限制。 增加限制前: 增加限制後: 但在上圖示紅處選擇所有檔案時又會
基於Bootstrap的多圖片(檔案也可以)上傳、預覽、刪除、縮放、進度...顯示
一、引用js、css 二、Html程式碼 <input id="myFile" type="file" name="myFile" multiple class="file-loading
android手機拍照6.0,7.0問題
因前期專案執行沒有再7.0以上手機執行,最近一個同事用的華為8.0手機執行專案進行拍照,結果閃退,問題就來了,整的是焦頭爛額啊,先是用的FilePrivider,網上好多部落格都進行了講解,我在使用過程中FileProvider.getUriForFile發現返