完全解析Android拍照、照片选择以及图片裁剪
Android中头像选择,图片上传等功能几乎是每一个APP必备的功能,那么关于怎样使用相机,如何进行照片选择,以及选择后的图片裁剪,这一系列的问题都需要逐一处理。这也是本篇文章的主要内容。
一、应用场景


微信朋友圈上传图片,头像上传等功能,经常就会用到以上功能。
二、业务逻辑
主要分为两种业务逻辑:拍照,选择图片。
拍照逻辑:
- A 界面,点击按钮调用相机拍照;
- 拍照界面拍照后,点击确认得到拍完照片,跳转到 B 界面进行预览;
- B 界面进行图片裁剪,裁剪后确认,返回A界面进行图片回显;
选择图片逻辑:
- A界面,点击按钮调用相册选择图片;
- 相册界面选择图片后,跳转到B界面进行预览;
- B 界面进行图片裁剪,裁剪后确认,返回A界面进行图片回显;

从上面可以清楚地看出,两种方式的主要区别在第一步上面,一种是选择调用相机,另一种选择是调用相册。
下面我们来详情具体代码逻辑。
三、拍照具体实现
以如下使用场景为例:
<img src=”http://upload-images.jianshu.io/upload_images/3985563-b82a260a49b89082.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240″ width = “250” height = “450” align=center />
头像上传的使用。
1.调用相机
Android 程序上实现拍照功能的方式分为两种:第一种是利用相机的 API 来自己设置相机,第二种是利用 Intent 调用系统指定的相机拍照。下面讲的内容都是针对第二种实现方式的应用。
简单使用
Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);Uri fileUri = Uri.fromFile(mPhotoFile);captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);startActivityForResult(captureIntent, CAPTURE_PHOTO_REQUEST_CODE);很简单,通过上述四行代码就实现了调用系统相机。
加入MediaStore.EXTRA_OUTPUT,使得拍照后的图片输出到对应路径下。
然而因为Android手机的碎片化,我们之前调用系统指定的相机app来拍照,有些手机可能会没有这个app,所以在使用之前要检查能否有系统相机。
/** * 判断系统中能否存在可以启动的相机应用 * * @return 存在返回true,不存在返回false */ public boolean hasCamera() { PackageManager packageManager = mActivity.getPackageManager(); Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); List<ResolveInfo> list = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); return list.size() > 0; }2.照片预览及图片裁剪
因为之前使用startActivityForResult方式调用系统相机,那么在拍照完成后,会返回到上述页面,这时候需要重写onActivityResult方法,根据之前传入的mPhotoFile路径,就获取了图片所在地,由于拍照后的图片就存在该路径下。
而后我们只要要在开启一个照片预览Activity,进行后续裁剪即可以了。
由于这里我们调用系统裁剪,所以就不设置预览Activity,直接跳转到裁剪页面即可以了。
//拍照完成后 获取目标文件 跳转到裁剪页面 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == CapturePhotoHelper.CAPTURE_PHOTO_REQUEST_CODE) { //获取拍照后图片路径 File photoFile = mCapturePhotoHelper.getPhoto(); if (photoFile != null) { if (resultCode == RESULT_OK) { Uri uri = Uri.fromFile(photoFile); Intent intent = new Intent("com.android.camera.action.CROP"); intent.setDataAndType(uri, "image/*"); //intent.putExtra("crop", "true"); intent.putExtra("aspectX", 1); intent.putExtra("aspectY", 1); intent.putExtra("outputX", 300); intent.putExtra("outputY", 300); intent.putExtra("scale", true); intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); intent.putExtra("return-data", false); intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString()); intent.putExtra("noFaceDetection", true); // no face detection intent = Intent.createChooser(intent, "裁剪图片"); startActivityForResult(intent, REQUEST_PICKER_AND_CROP); } else { if (photoFile.exists()) { photoFile.delete(); } } } } else { super.onActivityResult(requestCode, resultCode, data); } }其中要注意几个 extra 字段:

注意:return-data: 设为 true 的时候,在 onActivityResult() 中可以直接通过 data.getParcelableExtra(“data”) 得到裁剪后的 Bitmap 对象。但是当 Bitmap 过大时,就不能使用这种方法了,容易出现OOM现象,需要通过获取文件,而后先缩放,再加载。
如下图所示:

3.回显图片
如上如所示,修剪图片完成后,点击确定,而后即可以编写回显逻辑。
同样在onActivityReuslt方法中
if(requestCode ==REQUEST_PICKER_AND_CROP){ File photoFile = mCapturePhotoHelper.getPhoto(); //存放到相册 BitmapUtils.displayToGallery(this, photoFile); //升级UI 显示图像 InformationBean informationBean = mList.get(0); informationBean.setContent(photoFile.getAbsoluteFile().toString()); informationBean.setSet(true); mAdapter.notifyItemChanged(0); }上述采用了RecyclerView,具体升级头像逻辑在Adapter中。
不过同样因为Android碎片化问题,图片会产生各种各样的问题,比方:拍出来的照片“歪了”,拍完照怎样闪退了,图片无法显示。
以上这些问题都比较坑,不过不要紧,已经有前人为我们趟出一条血路,一律都解决好了,详细代码及具体实现参考:Android拍照适配方案你需要知道哪些?。
这里我们只负责应用就好了:
String content = bean.getContent(); final File file = new File(content); ((SpecialViewHolder) holder).mImageView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { ((SpecialViewHolder) holder).mImageView.getViewTreeObserver().removeOnGlobalLayoutListener(this); mWidth = ((SpecialViewHolder) holder).mImageView.getMeasuredWidth(); mHeight = ((SpecialViewHolder) holder).mImageView.getMeasuredHeight(); Bitmap bitmap = BitmapUtils.decodeBitmapFromFile(file, mWidth, mHeight); if (bitmap != null) { //检查能否有被旋转,并进行纠正 System.out.println("文件所占空间:"+"file.getTotalSpace()"); int degree = BitmapUtils.getBitmapDegree(file.getAbsolutePath()); if (degree != 0) { bitmap = BitmapUtils.rotateBitmapByDegree(bitmap, degree); } ((SpecialViewHolder) holder).mImageView.setImageBitmap(bitmap); } } });先获取控件的宽高,进行压缩,避免图片无法显示的问题。
而后检查有没有被旋转,假如旋转,那么通过矩阵矫正,避免照片”歪了”的问题。
至于拍完照片后闪退,可以通过重写onSaveInstanceState 和 onRestoreInstanceState 来实现。
回显结果:

四、选择图片具体实现
因为和上述方式只是第一步有区别,我们就具体看看第一步的实现。
Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); intent.putExtra(MediaStore.EXTRA_OUTPUT,uri); startActivityForResult(intent, REQUEST_PICK_IMAGE);直接开启系统图片选择应用就可,不用额外设置MediaStore.EXTRA_OUTPUT,由于图片已经保存在数据库内了,可直接获取,如下所示。
而后在onActivityResult中,获取图片存储路径,跳转到裁剪页面。
if (requestCode == REQUEST_PICK_IMAGE) { //获取选择图片后图片路径 if (resultCode == RESULT_OK) { Uri uri = data.getData(); Intent intent = new Intent("com.android.camera.action.CROP"); intent.setDataAndType(uri, "image/*"); intent.putExtra("aspectX", 1); intent.putExtra("aspectY", 1); intent.putExtra("outputX", 200); intent.putExtra("outputY", 200); intent.putExtra("scale", true); intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(tempFile)); intent.putExtra("return-data", false); intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString()); intent.putExtra("noFaceDetection", true); // no face detection intent = Intent.createChooser(intent, "裁剪图片"); startActivityForResult(intent, REQUEST_PICKER_AND_CROP_2); } }后面回显步骤和上述一致。
五、总结
上述详情了拍照,照片选择以及图片剪裁的使用,因为使用的是系统自带的应用,所以可能出现少量意想不到的适配问题,还有待处理。另外,因为是系统自带,功能比较单一,且无法使用个性化,如有这方面的需求,可以使用少量流行的第三方库,Github上有很多优秀的实现。
自己是从事了七年开发的Android工程师,不少人私下问我,2019年Android进阶该怎样学,方法有没有?
没错,年初我花了一个多月的时间整理出来的学习资料,希望能帮助那些想进阶提升Android开发,却又不知道怎样进阶学习的朋友。【包括高级UI、性能优化、架构师课程、NDK、Kotlin、混合式开发(ReactNative+Weex)、Flutter等架构技术资料】,希望能帮助到您面试前的复习且找到一个好的工作,也节省大家在网上搜索资料的时间来学习。
资料获取方式:加入Android架构交流QQ群聊:513088520 ,进群即领取资料!!!
点击链接加入群聊【Android移动架构总群】:加入群聊
资料大全
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 完全解析Android拍照、照片选择以及图片裁剪