總結Android呼叫系統相機拍照遇到的坑
阿新 • • 發佈:2019-02-19
拍照功能在應用開發中幾乎已成為標配,例如使用者通過拍照上傳頭像。實現拍照功能的方式有兩種。第一種是使用相機API(即Camera類)來自定義拍照,第二種是使用Intent呼叫系統相機來拍照。其中最常見的方式是直接呼叫系統相機拍照來獲取照片。
呼叫系統相機獲取照片的流程如下,這裡省去了宣告許可權和動態許可權申請:
下面進入填坑階段。
一.拍攝的照片被旋轉了
明明在拍攝時照片是正的,進入裁剪頁面發現照片竟然是被旋轉的。這個現象在不同的裝置上,表現會不一樣,可能正常也可能被旋轉,而且旋轉的角度也不同。要解決這個問題,需要首選獲取照片的旋轉角度,再反轉回去就可以了。
ExifInterface介面提供了多媒體檔案比如JPG格式圖片的一些附加資訊,比如檔案的旋轉,gps,拍攝時間等。如下程式碼展示了使用ExifInterface獲取圖片的旋轉角度。
進行圖形變換如旋轉、縮放、移動的操作,可以使用Matrix類來完成。如下程式碼展示了使用Matrix對圖片旋轉,生成新的Bitmap。
二.拍照完成後應用閃退
這個現象相對少見,目前在廉價的低端手機上出現過。啟動拍照時正常,當在拍照介面點選確認按鈕,拍照介面消失返回我們的應用時,直接閃退。更讓人崩潰的是,Logcat裡面沒有相應的日誌資訊。無奈只能藉助搜尋,發現原來是手機廠商對系統做了修改(為了在低端硬體上能夠執行Android系統...),當我們的應用程式的Activity啟動拍照,進入系統相機時,我們的Activity被銷燬了。這個情況在測試時也不是必現。測試機在使用一段時間後很容易出現,但如果將裝置重啟後開啟我們的應用來拍照又沒有問題(這是我們遇到的情況,可能不是必現規則)。
Activity被回收時儲存資料,可以在onSaveInstanceState()生命週期方法中處理,將圖片路徑儲存到Bundle中。
然後在onCreate()方法裡,判斷savedInstanceState不為空時,取出圖片路徑的值。
網上看到另外一種恢復的方法。Activity沒有重新建立,而是成員變數被回收了,當拍照返回時,在onActivityResult()方法中mFilePath為空。解決方法是從onRestoreInstanceState()方法恢復資料。這種情況暫時沒遇到,做個記錄。
呼叫系統相機獲取照片的流程如下,這裡省去了宣告許可權和動態許可權申請:
通過Action為MediaStore.ACTION_IMAGE_CAPTURE的隱式Intent啟動拍照,使用MediaStore.EXTRA_OUTPUT指定照片路徑,成功返回後即可獲取到照片的完整路徑。private String mFilePath;// 需要初始化,路徑自定義 private static final int REQUEST_CODE_CAMERA = 1; // 啟動系統相機 private void startCamareActivity() { Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.parse("file://" + mFilePath)); startActivityForResult(intent, REQUEST_CODE_CAMERA); } // 拿到照片 @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_CODE_CAMERA && resultCode == RESULT_OK) { // mFilePath即是拍照完成後的圖片 // 這裡可以進入裁剪頁面了 } }
下面進入填坑階段。
一.拍攝的照片被旋轉了
明明在拍攝時照片是正的,進入裁剪頁面發現照片竟然是被旋轉的。這個現象在不同的裝置上,表現會不一樣,可能正常也可能被旋轉,而且旋轉的角度也不同。要解決這個問題,需要首選獲取照片的旋轉角度,再反轉回去就可以了。
ExifInterface介面提供了多媒體檔案比如JPG格式圖片的一些附加資訊,比如檔案的旋轉,gps,拍攝時間等。如下程式碼展示了使用ExifInterface獲取圖片的旋轉角度。
/** * 獲取圖片的旋轉角度 * @param path 圖片的絕對路徑 */ private int getBitmapDegree(String imagePath) { int degree = 0; try { // 從指定路徑下讀取圖片,並獲取其EXIF資訊 ExifInterface exifInterface = new ExifInterface(imagePath); // 獲取圖片的旋轉資訊 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; }
進行圖形變換如旋轉、縮放、移動的操作,可以使用Matrix類來完成。如下程式碼展示了使用Matrix對圖片旋轉,生成新的Bitmap。
/** * 將圖片按照某個角度進行旋轉 * @param bitmap 需要旋轉的圖片 * @param degree 旋轉角度 */ public static Bitmap rotateBitmapByDegree(Bitmap bitmap, int degree) { Bitmap result = null; // 根據旋轉角度,生成旋轉矩陣 Matrix matrix = new Matrix(); matrix.postRotate(degree); try { // 將原始圖片按照旋轉矩陣進行旋轉,並得到新的圖片 result = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); } catch (Exception e) { } if (bitmap != null && !bitmap.isRecycled()) { bitmap.recycle(); bitmap = null; } return result; }
二.拍照完成後應用閃退
這個現象相對少見,目前在廉價的低端手機上出現過。啟動拍照時正常,當在拍照介面點選確認按鈕,拍照介面消失返回我們的應用時,直接閃退。更讓人崩潰的是,Logcat裡面沒有相應的日誌資訊。無奈只能藉助搜尋,發現原來是手機廠商對系統做了修改(為了在低端硬體上能夠執行Android系統...),當我們的應用程式的Activity啟動拍照,進入系統相機時,我們的Activity被銷燬了。這個情況在測試時也不是必現。測試機在使用一段時間後很容易出現,但如果將裝置重啟後開啟我們的應用來拍照又沒有問題(這是我們遇到的情況,可能不是必現規則)。
Activity被回收時儲存資料,可以在onSaveInstanceState()生命週期方法中處理,將圖片路徑儲存到Bundle中。
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putString("file_path", mFilePath);
super.onSaveInstanceState(outState);
}
然後在onCreate()方法裡,判斷savedInstanceState不為空時,取出圖片路徑的值。
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
if (savedInstanceState != null) {
mFilePath = savedInstanceState.getString("file_path");
// your code here
}
}
網上看到另外一種恢復的方法。Activity沒有重新建立,而是成員變數被回收了,當拍照返回時,在onActivityResult()方法中mFilePath為空。解決方法是從onRestoreInstanceState()方法恢復資料。這種情況暫時沒遇到,做個記錄。
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
if (TextUtils.isEmpty(mFilePath)) {
mFilePath = savedInstanceState.getString("file_path");
}
super.onRestoreInstanceState(savedInstanceState);
}