引言:
調用系統相機拍照是安卓中比較常見,也比較簡單的功能。那麼拍照後得到的照片,我們有兩種處理:第一種,我們隻把它們顯示出來(不儲存在檔案中,但是這種隻能顯示縮略圖)。第二種,我們将照片寫入到檔案中,即儲存起來,那麼就可以重複使用。對于寫入到檔案這種操作,7.0以上版本需要特殊照顧,以避免出現FileUriExposedException異常。
第一種(擷取縮略圖):
首先,啟動系統相機:
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
}
然後,重寫回調方法:
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode==REQUEST_IMAGE_CAPTURE&& resultCode == RESULT_OK){
Bundle extras=data.getExtras();
Bitmap imageBitmap = (Bitmap) extras.get("data");
save(imageBitmap);
}
}
現在,imageBitmap就是我們的縮略圖,可以直接顯示在UI界面。其實,縮略圖也是可以儲存的,我這裡的save方法,就是将它寫入到外部存儲中。
/**
* 處理系統相機拍照後對圖檔的儲存,但是實際上儲存的僅僅是一張縮略圖,有些時候并不能滿足需要
* 而完整圖檔的擷取在7.0以上版本并不容易,用原始方法的話會報異常,
* @param bitmap
*/
private void save(Bitmap bitmap){
Date date = new Date();
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss"); // 格式化時間
String filename = format.format(date) + ".jpg";
File dir=getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File folder=new File(dir,"my_pictures");
/**
* File folder=new File(Environment.getExternalStorageDirectory()+"/my_pictures");
* getExternalStorageDirectory()方法已經廢棄,官方也不推薦使用,getExternalFilesDir是
* 官方文檔中使用的API
*/
if(!folder.exists()){
folder.mkdir();
}
File file=new File(folder,filename);
try {
FileOutputStream outputStream=new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG,100,outputStream);
outputStream.flush();
outputStream.close();
Toast.makeText(this, "Take finish", Toast.LENGTH_SHORT).show();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Log.w("Tag",file.getPath()); //列印存儲路徑
}
一張縮略圖,放大了就會模糊,可能不太夠用。是以不建議這樣做,那麼下面開始第二種方法的講解。
第二種(存入到檔案中):
/**
* 調用系統相機拍照并儲存的原始方法,7.0以上需要适配
* 這裡涉及到的問題就是内容共享,要使用到Provider類
*/
private void dispatchTakePictureIntent() {
Date date = new Date();
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss"); // 格式化時間
String filename = format.format(date) + ".jpg";
File dir=getExternalFilesDir(Environment.DIRECTORY_PICTURES);
//以dir檔案為父級檔案夾建立子檔案
File folder=new File(dir,"my_pictures");
/**
* File folder=new File(Environment.getExternalStorageDirectory()+"/my_pictures");
* getExternalStorageDirectory()方法已經廢棄,官方也不推薦使用,getExternalFilesDir是
* 官方文檔中使用的API
*/
if(!folder.exists()){
//建立目錄
folder.mkdir();
}
File file=new File(folder,filename);
Uri uri=null;
if(Build.VERSION.SDK_INT>=24){
uri= FileProvider.getUriForFile(MainActivity.this,"com.example.camera.fileprovider",file);
}else{
uri=Uri.fromFile(file);
}
Log.w("Tag0",uri+"\n"+getExternalFilesDir(Environment.DIRECTORY_PICTURES));
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,uri);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
}
}
我們發現對Intent的操作多了一行,takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,uri);
這句話大概是将拍攝完成的照片存儲到對應uri的路徑,原來舊版很容易實作,定義好存儲路徑,直接一行代碼,通過檔案擷取路徑就可以了。7.0以上對内容共享做了限制,需要使用到Provider類,然後調用FileProvider類的方法擷取uri,下面給出适配的相關操作:
1.在res目錄下建立xml檔案夾,然後建立根節點為paths的路徑檔案:
file_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="my_images"
path="/Android/data/com.example.camera/files/Pictures/my_pictures"/>
</paths>
external-path标簽就代表着外部存儲,path屬性相當于它之下的目錄,我們可以不再往下寫,直接寫一個"/",其實最終檔案存儲的路徑都一樣,隻是我們得到的Uri不同,那就來對比一下吧:
完整uri

不完整uri
列印的第一行是我們最終調用getUriForFile得到的uri,第二行是getExternalFilesDir(Environment.DIRECTORY_PICTURES)傳回的路徑。
2.在清單檔案中注冊provider:
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.example.camera.fileprovider"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"
/>
</provider>
都是固定用法,注意authorities請使用"包名.fileprovider",貌似後面是什麼影響不大,但是包名必須寫對,在getUriForFile方法中要傳入authorities對應String型參數。
android:grantUriPermissions=“true”,不可以省略,必須為true,不然會報錯。
文獻參考:
Android Developers官方文檔:拍照
這裡面還介紹了如何将照片顯示在相冊(預設情況是不會直接顯示在相冊的),以及圖檔的解碼。