Android 图片裁剪踩坑
今天做图库图片的裁剪遇到了不少坑,今天记录一下,以下坑位供各位看官参考;
如果有不对之处,欢迎各位看官留言评论!
图片裁剪踩坑锦囊:
问题一:相册裁剪权限问题
解:这个简单,对于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 图片裁剪踩坑的更多相关文章
- [Android] 图片裁剪总结——调用系统裁剪
花了两天时间看了下android的图片裁剪功能的实现.其实刚开始做这个我挺虚的,以为整个功能都需要自己写出来,但查了些资料,发现android已经提供了裁剪功能,需要的话自己调用就成了.soga,这下 ...
- Android Studio安装踩坑
title: Android Studio安装踩坑 date: 2018-09-07 19:31:32 updated: tags: [Android,Android Studio,坑] descri ...
- Android 图片裁剪库 uCrop
引语 晚上好,我是猫咪,我的公众号「程序媛猫咪」会推荐 GitHub 上好玩的项目,挖掘开源的价值,欢迎关注我. 现在 Android 开发,离不开图片,必然也需要图片裁剪功能,这个实现可以调用系统的 ...
- React Native Android配置部署踩坑日记
万事开头难 作为一只进入ECMAScript世界不久的菜鸟,已经被React Native的名气惊到了,开源一周数万星勾起了我浓烈的兴趣.新年新气象,来个HellWorld压压惊吧^_^(故意少打个' ...
- Android图片裁剪之自由裁剪
我的博客http://blog.csdn.net/dawn_moon 客户的需求都是非常怪的.我有时候在给客户做项目的时候就想骂客户是sb.可是请你相信我,等你有需求,自己变成客户的时候,给你做项目的 ...
- Android图片裁剪解决方案 -- 从相册截图
在看Storage Access Framework,里面有一个加载相册图片的程序片断,可能是系统版本的问题,无法返回结果,这里找到一个适用于旧版本的方法. 在Android开发中,可以轻松调用一个I ...
- [Android] 图片裁剪总结——自定义裁剪工具
上次弄完调用系统裁剪之后,我又试着做一个自定义的裁剪工具. 原文地址请保留http://www.cnblogs.com/rossoneri/p/3988405.html 老习惯,文章开始前还是先把我参 ...
- Android WebView 文明踩坑之路
情景一 webview 以头布局的形式添加到 RecyclerView 中,第一次进入页面,当页面中有 EditText 并且点击时,甚至是类似点赞更换图片.点击 WebView 任意区域,都会造成 ...
- Android 之 ToolBar 踩坑笔记
写在前面 •前言 这两天,学完了 Fragment 的基础知识,正准备跟着<第一行代码>学习制作一个简易版的新闻应用: 嘀嘀嘀~~~ 一声消息传来,像往常一样,打开 QQ,当我看到 QQ ...
随机推荐
- Jenkins-gogs安装及使用
很多同学可能第一次了解什么是ci-cd,什么是Jenkins,首先会介绍下cicd的概念及应用场景,之后再详细介绍下Jenkins的概念.安装及使用. 什么是CI-CD? 首先明确CI-CD是一种技术 ...
- 笔记二(JavaWeb)
上一个笔记写的好累,这次换Markdown试试 缺省适配器设计模式:父类不实现该方法,让子类去实现(抽象方法) 模板方法设计模式:定义一个操作中的方法骨架,而将一些步骤延迟到子类中.模板方法使得子类可 ...
- PAT-1135 Is It A Red-Black Tree(二叉查找树的创建和遍历)
There is a kind of balanced binary search tree named red-black tree in the data structure. It has th ...
- 洛谷P2765 魔术球问题
题目链接:https://www.luogu.org/problemnew/show/P2765 知识点: 最大流 解题思路: 本题所有边的容量均为 \(1\). 从 \(1\) 开始加入数字,将这个 ...
- 模板:DOM常用场景【表单提交】——javascript结合HTML DOM(或者JQuery)运用
一.删除行为前的提示 首先要有一个onclick的DOM(点击)事件,和一个JavaScript弹出框:confirm()确认框 <script> function del(){ var ...
- let面试题
两次输出结构都是2 0 1
- C#网络编程入门之UDP
目录: C#网络编程入门系列包括三篇文章: (一)C#网络编程入门之UDP (二)C#网络编程入门之TCP (三)C#网络编程入门之HTTP 一.概述 UDP和TCP是网络通讯常用的两个传输协议,C# ...
- Redis学习笔记(1)
一.NoSQL基础知识 1. NoSQL概念 NoSQL(NoSQL = Not Only SQL ),意即“不仅仅是SQL”,泛指非关系型的数据库.随着互联网web2.0网站的兴起,传统的关系数据库 ...
- css 盒模型、box-sizing 学习笔记
默认情况下,给元素设置的高度和宽度是元素内容区的宽度和高度,给元素加padding 和 border ,元素的实际宽度和高度的计算方式是下面的两个公式: 元素的宽度= 元素的内容区宽度 + 内边距宽度 ...
- xxshenqi分析报告
背景 今年七夕爆发了一场大规模手机病毒传播,apk的名字叫做xxshenqi.中了这个病毒的用户会群发手机所有联系人一条信息,内容是包含这个apk下载的链接,同时用户的联系人信息和短信会被窃取,造成隐 ...