概述

比奇小说网 m.biqi.org

最近在看 nanChen 写的图片选择器 ImagePicker,感觉写得很不错,也打算把从中学到的东西写下来。很多时候,遇到一个好的框架能够降低开发成本这是好事。但是也要去了解其内部具体实现逻辑,说不定哪天你需要完成一个类似的小功能,你知道原理就能快速写出来,而不是引入整个框架。

本文讲其中的第一个功能:如何调起手机的相机拍照?

系统现有相机应用

对于如何调用系统现有应用,这里简单再说一下。在开发的应用中调用系统现有应用,需要使用 Intent 指定开启的应用的 Action 和 Category,然后通过 startActivity(Intent) 或者 startActivityForResult(Intent, int) 开启指定的 Activity,如果使用 startActivityForResult() 方法开启并需要返回值,再重写 onActivityResult(int, int, Intent) 即可。

先来看看系统现有相机应用的 AndroidManifest.xml 清单文件定义的 Activity:

        <activityandroid:name="com.android.camera.Camera"android:clearTaskOnLaunch="true"android:configChanges="orientation|keyboardHidden"android:screenOrientation="landscape"android:taskAffinity="android.task.camera"android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen" ><intent-filter><action android:name="android.intent.action.MAIN" /><categroy android:name="android.intent.category.DEFAULT" /><categroy android:name="android.intent.category.LAUNCHER" /></intent-filter><intent-filter><action android:name="android.media.action.IMAGE_CAPTURE" /><categroy android:name="android.intent.category.DEFAULT" /></intent-filter><intent-filter><action android:name="android.media.action.STILL_IMAGE_CAMERA" /><categroy android:name="android.intent.category.DEFAULT" /></intent-filter></activity><activityandroid:name="com.android.camera.VideoCamera"android:clearTaskOnLaunch="true"android:configChanges="origientation|keyboardHidden"android:label="@string/video_camera_label"android:screenOrientation="landscape"android:taskAffinity="android.task.camcorder"android:theme="@android:style/theme.Black.NoTitleBar.Fullscreen" ><intent-filter><action android:name="android.media.action.VIDEO_CAMERA" /><categroy android:name="android.intent.category.DEFAULT" /></intent-filter><intent-filter><action android:name="android.media.action.VIDEO_CAPTURE" /><categroy android:name="android.intent.category.DEFAULT" /></intent-filter></activity>    

它定义了两个 Activity,com.android.camera.Camera 表示照相机,com.android.camera.VideoCamera 表示摄像机。从字面意思可以看出,为了捕获系统相机返回的数据,一般需要使用一下两个 Action 即可开启照相机与摄像机:

  • android.media.action.IMAGE_CAPTURE:Intent 的 Action 类型,从现有的相机应用中请求一张图片。

  • android.media.action.VIDEO_CAPTURE:Intent 的 Action 类型,从现有的相机应用中请求一段视频。

上面两个参数,均在 MediaStore 类中以静态常量的形式定义好了,分别是:MediaStore.ACTION_IMAGE_CAPTURE (相机) 和 MediaStore.ACTION_VIDEO_CAPTURE (摄像机)。

获取系统现有相机拍摄的图片

在新开启的 Activity 中,如果需要获取它的返回值,则需要使用 startActivityForResult(Intent,int) 方法打开 Activity,并重写 onActivityResult(int, int, Intent) 获取系统相机的返回数据,那么我们只需要在 onActivityResult() 中获取到返回值即可。

系统相机拍摄的照片,如果不指定路径,会保存在系统默认文件夹下,可以使用 Intent.getExtra() 方法得到,得到的是一个 Uri 地址,表示了一个内容提供者的地址。如果通过MediaStore.EXTRA_OUTPUT 指定了保存路径,那么通过 Intent.getExtra() 得到的将是一个空地址,但是既然是我们指定的地址,那么也不愁找不到它了。

但是如果是7.0以上,需要使用 FileProvider 来得到这个 uri 地址。

实现方案

说清楚流程之后,下面就是具体代码实现:

首先是在 AndroidManiFest.xml 下声明拍照权限:

<uses-permission android:name="android.permission.CAMERA" />

声明权限后,在开始拍照前,还是需要判断用户是否给了我们拍照的权限:

if (( mActivity).checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {ActivityCompat.requestPermissions(mActivity, new String[]{Manifest.permission.CAMERA}, GalleryActivity.REQUEST_PERMISSION_CAMERA);
} else {imagePicker.takePicture(mActivity, GalleryActivity.REQUEST_CODE_TAKE);
}

如果用户没有给权限,那么需要申请权限,权限申请以后,会有一个回调通知开发者是否允许了,具体见下发的代码:

    @Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);if (requestCode == REQUEST_PERMISSION_CAMERA) {if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {imagePicker.takePicture(this, REQUEST_CODE_TAKE);} else {showToast("权限被禁止,无法打开相机");}}}

权限允许之后,通过 imagePicker.takePicture 去拍照。下面看下拍照的具体代码逻辑:

    /*** 拍照的方法*/public void takePicture(Activity activity, int requestCode) {Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);takePictureIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);if (takePictureIntent.resolveActivity(activity.getPackageManager()) != null) {if (Utils.existSDCard()) takeImageFile = new File(Environment.getExternalStorageDirectory(), "/DCIM/camera/");else takeImageFile = Environment.getDataDirectory();takeImageFile = createFile(takeImageFile, "IMG_", ".jpg");if (takeImageFile != null) {// 默认情况下,即不需要指定intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);// 照相机有自己默认的存储路径,拍摄的照片将返回一个缩略图。如果想访问原始图片,// 可以通过dat extra能够得到原始图片位置。即,如果指定了目标uri,data就没有数据,// 如果没有指定uri,则data就返回有数据!
Uri uri;if (VERSION.SDK_INT <= VERSION_CODES.M) {uri = Uri.fromFile(takeImageFile);} else {/*** 7.0 调用系统相机拍照不再允许使用Uri方式,应该替换为FileProvider* 并且这样可以解决MIUI系统上拍照返回size为0的情况*/uri = FileProvider.getUriForFile(activity, "com.example.myapplication.provider", takeImageFile);//加入uri权限 要不三星手机不能拍照List<ResolveInfo> resInfoList = activity.getPackageManager().queryIntentActivities(takePictureIntent, PackageManager.MATCH_DEFAULT_ONLY);for (ResolveInfo resolveInfo : resInfoList) {String packageName = resolveInfo.activityInfo.packageName;activity.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);}}//  Log.e("nanchen", ProviderUtil.getFileProviderName(activity));
                takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri);}}activity.startActivityForResult(takePictureIntent, requestCode);}

关于 Intent.resolveActivity 作用,简单来说就是当你在调用第三方软件或者系统 Activity,类似打开相机,发送图片等隐式 Intent,是并不一定能够在所有的 Android 设备上都正常运行。通过该方法判断这个 intent 对应的 activity 是否存在,确保不会出现崩溃。

takeImageFile 是图片存储地址,在7.0以前,需要用 Uri.fromFile 进行处理。7.0之后的采用 FileProvider。下面介绍下 FileProvider 的使用方法:

注册

使用 FileProvider 需要在 AndroidManiFest.xml 里声明:

        <providerandroid:name="android.support.v4.content.FileProvider"android:authorities="com.example.myapplication.provider"android:exported="false"android:grantUriPermissions="true"><meta-dataandroid:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/file_path" /></provider> 

这里是直接使用的 v4 包中的 FileProvider,我们也可以直接继承 FileProvider 类,适当重写重载函数,但不建议如此做。下面来介绍上面的几个设置:

  • name: provider 的类名,若使用默认的 v4 的 FileProvider 可使用 "android.support.v4.content.FileProvider",也可以设置为自定义的继承 FileProvider 的 provider 类;

  • authorities: 一个签名认证,可以自定义,但在获取 uri 的时候需要保持一致;

  • grantUriPermissions: 使用 FileProvider 的使用需要我们给流出的URI 赋予临时访问权限(READ 和 WRITE),该设置是允许我们行使该项权力;

  • meta-data: meta-data 配置的是我们可以访问的文件的路径配置信息,需要使用 xml 文件进行配置,FileProvider 会通过解析 xml 文件获取配置项,其中 name 名字不可改变为: android.support.FILE_PROVIDER_PATHS,resource 为配置路径信息的配置项目。

路径配置

可访问的路径配置可以在 res 中建立一个 xml 文件下面建立一个配置文件,格式如下:

<?xml version="1.0" encoding="utf-8"?>
<paths><!--path:需要临时授权访问的路径(.代表所有路径)--><!--name:就是你给这个访问路径起个名字--><external-path name="cam" path="." />
</paths>

下面解释下:

  • <files-path/>   代表的根目录: Context.getFilesDir()

  • <external-path/>   代表的根目录: Environment.getExternalStorageDirectory()
  • <cache-path/>  代表的根目录: getCacheDir()

最后,将 uri 放到 MediaStore.EXTRA_OUTPUT 中。

takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri);

拍照完成后,会回调 onActivityResult,在这里我们可以根据先前传的值将图片展示到 ImageView 中:

    @Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {Log.i(TAG, "系统相机拍照完成,resultCode=" + resultCode + " " + requestCode);if (requestCode == REQUEST_CODE_TAKE) {Uri uri = Uri.fromFile(takeImageFile);iv_CameraImg.setImageURI(uri);} }

到此,调用系统相机拍照的过程到此就结束了。

Android 调起系统相机拍照相关推荐

  1. android代码调用相机,Android如何调用系统相机拍照

    本文实例为大家分享了Android调用系统相机拍照的具体代码,供大家参考,具体内容如下 /** * 调用系统相机 */ private void takePhoto() { Uri uri = nul ...

  2. Android手机调用系统相机拍照、裁剪以及获取Url上传图片

    前言 最近一个人在公司搞独立开发,遇到问题只能自己解决,虽然过程比较坎坷,但是收获还是颇多的,一个人也是要坚强滴,最近弄用户的头像遇到了一些小问题,虽然上一款应用有头像上传,但是发现了其中的一些小问题 ...

  3. Android多层嵌套Fragment中调用系统相机拍照以及裁剪的相关问题解决

    多层嵌套Fragment中调用系统相机拍照.裁剪 最近公司项目有频繁使用相机拍照的需求,然后频繁的实际使用过程中遇到很多大大小小的坑,在此记录下. (ps.关于一些拍照后图片旋转.裁剪设置返回数据为t ...

  4. Android调用系统相机拍照并保存到指定位置

    Android调用系统相机拍照并保存到指定位置 @Click(R.id.btn_takePhoto)void onclick() {Intent intent = new Intent(MediaSt ...

  5. Android开发 调用系统相机相册图片功能,解决小米手机拍照或者图片横竖相反问题,及小米手机相册图片路径问题

    Android开发 调用系统相机相册图片功能,解决小米手机拍照或者图片横竖相反问题,及小米手机相册图片路径问题 1.调用相机,兼容7.0 AndroidManifest配置 <providera ...

  6. android 相机拍照返回,Android6.0机型上调用系统相机拍照返回的resultCode值始终等于0的问题...

    版权声明:本文为博主原创文章,未经博主允许不得转载. 正常情况下调用系统相机拍照: 如果拍照后点击的是"确定"图标,返回的resultCode = -1(Activity.RESU ...

  7. android固定位置拍照,Android调用系统相机拍照并保存到指定位置

    Android调用系统相机拍照并保存到指定位置 @Click(R.id.btn_takePhoto) void onclick() { Intent intent = new Intent(Media ...

  8. Android Webview 调起系统相机、相册

    公司有个业务需求,需要在H5页中可以分别调起相同中的相机.相册.拿到该H5页看了一下,H5中调起相机和打开相册的源码分别是: #调起相机 <input type="file" ...

  9. android 调用系统相机拍照 获取原图

    博客源址:android 调用系统相机拍照 获取原图 博客时间:2013-04-23 11:08 好吧,为了这个问题又折腾了一整天.之前在网上找来的方法,如果在onActivityResult中直接用 ...

最新文章

  1. 微信小程序setData()方法的使用
  2. 使用 Servlet 读取表单数据
  3. VTK:图像平面小部件用法实战
  4. 验证视图状态 MAC 失败
  5. iBiology |除了B站,这还有个专业的生科科普网站
  6. AE鱼眼镜头畸变扭曲修复插件RevisionFX RELens for Mac
  7. 太棒了!输入文本直接生成流程图
  8. 【算术、关系、逻辑、位、复合赋值、带副作用的、自增、自减、其它】运算符(学习笔记4--C语言运算符)
  9. 用XSLT和XML改进Struts
  10. EFK 配置geo-ip落地实践
  11. 7 大版块 | 全面解读与认知支付系统
  12. verilog 之数字电路 边沿检测电路
  13. 国内三大通信运营商频段制式
  14. 计算机启动到安全模式,电脑正常开机会进入安全模式怎么办
  15. html超链接自动下划线,html超链接下划线应该加入吗?
  16. 第三届“网鼎杯”官方资格赛圆满结束,问鼎之战即将开启!
  17. 【深度思考,极客大学Java进阶训练营
  18. 优秀的程序员——用批判性思维批判下
  19. python中global用法实例
  20. Git提交信息规范化

热门文章

  1. Adnroid 自定义流式布局
  2. 首屏加载从11s到1s,详解前端性能优化
  3. 爱我所爱,行我所行,听从我心,无问西东
  4. “电梯”英文域名Lifts.com超63.5万结拍
  5. 复旦MBA:热情从未消退,让这场云端盛会点燃你的斗志
  6. 骑行,因人和美景而精彩
  7. 卡牌游戏战斗系统的设计和实现二
  8. htc one x android5.0,终于来了 HTC One M8升级Android 5.0体验
  9. 微型计算机的计算器,微机简单计算器程序设计
  10. HarmonyOS——一个面向物联网的操作系统