一章贞、前言
最近在開發(fā)中遇到了一個(gè)比較棘手的問題
由于在之前使用的版本-targetSdkVersion小于24也就是小于7.0所以在使用相機(jī)拍照的時(shí)候不會(huì)出現(xiàn)問題祥绞,但是當(dāng)targetSdkVersion版本大于或者等于7.0的時(shí)候用原來(lái)的方法調(diào)用相機(jī)就會(huì)拋出一個(gè)SecurityException安全異常
通過搜索發(fā)現(xiàn)是出于對(duì)系統(tǒng)安全的考慮,在sdk24及以上鸭限,對(duì)相機(jī)的操作需要使用FileProvider才行蜕径。
雖然有些麻煩,但除非用第三方框架败京,不然也只能自己動(dòng)手去解決了兜喻。
二、操作流程
1赡麦、定義全局標(biāo)識(shí)
用于接收?qǐng)D庫(kù)選擇或拍照完成后的結(jié)果回調(diào)
//圖庫(kù)
private static final int PHOTO_TK = 0;
//拍照
private static final int PHOTO_PZ = 1;
//裁剪
private static final int PHOTO_CLIP = 2;
定義全局的uri
private Uri contentUri;
2朴皆、圖庫(kù)操作
這里用的是一個(gè)自定義的dialog
update_dialog_TK.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//調(diào)用系統(tǒng)圖庫(kù),選擇圖片
Intent intent = new Intent(Intent.ACTION_PICK, null);
intent.setDataAndType(
MediaStore.Images.Media.INTERNAL_CONTENT_URI, "image/*");
//返回結(jié)果和標(biāo)識(shí)
startActivityForResult(intent, PHOTO_TK);
dialog.dismiss();
}
});
3泛粹、相機(jī)操作
3.1 Android7.0以下版本
直接調(diào)用系統(tǒng)相機(jī)遂铡,通過日志可以看到會(huì)返回一個(gè)類似
file:///storage/emulated/0/temp.jpg
的文件
update_dialog_PZ.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 啟動(dòng)系統(tǒng)相機(jī)
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// 獲取拍完后的uri
Uri mImageCaptureUri = Uri.fromFile(new File(Environment.getExternalStorageDirectory(), "temp.jpg"));
intent.putExtra(MediaStore.EXTRA_OUTPUT, mImageCaptureUri);
// 返回結(jié)果和標(biāo)識(shí)
startActivityForResult(intent, PHOTO_PZ);
dialog.dismiss();
}
});
3.2 兼容Android7.0以上版本
在新的版本中,Android對(duì)內(nèi)容提供者做了限制晶姊,返回的不再是uri扒接,而需要一個(gè)FileProvider
使用 content://
代替了 file:///
所以如果直接使用原來(lái)的方法就會(huì)報(bào)錯(cuò)。
所以在之前我們要給AndroidManifest文件中application標(biāo)簽添加權(quán)限
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="你的包名.fileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
并且需要?jiǎng)?chuàng)建一個(gè)xml
(可以在上面android:resource="@xml/provider_paths"
直接使用快捷鍵alt+enter創(chuàng)建们衙,然后將代碼拷貝進(jìn)去)
external-path標(biāo)簽用來(lái)指定Uri共享钾怔,name屬性的值可以自定義,path屬性的值表示共享的具體位置
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="."/>
</paths>
然后在調(diào)用系統(tǒng)相機(jī)的時(shí)候判斷
update_dialog_PZ.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 啟動(dòng)系統(tǒng)相機(jī)
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
Uri mImageCaptureUri;
// 判斷7.0android系統(tǒng)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
//臨時(shí)添加一個(gè)拍照權(quán)限
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
//通過FileProvider獲取uri
contentUri = FileProvider.getUriForFile(UpdatePhotoActivity.this,
"你的包名.fileProvider",
new File(Environment.getExternalStorageDirectory(), "temp.jpg"));
intent.putExtra(MediaStore.EXTRA_OUTPUT, contentUri);
} else {
mImageCaptureUri = Uri.fromFile(new File(Environment.getExternalStorageDirectory(), "temp.jpg"));
intent.putExtra(MediaStore.EXTRA_OUTPUT, mImageCaptureUri);
}
startActivityForResult(intent, PHOTO_PZ);
dialog.dismiss();
}
});
4蒙挑、 onActivityResult
使用onActivityResult接收操作完成的回調(diào)
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK) {
switch (requestCode) {
case PHOTO_PZ:
//獲取拍照結(jié)果蒂教,執(zhí)行裁剪
Uri pictur;
//如果是7.0android系統(tǒng),直接獲取uri
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
pictur = contentUri;
} else {
pictur = Uri.fromFile(new File(
Environment.getExternalStorageDirectory() + "/temp.jpg"));
}
startPhotoZoom(pictur);
break;
case PHOTO_TK:
//獲取圖庫(kù)結(jié)果脆荷,執(zhí)行裁剪
startPhotoZoom(data.getData());
break;
case PHOTO_CLIP:
//裁剪完成后的操作凝垛,上傳至服務(wù)器或者本地設(shè)置
break;
}
}
}
5懊悯、裁剪
當(dāng)拍照完成后或者本地選擇圖片完畢之后會(huì)執(zhí)行該方法。同時(shí)也做了7.0適配
/**
* 裁剪圖片的方法.
* 用于拍照完成或者選擇本地圖片之后
*/
private Uri uritempFile;
public void startPhotoZoom(Uri uri) {
Log.e("uri=====", "" + uri);
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(uri, "image/*");
intent.putExtra("crop", "true");
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
intent.putExtra("outputX", 60);
intent.putExtra("outputY", 60);
//uritempFile為Uri類變量梦皮,實(shí)例化uritempFile
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
//開啟臨時(shí)權(quán)限
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
//重點(diǎn):針對(duì)7.0以上的操作
intent.setClipData(ClipData.newRawUri(MediaStore.EXTRA_OUTPUT, uri));
uritempFile = uri;
} else {
uritempFile = Uri.parse("file://" + "/" + Environment.getExternalStorageDirectory().getPath() + "/" + "small.jpg");
}
intent.putExtra(MediaStore.EXTRA_OUTPUT, uritempFile);
intent.putExtra("return-data", false);
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
intent.putExtra("noFaceDetection", true);
startActivityForResult(intent, PHOTO_CLIP);
}
三炭分、裁剪后的處理
在onActivityResult方法還有一個(gè)最后的處理,前面只是在給圖片做操作剑肯,下面是將完成后的圖片選擇加載到本地或者上傳到項(xiàng)目的服務(wù)端捧毛,一般在實(shí)際開發(fā)中用的比較多
1、加載至本地
使用Picasso加載让网,因?yàn)镻icasso支持Uri呀忧、File、Stirng類型溃睹,不需要做進(jìn)一步的轉(zhuǎn)換就可以直接用而账。
Picasso.with(this)
.load(uritempFile)
.into(cardviewImg);
2、轉(zhuǎn)成File并壓縮因篇、上傳
在實(shí)際開發(fā)中有時(shí)候需要對(duì)圖片進(jìn)行進(jìn)一步的處理泞辐,比如傳到服務(wù)器需要File類型的文件,所以就需要進(jìn)行再一次的轉(zhuǎn)換竞滓。
具體可以根據(jù)需求來(lái)進(jìn)行相應(yīng)的操作
//裁剪后的圖像轉(zhuǎn)成BitMap
photo1 = BitmapFactory.decodeStream(getContentResolver().openInputStream(uritempFile));
//創(chuàng)建路徑
String path = Environment.getExternalStorageDirectory()
.getPath() + "/Pic";
//獲取外部?jī)?chǔ)存目錄
file = new File(path);
Log.e("file", file.getPath());
//創(chuàng)建新目錄
file.mkdirs();
//以當(dāng)前時(shí)間重新命名文件
long i = System.currentTimeMillis();
//生成新的文件
file = new File(file.toString() + "/" + i + ".png");
Log.e("fileNew", file.getPath());
//創(chuàng)建輸出流
OutputStream out = new FileOutputStream(file.getPath());
//壓縮文件咐吼,返回結(jié)果
boolean flag = photo1.compress(Bitmap.CompressFormat.JPEG, 100, out);
項(xiàng)目demo地址:https://github.com/wapchief/android-CollectionDemo
相關(guān)文章:
Android版本相機(jī)適配問題集合(不斷整理更新中)