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 ...
随机推荐
- 接触Ubuntu的第一周大致总结
VB上安装,很简单,和入门的帮助,man,--help. 基础的分区,FDISK,文件系统,目录 ,LS,cd,文件操作mkdir,rm,cp等,文件查看,cat,tac,less,等,文件查找loc ...
- JSONObject转换为map
http://cloudyxuq.iteye.com/blog/1618213 private static HashMap<String, String> toHashMap(Objec ...
- 201771010128 王玉兰《面象对象程序设计 (Java) 》第六周学习总结
---恢复内容开始--- 第一部分:基础知识总结: 1.继承 A:用已有类来构建新类的一种机制,当定义了一个新类继承一个类时,这个新类就继承了这个类的方法和域以适应新的情况: B:特点:具有层次结构. ...
- PAT-1058 A+B in Hogwarts (进制转换)
1058. A+B in Hogwarts If you are a fan of Harry Potter, you would know the world of magic has its ow ...
- GYM100962A ABBA
题目链接:https://vjudge.net/problem/Gym-100962A 题目大意: 给出一个 \(h \times w\) 的目标矩阵.定义一种 \(h \times w\) 的矩阵, ...
- Spring MVC介绍和第一个例子
1.Spring mvc概述 spring mvc是spring提供给web应用框架设计,实际上MVC框架是一个设计理念.它不仅存在java世界中而且广泛在于各类语言和开发中,包括web的前端应用.对 ...
- 【Ubuntu】Ubuntu系统启动过程中,输入用户名与密码后登录一直卡在紫色界面问题(未解决,最后通过重装系统)
0. 前言 由于本电脑为公用电脑,可能由于其他人点了图像界面中推荐的内核更新,导致原来安装的NVIDIA显卡驱动 430 与升级后的 5.0 内核不兼容,从而导致输入用户名后登录一直卡在紫色界面.在排 ...
- 一篇文章,全面掌握Git
版本控制 版本控制就是记录项目文件的历史变化.它为我们查阅日志,回退,协作等方面提供了有力的帮助. 版本控制一般分为集中化版本控制和分布式版本控制. 集中化主要的版本数据都保存服务端. 分布式版本数据 ...
- Codeforces Round #646 (Div. 2)【C. Game On Leaves 题解】
题意分析 关于这道题,意思就是两个人摘叶子,谁最后摘到编号为x的谁就赢了.既然是叶子,说明其最多只有一个分支,由于题目上说了是无向图,那就是度数小于等于的节点.也就是一步步移除度数小于等于的节点,直到 ...
- Chisel3 - util - LockingArbiter
https://mp.weixin.qq.com/s/5oAwH3scumARzPidRBfG2w 带锁多入单出仲裁器,输出会被锁定指定的时钟周期. 参考链接: https://githu ...