1.調用相機android
private void takePhoto() { //建立File對象,用於存儲拍照後的圖片 File outputImage = new File(getExternalCacheDir(), "output_iamge.jpg"); try { if (outputImage.exists()) { outputImage.delete(); } outputImage.createNewFile(); } catch (IOException e) { e.printStackTrace(); } if (Build.VERSION.SDK_INT >= 24) { imageUri = FileProvider.getUriForFile(this, "com.test.camera.fileprovider", outputImage); } else { imageUri = Uri.fromFile(outputImage); } //啓動相機程序 Intent intent = new Intent("android.media.action.IMAGE_CAPTURE"); intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); startActivityForResult(intent, 3); }
首先這裏建立了一個File對象,用於存放攝像頭拍下的照片(output_image.jpg),並將他放在SD卡的應用緩存目錄下(指SD卡中專門用於存放當前應用緩存數據的位置),調用getExternalCacheDir()方法能夠獲得該目錄,具體路徑爲/sdcard/Android/data/<package name>/cache。爲何呢?應爲從Android6.0系統開始,讀寫SD卡被列爲危險權限,若是將圖片放在SD卡的任何其餘目錄,都要進行運行時權限處理,而是用應用關聯目錄則能夠跳過這一步。緩存
接着進行一個判斷,若是運行設備的系統版本低於Android7.0,就調用Uri的fromFile()方法將File對象轉換成Uri對象,這個Uri對象標識着output_image.jpg這張圖片本地的真實路徑。不然,就調用FileProvider的getUriForFile()的方法將File對象轉換成一個封裝過的Uri對象。getUriForFile()第二個參數能夠是任意惟一的字符串。之因此進行這樣一層轉換,是由於從Android7.0系統開始,直接使用本地真實路徑的Uri被認爲是不安全,會拋出FileUriExposedException異常。而FileProvider則是一種特殊的內容提供器,它使用了和內容提供器相似的機制來對數據進行保護,能夠選擇性的將封裝過的Uri共享給外部,從而提升了應用的安全性。安全
因爲咱們使用的是一個隱式Intent,系統會找出可以響應這個Intent的活動去啓動,這樣照相機程序就會啓動,拍下的照片就會輸出到output_image.jpg中。app
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case 3: if (resultCode == RESULT_OK) { try { //將拍攝的照片顯示出來 Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri)); img_img.setImageBitmap(bitmap); } catch (FileNotFoundException e) { e.printStackTrace(); } } break; } }
不過如今尚未結束,剛纔提到了內容提供器,咱們天然要在AndroidManifest.xml中對內容提供器進行註冊。ide
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="..."> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> ... <provider android:name="android.support.v4.content.FileProvider" android:authorities="com.test.camera.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider> </application> </manifest>
其中,android:name屬性的值是固定的,android:authorities屬性值必需要和剛纔FileProvider.getUriFile()方法中的第二個參數一致。另外,<provider>標籤內部使用<meta-data>來指定Uri的共享路徑,並引用了@xml/file_paths資源。下面咱們來建立它。ui
res->New->Directory,建立一個xml目錄,接着xml->New->File,建立一個file_paths.xml文件。文件以下:this
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-path name="my_images" path="" /> </paths>
其中,external-path就是用來指定Uri共享的,name屬性的值隨便填,path屬性的值爲共享的具體路徑。這裏設置爲空就表示將整個SD卡進行共享,固然也能夠僅共享output——image.jpg這張圖片的路徑。code
另外,Android4.4系統以前,訪問SD卡的應用關聯目錄也是要聲明權限的,從4.4系統開始就不須要權限的聲明。爲兼容老版本須要聲明該權限:xml
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
2.調用相冊對象
從相冊中讀取圖片,咱們先要動態申請WRITE_EXTERNAL_STORAGE這個危險權限。
String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE}; private void getImage() { if (ContextCompat.checkSelfPermission(this, permissions[0]) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, permissions, 1); } else { openAlbum(); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode) { case 1: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { openAlbum(); } else { Toast.makeText(this, "You denied the permission", Toast.LENGTH_SHORT).show(); } break; } }
用戶受權成功後調用openAlbum()方法,調用相冊。
private void openAlbum() { Intent intent = new Intent("android.intent.action.GET_CONTENT"); intent.setType("image/*"); startActivityForResult(intent, 2); }
Android系統從4.4版本開始,選取相冊中的圖片再也不返回圖片真實的Uri,而是封裝過的Uri,所以在4.4版本以上的手機就須要對這個Uri解析。
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case 2: if (resultCode == RESULT_OK) { if (Build.VERSION.SDK_INT >= 19) { handleImageOnKitKat(data); } else { handleImageBeforeKitKat(data); } } break; } } @TargetApi(19) private void handleImageOnKitKat(Intent data) { String imagePath = null; Uri uri = data.getData(); if (DocumentsContract.isDocumentUri(this, uri)) { //若是是document類型的uri,則經過document id 處理 String docId = DocumentsContract.getDocumentId(uri); if ("com.android.providers.media.documents".equals(uri.getAuthority())) { String id = docId.split(":")[1];//解析出數字格式的id String selection = MediaStore.Images.Media._ID + "=" + id; imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection); } else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())) { Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(docId)); imagePath = getImagePath(contentUri, null); } } else if ("content".equalsIgnoreCase(uri.getScheme())) { //若是content類型的uri,則使用普通方式處理 imagePath = getImagePath(uri, null); } else if ("file".equalsIgnoreCase(uri.getScheme())) { //若是是file類型的uri,直接獲取圖片路徑便可 imagePath = uri.getPath(); } displayImage(imagePath); } private void handleImageBeforeKitKat(Intent data) { Uri uri = data.getData(); String imagePath = getImagePath(uri, null); displayImage(imagePath); } private String getImagePath(Uri uri, String selection) { String path = null; //經過Uri和selection來獲取真實的圖片路徑 Cursor cursor = getContentResolver().query(uri, null, selection, null, null); if (cursor != null) { if (cursor.moveToFirst()) path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA)); cursor.close(); } return path; } private void displayImage(String imagePath) { if (imagePath != null) { Bitmap bitmap = BitmapFactory.decodeFile(imagePath); img_img.setImageBitmap(bitmap); } else { Toast.makeText(this, "failed to get image", Toast.LENGTH_SHORT).show(); } }
出自郭霖《第一行代碼 Android 第二版》第八章。