在Android 7.0以上,在相机拍照 , 调用在对图片裁剪上,可能会碰到以下一些错误:
主要是由于在Android 7.0以后,用了Content Uri 替换了原本的File Uri,故在targetSdkVersion=24的时候,部分 “`Uri.fromFile()“` 方法就不适用了。 **File Uri 与 Content Uri 的区别** - File Uri 对应的是文件本身的存储路径 - Content Uri 对应的是文件在Content Provider的路径 所以在android 7.0 以上,我们就需要将File Uri转换为 Content Uri。具体转换方法如下:
开始写入标识 以及 函数值
protected static final int CHOOSE_PICTURE = 0;
protected static final int TAKE_PICTURE = 1;
private static final int CROP_SMALL_PICTURE = 2;
private File tempFile;
private Uri cropImageUri;
第一:首先我们设置 本地图库 与 相机的点击事件:
/**
* 显示会话框,选择
*/
public void showChoosePicDialog(View view) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("设置头像");
String[] items = { "选择本地照片", "拍照" };
builder.setNegativeButton("取消", null);
builder.setItems(items, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case CHOOSE_PICTURE: // 选择本地照片
Intent openAlbumIntent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
openAlbumIntent.setType("image/*");
startActivityForResult(openAlbumIntent, CHOOSE_PICTURE);
break;
case TAKE_PICTURE: // 拍照
takecamera();
break;
}
}
});
builder.create().show();
}
第二步: 对我们调用系统相机
/**
* 相机7.0适配7.0 手机适配 文件存储地址
*/
private void takecamera() {
String status= Environment.getExternalStorageState();
if(status.equals(Environment.MEDIA_MOUNTED))
{
tempFile=new File(getExternalCacheDir(),getPhotoFileName());//SD卡的应用关联缓存目录
try {
if(tempFile.exists()){
tempFile.delete();
}
tempFile.createNewFile();
} catch (Exception e) {
Toast.makeText(InfomationActivity.this, "没有找到储存目录",Toast.LENGTH_LONG).show();
}
}else {
Toast.makeText(InfomationActivity.this, "没有储存卡",Toast.LENGTH_LONG).show();
}
Intent cameraintent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// 指定调用相机拍照后照片的储存路径
Uri uri=null;
if (Build.VERSION.SDK_INT >= 24) {
uri = FileProvider.getUriForFile(InfomationActivity.this, BuildConfig.APPLICATION_ID + ".myFileProvider", tempFile);
}else {
uri = Uri.fromFile(tempFile);
}
cameraintent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
startActivityForResult(cameraintent, TAKE_PICTURE);
}
//使用系统当前日期加以调整作为照片的名称
private String getPhotoFileName() {
Date date = new Date(System.currentTimeMillis());
SimpleDateFormat dateFormat = new SimpleDateFormat("'IMG'_yyyyMMdd_HHmmss");
return dateFormat.format(date) + ".jpg";
}
第三步:返回调用回来的信息:
/**
* 手机拍摄返回结果
* @param requestCode
* @param resultCode
* @param data
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case TAKE_PICTURE:
if (resultCode == RESULT_OK) {
Uri uri=null;
if(Build.VERSION.SDK_INT >= 24) {
uri = FileProvider.getUriForFile(InfomationActivity.this,BuildConfig.APPLICATION_ID+".myFileProvider",tempFile);
startPhotoZoom(uri);
}else {
uri = Uri.fromFile(tempFile);
startPhotoZoom(uri);
}
}
break;
case CHOOSE_PICTURE:
if (resultCode == RESULT_OK) {
startPhotoZoom(data.getData());
}
break;
case CROP_SMALL_PICTURE:
if(resultCode==RESULT_OK) {
if (data != null) {
try {
Bitmap bp = BitmapFactory.decodeStream(getContentResolver().openInputStream(cropImageUri));
circleIcon.setImageBitmap(bp);
uploadPic(bp);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
break;
}
}
第四步:手机图片裁切
/**
* 7.0 适配7.0 以上手机 裁切图片
* @param
*/
private void startPhotoZoom(Uri uri) {
File CropPhoto=new File(getExternalCacheDir(),"crop.jpg");
try{
if(CropPhoto.exists()){
CropPhoto.delete();
}
CropPhoto.createNewFile();
}catch(IOException e){
e.printStackTrace();
}
cropImageUri=Uri.fromFile(CropPhoto);
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(uri, "image/*");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); //添加这一句表示对目标应用临时授权该Uri所代表的文件
}
// 下面这个crop=true是设置在开启的Intent中设置显示的VIEW可裁剪
intent.putExtra("crop", "true");
intent.putExtra("scale", true);
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
//输出的宽高
intent.putExtra("outputX", 200);
intent.putExtra("outputY", 200);
intent.putExtra("return-data", false);
intent.putExtra(MediaStore.EXTRA_OUTPUT, cropImageUri);
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
intent.putExtra("noFaceDetection", true); // no face detection
startActivityForResult(intent, CROP_SMALL_PICTURE);
}
第五步:上传服务器 我这Retrofit+Rxjava上传上传不是File 而是 MultipartBody.Part body :
具体就 RequestBody
//创建RequestBody 封装file参数
RequestBody fileBody = RequestBody.create(MediaType.parse("multipart/form-data"), file);
//创建RequestBody 设置类型等
MultipartBody.Part requestBody = MultipartBody.Part.createFormData("icon", file.getName(), fileBody);
/**
* 上传到服务器
* @param bitmap
*/
private void uploadPic(Bitmap bitmap) {
// ... 可以在这里把Bitmap转换成file,然后得到file的url,做文件上传操作
// 注意这里得到的图片已经是圆形图片了
// bitmap是没有做个圆形处理的,但已经被裁剪了
String imagePath = ImageWzqUtils.savePhoto(bitmap, Environment
.getExternalStorageDirectory().getAbsolutePath(), String
.valueOf(System.currentTimeMillis()));
Log.e("imagePath", imagePath+"");
if(imagePath != null){
// 拿着imagePath上着imagePath上传了
// Bitmap bm = BitmapFactory.decodeFile(imagePath);
File file=new File(imagePath);
OkHttpClient okHttpClient=new OkHttpClient();
//创建RequestBody 封装file参数
RequestBody fileBody = RequestBody.create(MediaType.parse("multipart/form-data"), file);
//创建RequestBody 设置类型等
MultipartBody.Part requestBody = MultipartBody.Part.createFormData("icon", file.getName(), fileBody);
persenter.getUpdatePic(requestBody);
}
}
这是因为拍照存储的文件,也需要以Content Uri的形式,故采用以下办法解决:
Step.1
修改AndroidManifest.xml
<application
...>
<provider
<!--Android中解决7.0权限FileUriPermission-->
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.myFileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>
Step.2
在res/xml/下新建file_paths.xml文件
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<root-path name="root" path="." />
<files-path name="files" path="." />
<cache-path name="cache" path="." />
<external-path name="external" path="." />
<external-files-path name="external_files" path="." />
<external-cache-path name="external_cache" path="." />
<external-path name="external_files" path="."/>
</paths>
另外我也b把7.0的FileUriPermissCompat这个也分享一下:
public class FileUriPermissionCompat {
private static final String TAG = FileUriPermissionCompat.class.getSimpleName();
//此处需要改成AndroidManifest.xml中申请的对应的provider的值
private static final String AUTHORITIES = BuildConfig.APPLICATION_ID +".myFileProvider";
/**
* 是否需要适配7.0权限
* @return
*/
public static boolean isNeedAdapt() {
//24以上版本
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
}
//适应Uri授权权限
public static Uri adaptUriAndGrantPermission(Context context, Intent intent, File file) {
Uri uri = adaptUri(context, file);
if (uri == null) {
return null;
}
grantUriPermission(context, intent, uri);
return uri;
}
public static Uri adaptUri(Context context, File file) {
if (context == null || file == null) {
return null;
}
//网络路径的特殊处理,不需要7.0适配,但必须用parse()方法
if (file.getPath().startsWith("http")) {
return Uri.parse(file.getPath());
}
Uri uri = null;
try {
if (isNeedAdapt()) {
//需要7.0特殊适配;通过系统提供的FileProvider类创建一个content类型的Uri对象
uri = FileProvider.getUriForFile(context, AUTHORITIES, file);
} else {
//不需要适配
uri = Uri.fromFile(file);
}
} catch (Exception e) {
Log.e(TAG, "authorities value error, so can't convert uri !");
e.printStackTrace();
}
return uri;
}
/**
* 对第三方应用赋予对uri读写的权限
* @param context
* @param intent
* @param saveUri 适配后的uri
*/
public static void grantUriPermission(Context context, Intent intent, Uri saveUri) {
if (!isNeedAdapt()) {
return;
}
if (context == null || intent == null || saveUri == null) {
return;
}
//网络路径的特殊处理,不需要权限
if (saveUri.getScheme() != null && saveUri.getScheme().startsWith("http")) {
//不需要授权
return;
}
//1、授权(系统相册、相机、裁剪时需要) -- 这种写法待分析
List<ResolveInfo> resolveInfos = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resolveInfos) {
String packageName = resolveInfo.activityInfo.packageName;
if (TextUtils.isEmpty(packageName)) {
continue;
}
context.grantUriPermission(packageName, saveUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
//2、授权(安装apk时需要)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
/**
* 取消URi权限
* @param context
* @param intent
* @param saveUri
*/
public static void revokeUriPermission(Context context, Intent intent, Uri saveUri) {
if (!isNeedAdapt()) {
return;
}
if (context == null || intent == null || saveUri == null) {
return;
}
//网络路径的特殊处理,不需要权限
if (saveUri.getScheme() != null && saveUri.getScheme().startsWith("http")) {
//不需要授权
return;
}
try {
context.revokeUriPermission(saveUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
} catch (Exception e) {
e.printStackTrace();
}
}
目前也就这么多。搞定了7.0手机截图以及拍照适配。