为什么会突然想到 选择图片,还有裁剪什么的,
是因为突然看到。联系人模块(6.0),创建用户的时候,当填写完用户信息,他还可以让用户选择自己的头像,当然也是点击选择图片,裁剪了, 但是我看到的是他用到了一个之前没怎么注意过的类 FilePorvider, 查了一下,也试着使用
这样子用 FilePorvider 有什么好处呢, 我看到的模块源码是,实质是将裁剪后的图片保存在了 /data/data/包名/cache 目录下了, 这个样子想想也能想通,毕竟把这个头像数据保存在cache文件夹下了,并且用的是fileprovider,这样其他模块app如果需要获取联系人的信息的话,头像的数据就可以直接从 联系人模块中获取了。
==先把问题放在上面==
测试:
小米的机子是好的 6.0, 移动的一款也是好的 6.0系统(2种跳转方式选择图片 裁剪都是好的)
移动的另外一款机子不行直接权限错误 6.0系统(选完图片后,去裁剪时,跟前面的跳转方式有关,如果用下面跳转方式的,第一种方式,进入com.android.documentsui 模块,然后用FileProvider 进行一系列操作, 在裁剪startActivityResult 就会报错,说是权限不足),但是联系人是好着的,我也不知道这个权限要怎么加 在哪里加,能加的权限都加了
大神看到了一定要帮我解决一下
错误如下:
uri content://media/external/images/media/87 from pid=10707, uid=10108 requires android.permission.READ_EXTERNAL_STORAGE, or grantUriPermission()
java.lang.SecurityException: Uid 10108 does not have permission to uri 0 @ content://com.android.providers.media.documents/document/image%3A71
一、 选择图片首先是跳转选择图片有2种
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
startActivityForResult(intent, 2)
有些机子直接进入的是这个模块 com.android.documentsui
2. Intent.ACTION_PICK
Intent i = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(i,2);
选择图库进入 com.android.gallery3d 模块
选择相册进入 com.google.android.apps.photos 模块
选择图片后, 会返回uri类似
uri: content://media/external/images/media/203474
uri.getpath: /external/images/media/203474
二、 进入裁剪图片模式
2.1 普通正常的裁剪图片方式
intent.setDataAndType(in, "image/*");
intent.putExtra("crop", "true");
intent.putExtra("scale", true);
intent.putExtra("scaleUpIfNeeded", true);
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
// outputX,outputY 是剪裁图片的宽高
intent.putExtra("outputX", 150);
intent.putExtra("outputY", 150);
// intent.putExtra("return-data", true);
startActivityForResult(intent, 3);
裁剪完成后 会返回uri 也就是 裁剪后的图片的uri
Intent { dat=content://media/external/images/media/84 (has extras) }
content://media/external/images/media/84
2.2 用 FileProvider (这个才是重点)
这是模块创建文件的代码
//创建文件名称
private static final String PHOTO_DATE_FORMAT = "'IMG'_yyyyMMdd_HHmmss";
private static String generateTempCroppedPhotoFileName() {
final Date date = new Date(System.currentTimeMillis());
SimpleDateFormat dateFormat = new SimpleDateFormat(PHOTO_DATE_FORMAT, Locale.US);
return "ContactPhoto-" + dateFormat.format(date) + "-cropped.jpg";
}
//路径 ontext.getCacheDir()
private static String pathForTempPhoto(Context context, String fileName) {
final File dir = context.getCacheDir();
dir.mkdirs();
final File f = new File(dir, fileName);
return f.getAbsolutePath();
}
//用 FileProvider 获取uri
public Uri getOutUri(Context context){
// com.example.aaaa.fileprovider
final String fileProviderAuthority ="com.example.aaaa.fileprovider" ;
File ff = new File(pathForTempPhoto(context, generateTempCroppedPhotoFileName()));
Uri uri = FileProvider.getUriForFile(context, fileProviderAuthority,ff);
return uri;
}
//这里设置 intent 把 fileprovider 生成的参数传过去
out = getOutUri(MainActivity.this);
intent.setDataAndType(in, "image/*");
//设置输出文件
intent.putExtra(MediaStore.EXTRA_OUTPUT, out );
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setClipData(ClipData.newRawUri(MediaStore.EXTRA_OUTPUT, out ));
intent.putExtra("crop", "true");
intent.putExtra("scale", true);
intent.putExtra("scaleUpIfNeeded", true);
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
// outputX,outputY 是剪裁图片的宽高
intent.putExtra("outputX", 150);
intent.putExtra("outputY", 150);
// intent.putExtra("return-data", true);
startActivityForResult(intent, 3);
//有可能 onActivityresult 有时候 uri是有路径的, 有时候是null, 可以先对
onActivityresult 中的
intent.getdata() 进行判断 ,或者直接用前面生成好的路径uri。去取数据,看看有没有
final ContentResolver cr = this.getContentResolver();
new Thread(){
public void run() {
try {
// FileProvider 提供的数据
//bmp = BitmapFactory.decodeStream(cr.openInputStream(out), null ,options);
bmp = BitmapFactory.decodeStream(cr.openInputStream( out ));
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.println("小米======exception==");
e.printStackTrace();
}
aa = new BitmapDrawable(bmp) ;
han.sendEmptyMessage(2);
};
}.start();
在res文件夹下建立 xml文件夹
随便输个名字,一会要配置到 AndroidManifest.xml 配置文件
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<cache-path name="my_cache" />
paths>
在AndroidManifest.xml中配置
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="@string/fileprovider_authority" //这里是作者名称,随便写
android:exported="false"
android:grantUriPermissions="true" >
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" /> //这个名字就是xml文件夹中新建的文件的名称
provider>
PS:看网上的也还有一种方式是这样
// Uri.fromFile(new File("/mnt/sdcard/temp"))
// Uri.parse("file://" + Environment.getExternalStorageDirectory().getPath() + "/" +"aaaa.jpg");
//但是这样的方式的 uri其实会有问题, 有的手机会报
java.io.FileNotFoundException: /storage/emulated/0/aa.jpg: open failed: EACCES (Permission denied)
// 路径(关于 android 文件存储文件路径 http://blog.csdn.net/kongbaidepao/article/details/52317252 )
应该改成这样:
//2种方式生成uri
out= Uri.fromFile(new File(this.getExternalCacheDir()+ "/" + "smalaaaal.jpg"));
//实际路径为 file:///storage/emulated/0/Android/data/com.example.aaaa/cache/smalaaaal.jpg
out= Uri.parse( "file://" +this.getExternalCacheDir()+ "/" + "smalaaaal.jpg");
这里注意不能写成
out= Uri.parse(this.getExternalCacheDir()+ "/" + "smalaaaal.jpg");
会报:
FileNotFoundException: No content provider: /storage/emulated/0/Android/data/com.example.aaaa/cache
// (MediaStore.EXTRA_OUTPUT 的值是 output
intent.putExtra(MediaStore.EXTRA_OUTPUT, out);
//intent.putExtra("outputFormat", "JPEG");// 返回格式
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
intent.putExtra("noFaceDetection", true);