Android 头像选择(拍照、相册裁剪),含7.0的坑
2017-08-30 11:42
489 查看
作者:夏至,欢迎转载,但请保留这段申明,谢谢。
http://blog.csdn.net/u011418943/article/details/77712662
首先,好规则,看看自己的实现效果:
当然,这个github 各种开源库,这里只讲 Android 自带的功能。
其实这个也不难,关键点无非就2个:
7.0 之后相机的 uri 获取
裁剪时的 uri 获取
这里可以放一下底部 popupwindow 的布局,另外可以看我的 popupwindow封装:
拒绝无用功,封装一个通用的 PopupWindow
而头像,我用的时开源框架,circleiamgeview,这个用来处理圆形头像就可以了,这里就不贴出来了,链接如下:
其中点击事件如下:
图片:
相机:
在拍照这里,7.0 是需要做处理的,google 在7.0 之后,考虑到安全性的问题,用 fileProvider 来封装 uri了,所以,这里我们也处理一下,mCameraUri 是用来保存拍照后,照片的 uri,可以简单理解成该图片的索引。
注意!!!如果是6.0的手机,请添加权限申请,我这里已经封装好,就不贴出来干扰大家了。
既然上面填写了 fileprovider 了,那么用过content provider的都知道,我们得配置 provider 属性了,如下,在你的 Androidmanifest.xml 下添加:
上面得 authorities 必须跟你刚才在 getUriForFile 得authorities 一致,exported 这里填写 false,不然又得来权限问题了,grantUriPermissions 表示授予临时访问,然后再在下面添加一个 mete-data 标签,用来提供 fileprovider 的共享路径。
file_paths 名字随便取,然后在新建一个 xml 文件夹,席间 file_paths 文件填写如下代码:
解释如下:
external-path 表示用来指定共享路径的
name 随便取,只是一个标签,买个关子,下面会解释
path 这个比较重要,如果不设置,则表示将整个 SD 卡进行共享,然后制定了,比如 path=”Pictrues”那么,就只共享 sd卡下的 Pictures 文件夹
既然都配置好了,我们把上面的 mCameraUri 打印一下,如下所示:
看到这个 camera_photos 了吧,其实它就是一个 虚拟目录,可以不管的。这个就是 7.0 之后 uri 经过封装后的样子。
拍照之后,就是要处理获取的图片了,在 onActivityReslut 中的处理也非常简单,既然不管是从相册,还是从相册,我们都获取到了正确的 uri,那么,在获取到 这个 uri 之后,启动裁剪的 activity 就可以了,如下:
可以看到,裁剪分为两个,一个是从相册中获取,另一个则是拍照之后,再来裁剪的。不一样的地方,往下看:
相册裁剪:
相机裁剪:
上面两个,相似度极高,不同的是,相册是直接获取 uri,而相机,则是通过拍照之后,获取的 uri,你这里可以合并一下,只是参数不一样而已,这里我为了让大家有个对比的效果,就分开出来了。
上面一个,注意到用了 intent.putExtra(“return-data”,false);如果这里设置为 true,那么,返回是就是一个 btimap,但这样做在内存不足的情况下,会报 OOM 的,所以,像这种大图片的,我们一般是在本地生成一个裁剪之后的图片,然后获取到这个裁剪之后的 uri,再解析出来,才是正确的做法,所以这里设置为false,并用 mcuturi 来作为这个本地存储的 uri。
这样就讲完了。
这里推荐一下,github,我觉得还不错的两个裁剪开源库,SmartCropper,链接如下:
TakePhoto :
http://blog.csdn.net/u011418943/article/details/77712662
首先,好规则,看看自己的实现效果:
当然,这个github 各种开源库,这里只讲 Android 自带的功能。
其实这个也不难,关键点无非就2个:
7.0 之后相机的 uri 获取
裁剪时的 uri 获取
这里可以放一下底部 popupwindow 的布局,另外可以看我的 popupwindow封装:
拒绝无用功,封装一个通用的 PopupWindow
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/pop_root_ly" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="5dp" android:orientation="vertical"> <android.support.v7.widget.CardView android:layout_width="match_parent" android:layout_height="wrap_content" app:cardCornerRadius="5dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:id="@+id/pop_pic" android:layout_width="match_parent" android:layout_height="40dp" android:gravity="center" android:text="@string/pic" android:textColor="@color/black" android:textSize="18sp" /> <View android:layout_width="match_parent" android:layout_height="1dp" android:background="?android:attr/listDivider" android:padding="2dp" /> <TextView android:id="@+id/pop_camera" android:layout_width="match_parent" android:layout_height="40dp" android:gravity="center" android:text="@string/camera" android:textColor="@color/black" android:textSize="18sp" /> </LinearLayout> </android.support.v7.widget.CardView> <android.support.v7.widget.CardView android:layout_width="match_parent" android:layout_marginTop="5dp" android:layout_marginBottom="5dp" android:layout_height="wrap_content"> <TextView android:id="@+id/pop_cancel" android:layout_width="match_parent" android:layout_height="40dp" android:gravity="center" android:text="取消" android:textColor="@color/black" android:textSize="18sp" /> </android.support.v7.widget.CardView> </LinearLayout>
而头像,我用的时开源框架,circleiamgeview,这个用来处理圆形头像就可以了,这里就不贴出来了,链接如下:
compile 'de.hdodenhof:circleimageview:2.1.0'
其中点击事件如下:
图片:
/*Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("image*//**//*"); startActivityForResult(intent,ToolUtils.SCAN_OPEN_PHONE);*/ //由于模拟器图库的刷新问题,采用如下打开方式,实际开发请采用上面这种 Intent intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI); startActivityForResult(intent, ToolUtils.SCAN_OPEN_PHONE);
相机:
/** * 打开相册 */ private void cameraPic() { //创建一个file,用来存储拍照后的照片 File outputfile = new File(mActivity.getExternalCacheDir(),"output.png"); try { if (outputfile.exists()){ outputfile.delete();//删除 } outputfile.createNewFile(); } catch (Exception e) { e.printStackTrace(); } Uri imageuri ; if (Build.VERSION.SDK_INT >= 24){ imageuri = FileProvider.getUriForFile(mActivity, "com.rachel.studyapp.fileprovider", //可以是任意字符串 outputfile); }else{ imageuri = Uri.fromFile(outputfile); } //启动相机程序 Intent intent = new Intent("android.media.action.IMAGE_CAPTURE"); intent.putExtra(MediaStore.EXTRA_OUTPUT,imageuri); startActivityForResult(intent,ToolUtils.PHONE_CAMERA); }
在拍照这里,7.0 是需要做处理的,google 在7.0 之后,考虑到安全性的问题,用 fileProvider 来封装 uri了,所以,这里我们也处理一下,mCameraUri 是用来保存拍照后,照片的 uri,可以简单理解成该图片的索引。
注意!!!如果是6.0的手机,请添加权限申请,我这里已经封装好,就不贴出来干扰大家了。
既然上面填写了 fileprovider 了,那么用过content provider的都知道,我们得配置 provider 属性了,如下,在你的 Androidmanifest.xml 下添加:
<provider android:name="android.support.v4.content.FileProvider" android:authorities="com.rachel.studyapp.fileprovider" android:exported="false" android:grantUriPermissions="true"> <!--提供共享路径--> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths"/> </provider>
上面得 authorities 必须跟你刚才在 getUriForFile 得authorities 一致,exported 这里填写 false,不然又得来权限问题了,grantUriPermissions 表示授予临时访问,然后再在下面添加一个 mete-data 标签,用来提供 fileprovider 的共享路径。
file_paths 名字随便取,然后在新建一个 xml 文件夹,席间 file_paths 文件填写如下代码:
<?xml version="1.0" encoding="utf-8"?> <resources> <paths> <external-path path="" name="camera_photos" /> </paths> </resources>
解释如下:
external-path 表示用来指定共享路径的
name 随便取,只是一个标签,买个关子,下面会解释
path 这个比较重要,如果不设置,则表示将整个 SD 卡进行共享,然后制定了,比如 path=”Pictrues”那么,就只共享 sd卡下的 Pictures 文件夹
既然都配置好了,我们把上面的 mCameraUri 打印一下,如下所示:
cameraPic: content://com.rachel.studyapp.fileprovider/camera_photos/Android/data/com.rachel.studyapp/cache/output.png
看到这个 camera_photos 了吧,其实它就是一个 虚拟目录,可以不管的。这个就是 7.0 之后 uri 经过封装后的样子。
拍照之后,就是要处理获取的图片了,在 onActivityReslut 中的处理也非常简单,既然不管是从相册,还是从相册,我们都获取到了正确的 uri,那么,在获取到 这个 uri 之后,启动裁剪的 activity 就可以了,如下:
@Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK){ switch (requestCode){ case ToolUtils.SCAN_OPEN_PHONE: //从相册图片后返回的uri //启动裁剪 startActivityForResult(CutForPhoto(data.getData()),ToolUtils.PHONE_CROP); break; case ToolUtils.PHONE_CAMERA: //相机返回的 uri //启动裁剪 String path = mActivity.getExternalCacheDir().getPath(); String name = "output.png"; startActivityForResult(CutForCamera(path,name),ToolUtils.PHONE_CROP); break; case ToolUtils.PHONE_CROP: try { //获取裁剪后的图片,并显示出来 Bitmap bitmap = BitmapFactory.decodeStream( mActivity.getContentResolver().openInputStream(mCutUri)); mUserLogoIcon.setImageBitmap(bitmap); } catch (FileNotFoundException e) { e.printStackTrace(); } break; } } }
可以看到,裁剪分为两个,一个是从相册中获取,另一个则是拍照之后,再来裁剪的。不一样的地方,往下看:
相册裁剪:
/** * 图片裁剪 * @param uri * @return */ @NonNull private Intent CutForPhoto(Uri uri) { try { //直接裁剪 Intent intent = new Intent("com.android.camera.action.CROP"); //设置裁剪之后的图片路径文件 File cutfile = new File(Environment.getExternalStorageDirectory().getPath(), "cutcamera.png"); //随便命名一个 if (cutfile.exists()){ //如果已经存在,则先删除,这里应该是上传到服务器,然后再删除本地的,没服务器,只能这样了 cutfile.delete(); } cutfile.createNewFile(); //初始化 uri Uri imageUri = uri; //返回来的 uri Uri outputUri = null; //真实的 uri Log.d(TAG, "CutForPhoto: "+cutfile); outputUri = Uri.fromFile(cutfile); mCutUri = outputUri; Log.d(TAG, "mCameraUri: "+mCutUri); // crop为true是设置在开启的intent中设置显示的view可以剪裁 intent.putExtra("crop",true); // aspectX,aspectY 是宽高的比例,这里设置正方形 intent.putExtra("aspectX",1); intent.putExtra("aspectY",1); //设置要裁剪的宽高 intent.putExtra("outputX", ToolUtils.dip2px(mActivity,200)); //200dp intent.putExtra("outputY",ToolUtils.dip2px(mActivity,200)); intent.putExtra("scale",true); //如果图片过大,会导致oom,这里设置为false intent.putExtra("return-data",false); if (imageUri != null) { intent.setDataAndType(imageUri, "image/*"); } if (outputUri != null) { intent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri); } intent.putExtra("noFaceDetection", true); //压缩图片 intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString()); return intent; } catch (IOException e) { e.printStackTrace(); } return null; }
相机裁剪:
/** * 拍照之后,启动裁剪 * @param camerapath 路径 * @param imgname img 的名字 * @return */ @NonNull private Intent CutForCamera(String camerapath,String imgname) { try { //设置裁剪之后的图片路径文件 File cutfile = new File(Environment.getExternalStorageDirectory().getPath(), "cutcamera.png"); //随便命名一个 if (cutfile.exists()){ //如果已经存在,则先删除,这里应该是上传到服务器,然后再删除本地的,没服务器,只能这样了 cutfile.delete(); } cutfile.createNewFile(); //初始化 uri Uri imageUri = null; //返回来的 uri Uri outputUri = null; //真实的 uri Intent intent = new Intent("com.android.camera.action.CROP"); //拍照留下的图片 File camerafile = new File(camerapath,imgname); if (Build.VERSION.SDK_INT >= 24) { intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); imageUri = FileProvider.getUriForFile(mActivity, "com.rachel.studyapp.fileprovider", camerafile); } else { imageUri = Uri.fromFile(camerafile); } outputUri = Uri.fromFile(cutfile); //把这个 uri 提供出去,就可以解析成 bitmap了 mCutUri = outputUri; // crop为true是设置在开启的intent中设置显示的view可以剪裁 intent.putExtra("crop",true); // aspectX,aspectY 是宽高的比例,这里设置正方形 intent.putExtra("aspectX",1); intent.putExtra("aspectY",1); //设置要裁剪的宽高 intent.putExtra("outputX", ToolUtils.dip2px(mActivity,200)); intent.putExtra("outputY",ToolUtils.dip2px(mActivity,200)); intent.putExtra("scale",true); //如果图片过大,会导致oom,这里设置为false intent.putExtra("return-data",false); if (imageUri != null) { intent.setDataAndType(imageUri, "image/*"); } if (outputUri != null) { intent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri); } intent.putExtra("noFaceDetection", true); //压缩图片 intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString()); return intent; } catch (IOException e) { e.printStackTrace(); } return null; }
上面两个,相似度极高,不同的是,相册是直接获取 uri,而相机,则是通过拍照之后,获取的 uri,你这里可以合并一下,只是参数不一样而已,这里我为了让大家有个对比的效果,就分开出来了。
上面一个,注意到用了 intent.putExtra(“return-data”,false);如果这里设置为 true,那么,返回是就是一个 btimap,但这样做在内存不足的情况下,会报 OOM 的,所以,像这种大图片的,我们一般是在本地生成一个裁剪之后的图片,然后获取到这个裁剪之后的 uri,再解析出来,才是正确的做法,所以这里设置为false,并用 mcuturi 来作为这个本地存储的 uri。
这样就讲完了。
这里推荐一下,github,我觉得还不错的两个裁剪开源库,SmartCropper,链接如下:
https://github.com/pqpo/SmartCropper
TakePhoto :
https://github.com/crazycodeboy/TakePhoto/
相关文章推荐
- android选择系统相机拍照和系统相册,裁剪图片并保存和设置头像,适配至7.0
- Android 头像(拍照,相册)选择后裁剪功能
- 超实用的Andoird圆形头像设置 —— 实现相机、相册选择并裁剪尽在一行代码之间(兼容Android6.0/7.0)
- Android - Android 7.0 拍照,相册选择和系统图片裁剪和删除适配
- Android开发之头像上传(包含拍照,从相册选择图片,裁剪等)
- android头像选择(拍照,相册,裁剪)
- android头像选择(拍照,相册,裁剪)
- android头像选择(拍照,相册,裁剪)
- Android 设置用户头像 -》拍照/相册选择+裁剪 实现
- Android圆形头像设置(实现相机、相册选择并裁剪)兼容6.0/7.0
- Android调用系统相册、拍照以及裁剪最简单的实现(兼容7.0)
- Android 中拍照、相册选择、裁剪照片
- Android 通过拍照或相册选择图片并裁剪(精简版)
- android头像相册/拍照选取,裁剪及上传综合案例
- Android 通过拍照或相册选择图片并裁剪(精简版)
- Android 相册选择照片或拍照获取图片系统6.0及7.0实现
- Android 调用系统相机拍照(适配7.0)、从图库选择图片、从数据库读取联系人信息、相册图片、扫描文件
- Android头像上传/拍照/从相册选择Demo(个人信息模块会用到)
- Android开发--通过相册或拍照选择头像
- Android 调用系统拍照及相册裁剪后作为头像,兼容到5.0