說明: 這篇文章主要介紹瞭如何在安卓Fragment中使用攝像頭拍照並儲存影像和縮圖。
這篇文章是我的“Android Studio下用Fragment進行攝像頭開發系列文章五篇”的第一篇,如果你還沒做好準備,可以先看看我的程式碼,GitHub:UltimateAndroidCameraGuide。這篇教程中也會詳細對程式碼進行說明,主要參考這個檔案:SimpleCameraIntentFragment.java。
在開始之前,先花點時間說明一下手機裝置的功能和釋出App時需要考慮的裝置功能檢測問題。
你的裝置有攝像頭嗎?
為了確保市場上的大多數裝置都能執行你的程式,必須在專案中做一些檢測,保證使用的裝置可以執行你的程式碼。
我們可以這麼做:
- 在程式的配置清單檔案中標明要求使用攝像頭;
- 在程式碼中用
PackageManager
進行裝置功能檢測;
在專案的androidManifest檔案中要求使用攝像頭,程式碼如下:
1 2 |
; html-script: false ] <uses-feature android:name="android.hardware.camera" android:required="true"/> |
(在一個Fragment中)用PackageManager
在程式碼中檢測裝置是否含有攝像頭,程式碼如下:
1 2 3 4 5 6 7 8 9 |
; html-script: false ] Context context = getActivity(); PackageManager packageManager = context.getPackageManager(); if(packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA) == false){ Toast.makeText(getActivity(), "This device does not have a camera.", Toast.LENGTH_SHORT) .show(); return; } |
如果有一個或者多個攝像頭怎麼辦?
在一些安卓裝置上會有前置攝像頭和後置攝像頭,我們可以用PackageManager
來對攝像頭進行功能檢測,例如:
1 2 |
; html-script: false ] context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA) |
通常我們需要檢測的是攝像頭的這些功能:
- FEATURE_CAMERA_FRONT (檢測是否含有前置攝像頭)
- FEATURE_CAMERA (檢測是否含有後置攝像頭)
- FEATURE_CAMERA_ANY (檢測是否含有任意一個攝像頭)
Fragment和攝像頭Intent元件
用Android Studio開啟我們的示例程式碼,然後在navigation drawer中選擇“Simple Camera Intent”,你會看到如下畫面:
當你選擇”Take Photo“,外部的拍照程式就會彈出來,然後我們就可以拍照了。拍照的結果會被顯示在主介面上,縮圖也會顯示在一個小區域裡。開啟SimpleCameraIntentFragment.java,可以看到下面這個方法(摘錄自Google’s Simple Camera documentation):
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 |
; html-script: false ] /** * Start the camera by dispatching a camera intent. */ protected void dispatchTakePictureIntent() { // Check if there is a camera. Context context = getActivity(); PackageManager packageManager = context.getPackageManager(); if(packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA) == false){ Toast.makeText(getActivity(), "This device does not have a camera.", Toast.LENGTH_SHORT) .show(); return; } // Camera exists? Then proceed... Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); // Ensure that there's a camera activity to handle the intent CameraActivity activity = (CameraActivity)getActivity(); if (takePictureIntent.resolveActivity(activity.getPackageManager()) != null) { // Create the File where the photo should go. // If you don't do this, you may get a crash in some devices. File photoFile = null; try { photoFile = createImageFile(); } catch (IOException ex) { // Error occurred while creating the File Toast toast = Toast.makeText(activity, "There was a problem saving the photo...", Toast.LENGTH_SHORT); toast.show(); } // Continue only if the File was successfully created if (photoFile != null) { Uri fileUri = Uri.fromFile(photoFile); activity.setCapturedImageURI(fileUri); activity.setCurrentPhotoPath(fileUri.getPath()); takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, activity.getCapturedImageURI()); startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO); } } } |
這裡我對攝像頭的檢測並不完美,因為只是簡單判斷是否有後置攝像頭。如果使用者的裝置只有一個前置攝像頭,那這種檢測就沒有什麼用了。
下一步我們要從攝像頭中接收影像資料,然後儲存下來。以下就是實現的程式碼,重複的部分就不一一貼出來了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
; html-script: false ] ** * The activity returns with the photo. * @param requestCode * @param resultCode * @param data */ * @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_TAKE_PHOTO && resultCode == Activity.RESULT_OK) { addPhotoToGallery(); CameraActivity activity = (CameraActivity)getActivity(); // Show the full sized image. setFullImageFromFilePath(activity.getCurrentPhotoPath(), mImageView); setFullImageFromFilePath(activity.getCurrentPhotoPath(), mThumbnailImageView); } else { Toast.makeText(getActivity(), "Image Capture Failed", Toast.LENGTH_SHORT) .show(); } } |
在Fragment中獲取Activity返回結果
接下來我們關注這幾行程式碼:
1 2 3 4 |
; html-script: false ] Uri fileUri = Uri.fromFile(photoFile); activity.setCapturedImageURI(fileUri); activity.setCurrentPhotoPath(fileUri.getPath()); |
我們知道,當選擇使用(由Activity管理的)Fragment時,為了保證所有部件執行正常,需要額外處理一些Fragment限制。在某些裝置上(比如三星),你必須把返回結果的影像資料儲存到一個檔案中(該檔案在使用Intent時需要提供)。但是當程式從照相程式中返回到前臺時,這個檔案就不可用了,然後程式會莫名崩潰了。
為了防止程式崩潰,我已經寫了一個特別的Activity
——“CameraActivity”,可以自動儲存和恢復攝像頭的資料檔案和Uri資料。當程式的生命週期改變時,我們需要這些資料。
安全地獲取圖片資料
我們來看看CameraActivity,這裡就不全部貼出程式碼了,但是你可以看到這個Activity
會在resume
中儲存和恢復攝像頭的資料檔案和Uri資料。
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 |
; html-script: false ] // Storage for camera image URI components private final static String CAPTURED_PHOTO_PATH_KEY = "mCurrentPhotoPath"; private final static String CAPTURED_PHOTO_URI_KEY = "mCapturedImageURI"; // Required for camera operations in order to save the image file on resume. private String mCurrentPhotoPath = null; private Uri mCapturedImageURI = null; @Override public void onSaveInstanceState(Bundle savedInstanceState) { if (mCurrentPhotoPath != null) { savedInstanceState.putString(CAPTURED_PHOTO_PATH_KEY, mCurrentPhotoPath); } if (mCapturedImageURI != null) { savedInstanceState.putString(CAPTURED_PHOTO_URI_KEY, mCapturedImageURI.toString()); } super.onSaveInstanceState(savedInstanceState); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { if (savedInstanceState.containsKey(CAPTURED_PHOTO_PATH_KEY)) { mCurrentPhotoPath = savedInstanceState.getString(CAPTURED_PHOTO_PATH_KEY); } if (savedInstanceState.containsKey(CAPTURED_PHOTO_URI_KEY)) { mCapturedImageURI = Uri.parse(savedInstanceState.getString(CAPTURED_PHOTO_URI_KEY)); } super.onRestoreInstanceState(savedInstanceState); } |
事實上我做的只是對這些需要的資訊用Intent
進行儲存和恢復。這樣,程式就知道該從哪裡找到需要引用的檔案。加入沒有這些程式碼,程式在onResume
中會崩潰。
以上這就是關於在Fragment中怎麼用攝像頭Intent元件進行拍照的簡單教程,你可以隨意使用以上所提供的原始碼進行修改使用。