今天做图库图片的裁剪遇到了不少坑,今天记录一下,以下坑位供各位看官参考;

如果有不对之处,欢迎各位看官留言评论!

图片裁剪踩坑锦囊:

问题一:相册裁剪权限问题

解:这个简单,对于Android6.0以上的手机需要进行动态权限的申请:

1>AndroidManifest.xml文件中添加

 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

2>调用之前动态申请

private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static String[] PERMISSIONS_STORAGE = {
"android.permission.READ_EXTERNAL_STORAGE",
"android.permission.WRITE_EXTERNAL_STORAGE"}; /**
* 打开相册
*/
private void openPhotoAlbum(int requestCode) { if (hasPermission()) {
ChoosePicUtil.getInstance().choosePic(getContext(), requestCode);
}
} private boolean hasPermission() { try {
//检测是否有写的权限
int permission = ActivityCompat.checkSelfPermission(getActivity(),
"android.permission.WRITE_EXTERNAL_STORAGE");
if (permission != PackageManager.PERMISSION_GRANTED) {
// 没有写的权限,去申请写的权限,会弹出对话框
ActivityCompat.requestPermissions(getActivity(), PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE);
} else {
return true;
}
} catch (Exception e) {
e.printStackTrace();
} return false;
}  

问题2:android android.system.ErrnoException: open failed: ENOENT (No such file or directory)

对于Android 7 以上的设备加强了文件权限的管理,需要使用FileProvider获取文件(此处是原图片)的uri,值得一提的是如果不使用FileProvider,debug包虽然没有不会crash,但是release包必crash,所以各位看官记得测一测release包。

解决:使用FileProvider:

1>AndroidManifest.xml中配置

<provider
android:authorities="包名.fileProvider"
android:name="android.support.v4.content.FileProvider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"
/>
</provider>

2>在res文件夹中创建xml文件夹,创建file_paths.xml

可以根据自己的图片存储选择对应的path标签,当然,如过不确定,也可以全部选择。

<paths>
<!-- 物理路径为Context.getFilesDir() + /files/* -->
<files-path path="files" name="files" />
<!-- 物理路径为Context.getCacheDir() + /files/* -->
<cache-path path="files" name="cache" />
<!-- 物理路径为Environment.getExternalStorageDirectory() + /files/* -->
<external-path path="files" name="external" />
<!-- 物理路径为Context.getExternalFilesDir(String) + /files/* -->
<external-files-path path="files" name="externalfiles"/>
<!-- 物理路径为Context.getExternalCacheDir() + /files/* -->
<external-cache-path path="files" name="externalcache"/>
<!-- 物理路径为`Context.getExternalMediaDirs() + /files/*, 要求API21+ -->
<external-media-path name="externalmedia" path="files" />
</paths>

注意:如果图片存储路径与我们选择的path标签不一致就会报错,可以根据报错日志找到对应图片存储路径,选择正确的标签。

3>使用FileProvider

FileProvider.getUriForFile(context, "包名.fileProvider", file)   //file: 我们创建的原图片文件  
//注意intent跳转时版本大于N时添加权限
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

  

注意:

这里的包名一定要和AndroidManifest中的包名一致且,如果是组件式开发,建议放在主app module的包中,避免包名不一致问题。 

问题3:裁剪后提示“无法保存裁剪后的图片”

存在以下几种可能

1:图片存储路径问题

2.对于部分机型图片太大会产生该问题,通过设置setOutputX/Y()小一点

3.裁剪后的图片file的uri不能是使用FileProvider生成的uri,即检查一下 imageCropUri

intent.putExtra(MediaStore.EXTRA_OUTPUT, imageCropUri);

源代码如下:

//图片选取工具类

public class ChoosePicUtil {
public static final int CHOOSE_BG_PICTURE_CODE = 1;
public static final int CHOOSE_COVER_PICTURE_CODE = 2;
public static final int CODE_CAMERA_CROP_1_1_REQUEST = 3;
public static final int CODE_CAMERA_CROP_9_16_REQUEST = 4; private PickParams mPickParams; private static ChoosePicUtil mInstance; public static ChoosePicUtil getInstance() {
if (mInstance == null) {
mInstance = new ChoosePicUtil();
}
return mInstance;
} public ChoosePicUtil buildPickParams(PickParams params) {
mPickParams = params;
return this;
} /**
* 裁剪图片
*
* @param uri
*/
public void cropImg(Context context, Uri uri, Uri imageCropUri, int requestCode) {
Intent intent = new Intent("com.android.camera.action.CROP"); //调用裁剪
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
intent.setDataAndType(uri, "image/*");
intent.putExtra("crop", "true");
if (mPickParams == null) {
mPickParams = new PickParams();
}
intent.putExtra("aspectX", mPickParams.aspectX); //设置宽高比例
intent.putExtra("aspectY", mPickParams.aspectY);
intent.putExtra("outputX", mPickParams.outputX); //设置裁剪图片宽高
intent.putExtra("outputY", mPickParams.outputY);
intent.putExtra("return-data", false);
intent.putExtra("scale", true);
intent.putExtra("scaleUpIfNeeded", true); //防止出现黑边框
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageCropUri);
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString()); //设置输出格式
intent.putExtra("noFaceDetection", true); if (context instanceof Activity) {
((Activity) context).startActivityForResult(intent, requestCode);
}
} /**
* 选取图片
* @param context
*/
public void choosePic(Context context, int requestCode) {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/*");
if (context instanceof Activity) {
((Activity) context).startActivityForResult(intent, requestCode);
}
} /**
* 该方法为了适配小米手机裁剪
* 将file://的uri转换为content的content:// 的uri;
* @param context
* @return
*/
public static Uri getImageUri(Context context,Intent data){
String imagePath = null;
Uri uri = data.getData();
if(Build.VERSION.SDK_INT >= 19){
if(DocumentsContract.isDocumentUri(context,uri)){
String docId = DocumentsContract.getDocumentId(uri);
if("com.android.providers.media.documents".equals(uri.getAuthority())){
String id = docId.split(":")[1];
String selection = MediaStore.Images.Media._ID+"="+id;
imagePath = getImagePath(context,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(context,contentUri,null);
}
}else if("content".equalsIgnoreCase(uri.getScheme())){
imagePath = getImagePath(context,uri,null);
}else if("file".equalsIgnoreCase(uri.getScheme())){
imagePath = uri.getPath();
}
}else{
uri= data.getData();
imagePath = getImagePath(context,uri,null);
}
File file = new File(imagePath);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
return FileProvider.getUriForFile(context, "io.liuliu.music.fileProvider", file);
} else {
return Uri.fromFile(file);
}
} private static String getImagePath(Context context,Uri uri, String selection) {
String path = null;
Cursor cursor = context.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;
} public static class PickParams {
private int outputX = 300;
private int outputY = 300;
private int aspectX = 1;
private int aspectY = 1; public PickParams() { } public int getOutputX() {
return outputX;
} public PickParams setOutputX(int outputX) {
this.outputX = outputX;
return this;
} public int getOutputY() {
return outputY;
} public PickParams setOutputY(int outputY) {
this.outputY = outputY;
return this; } public int getAspectX() {
return aspectX;
} public PickParams setAspectX(int aspectX) {
this.aspectX = aspectX;
return this; } public int getAspectY() {
return aspectY;
} public PickParams setAspectY(int aspectY) {
this.aspectY = aspectY;
return this; } }
}

//接收图片返回结果

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (data == null) {
return;
}
if (requestCode == CHOOSE_BG_PICTURE_CODE) { Uri uri = ChoosePicUtil.getImageUri(this, data);
//375 * 900 太大了,部分手机不支持 ChoosePicUtil.PickParams params =
new ChoosePicUtil.PickParams()
.setAspectX(9)
.setAspectY(16)
.setOutputX(270)
.setOutputY(480); imageCropUri = getImageCropUri(CHOOSE_BG_PICTURE_CODE); ChoosePicUtil.getInstance()
.buildPickParams(params)
.cropImg(this, uri, imageCropUri, ChoosePicUtil.CODE_CAMERA_CROP_9_16_REQUEST);
} else if (requestCode == CHOOSE_COVER_PICTURE_CODE) { Uri uri = ChoosePicUtil.getImageUri(this, data); imageCropUri = getImageCropUri(CHOOSE_COVER_PICTURE_CODE); ChoosePicUtil.PickParams params =
new ChoosePicUtil.PickParams()
.setAspectX(1)
.setAspectY(1)
.setOutputX(300)
.setOutputY(300); ChoosePicUtil.getInstance()
.buildPickParams(params)
.cropImg(this, uri, imageCropUri, ChoosePicUtil.CODE_CAMERA_CROP_1_1_REQUEST);
} else if (requestCode == ChoosePicUtil.CODE_CAMERA_CROP_9_16_REQUEST) {
if (imageCropUri != null) {
//do what you want
}
} else if (requestCode == ChoosePicUtil.CODE_CAMERA_CROP_1_1_REQUEST) {
if (imageCropUri != null) {
//do what you want
}
} }

 

Android 图片裁剪踩坑的更多相关文章

  1. [Android] 图片裁剪总结——调用系统裁剪

    花了两天时间看了下android的图片裁剪功能的实现.其实刚开始做这个我挺虚的,以为整个功能都需要自己写出来,但查了些资料,发现android已经提供了裁剪功能,需要的话自己调用就成了.soga,这下 ...

  2. Android Studio安装踩坑

    title: Android Studio安装踩坑 date: 2018-09-07 19:31:32 updated: tags: [Android,Android Studio,坑] descri ...

  3. Android 图片裁剪库 uCrop

    引语 晚上好,我是猫咪,我的公众号「程序媛猫咪」会推荐 GitHub 上好玩的项目,挖掘开源的价值,欢迎关注我. 现在 Android 开发,离不开图片,必然也需要图片裁剪功能,这个实现可以调用系统的 ...

  4. React Native Android配置部署踩坑日记

    万事开头难 作为一只进入ECMAScript世界不久的菜鸟,已经被React Native的名气惊到了,开源一周数万星勾起了我浓烈的兴趣.新年新气象,来个HellWorld压压惊吧^_^(故意少打个' ...

  5. Android图片裁剪之自由裁剪

    我的博客http://blog.csdn.net/dawn_moon 客户的需求都是非常怪的.我有时候在给客户做项目的时候就想骂客户是sb.可是请你相信我,等你有需求,自己变成客户的时候,给你做项目的 ...

  6. Android图片裁剪解决方案 -- 从相册截图

    在看Storage Access Framework,里面有一个加载相册图片的程序片断,可能是系统版本的问题,无法返回结果,这里找到一个适用于旧版本的方法. 在Android开发中,可以轻松调用一个I ...

  7. [Android] 图片裁剪总结——自定义裁剪工具

    上次弄完调用系统裁剪之后,我又试着做一个自定义的裁剪工具. 原文地址请保留http://www.cnblogs.com/rossoneri/p/3988405.html 老习惯,文章开始前还是先把我参 ...

  8. Android WebView 文明踩坑之路

    情景一 webview 以头布局的形式添加到 RecyclerView 中,第一次进入页面,当页面中有 EditText 并且点击时,甚至是类似点赞更换图片.点击 WebView 任意区域,都会造成 ...

  9. Android 之 ToolBar 踩坑笔记

    写在前面 •前言 这两天,学完了 Fragment 的基础知识,正准备跟着<第一行代码>学习制作一个简易版的新闻应用: 嘀嘀嘀~~~ 一声消息传来,像往常一样,打开 QQ,当我看到 QQ ...

随机推荐

  1. 2.5 Hello golang

    编写第一个hello golang 创建空文件hello.go,尝试执行 touch hello.go go run hello.go 产生如下报错 can't load package: packa ...

  2. linux常用命令---打包压缩解压

    打包压缩解压

  3. HTTP响应格式

    HTTP响应格式

  4. 按图索骥,一些mysql知识点

    有事没事多看看 基础知识考察 基础知识,尤其是一些理论知识,例如: MySQL有哪些索引类型,这是个半开放式命题: 从数据结构角度可分为B+树索引.哈希索引.以及不常用的FULLTEXT索引(现在My ...

  5. ie ajax 跨域情况遇到的各种问题

    jQuery.support.cors = true; http://blog.csdn.net/jupiter37/article/details/25694289 jQuery ajax跨域调用出 ...

  6. sqoop-介绍及安装

    1.sqoop概述 sqoop是Apache旗下一款hadoop和关系数据库服务器之间传送数据的工具: 核心的功能: 导入,迁入(从关系型数据库-->hdfs hive hbase) 导出,迁出 ...

  7. Mac打不开inkscape怎么办

    本经验题目提到的是一款矢量图片编辑软件,对于打开不开的软件,完全可以通过卸载软件后进行安装.这里就从安装以及卸载的过程说明一下这个软件的安装卸载过程. 方法/步骤 打开电脑任意一个浏览器图标,进入浏览 ...

  8. mysql 的大文本存储TEXT & BLOB

    TEXT & BLOB 一般在保存少量字符串的时候,我们会选择 CHAR 或者 VARCHAR:而在保存较大文本时,通常会选择使用 TEXT 或者 BLOB,二者之间的主要差别是 BLOB 能 ...

  9. day19 生成器函数

    生成器总结: 语法上和函数类似:生成器函数和常规函数几乎是一样的.它们都是使用def语句进行定义,差别在于,生成器使用yield语句返回一个值,而常规函数使用return语句返回一个值. 自动实现迭代 ...

  10. 微服务项目的docker自动化部署流程

    目录 微服务的Docker自动化部署 制作JDK1.8的Docker镜像 Docker常用命令介绍 制作image的一般流程 将本地的image上传至私人仓库 使用Maven插件实现自动化docker ...