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 ...
随机推荐
- jQuery的插件和跨域、ajax
1. 插件: 也称组件 什么是: 拥有专属的HTML,CSS和js的独立页面区域 为什么: 重用! 何时: 只要一个功能/区域可能被反复使用时 如何: 3个来源: 1. 官方插件:jquery ui ...
- select 下拉模糊查询
http://ivaynberg.github.io/select2/ https://github.com/ https://github.com/ivaynberg.github.io/selec ...
- wannafly挑战赛4树的距离 离线处理,dfs序
时间限制:C/C++ 2秒,其他语言4秒空间限制:C/C++ 262144K,其他语言524288K 64bit IO Format: %lld 题目描述 wyf非常喜欢树.一棵有根数树上有N个节点, ...
- POJ2516
题目链接:http://poj.org/problem?id=2516 解题思路: 最小费用最大流,这个没什么疑问.但此题小难点在于读题,大难点在于建图. 首先,供应量小于需求量的时候直接输出“-1” ...
- 【真相揭秘】requests获取网页编码乱码本质
有没有被网页编码抓狂,怎么转都是乱码. 通过查看requests源代码,才发现是库本身历史原因造成的. 作者是严格http协议标准写这个库的,<HTTP权威指南>里第16章国际化里提到,如 ...
- PIC16F887的LCD
RS RA5 RW RA4 RD RA3 将引脚设置为输出的时候要对ANS5 ANS4 ANS3 设置为0
- [wordpress使用]004_导入多媒体
在写文章的时候难免要需要用到图片.音频或者视频文件,wordpress不仅提供本地上传多媒体文件功能,更提供在线导入多媒体.能更方便,范围更大的获取我们所需要的资源. 本地上传文件 在写文章的界面,选 ...
- [PHP学习教程 - 文件]002.修改上传文件大小限制(File Upload Limit)
引言:通常大家直装xampp之后,默认的文件上传大小应该被设定成2M左右,这个时候如果上传超过2M的东西,就会报错,让人非常尴尬.如何修改呢? 导航索引: 概念 FTP常用API FTP封装类 其他 ...
- 【asp.net core 系列】 1 带你了解一下asp.net core
0. 前言 这是一个新的系列,名字是<ASP.NET Core 入门到实战>.这个系列主讲ASP.NET Core MVC,辅助一些前端的基础知识(能用来实现我们需要的即可,并非主讲).同 ...
- SDL初识
1.SDL是什么? SDL(Security Development Lifecycle)安全开发生命周期.是微软提出的从安全角度指导软件开发的管理模式,在软件开发的生命周期中尽可能地发现安全隐患,降 ...