玩转Android拍摄功能
简单拍照与摄像
在富媒体开始流行之前,整个世界是一个灰暗且平淡无奇的地方。还记得Gopher吗?我或许不记得了。自从APP成为用户生活的一部分之后,这便给他们提供了一种方式可以来存放他们生活的细节。使用设备上的相机,程序可以使用户扩大周围的视野或者见解,使以独特的化身,记录各个角落里的奇闻异事,或者只是简单的分享他们的境遇。
假设你正在实现一个众包的天气服务程序,这个服务可以使运行在设备上的客户端APP通过天空的混合照片生成全部的天气示意图(言下之意就是可以生成各种天气的天气图像),而整合照片只是程序很小的一部分功能。你并不想在这里花费很多时间,也不想彻底的改造相机程序。幸运的是,大多数的Android设备至少已经安装了一款相机应用。下面就是学习如何利用这个应用为程序拍一张照片。
请求相机权限
为了告知系统程序是基于相机的,需要在清单文件中添加标签:
如果程序需要使用,但是为了整个功能而不强制要求相机,那么可以设置 Android:required为false。这样做的话,Google Play会允许不带相机的设备下载你的程序。不过你有责任需要在运行时通过调用 hasSystemFeature(PackageManager.FEATURE_CAMERA) 方法检查设备上的相机是否可用。如果相机是不可用的,你应该禁用掉与相机相关的功能。
通过相机APP拍照
Android通过授权的方式让其他程序通过调用一个Intent来描述你想要做的事情。这个过程包含了三块:Intent本身,一个启动外部Activity的调用,以及一些当焦点返回Activity时处理图像数据的代码。
下面代码的功能用于调用一个意图来拍摄照片:
要注意,startActivityForResult() 方法被一个调用 resolveActivity() 方法的条件所保护,这个方法返回了可以处理这个Intent的第一个Activity组件。执行这项检查是非常重要的,因为如果你调用 startActivityForResult() 方法所使用的Intent没有APP可以处理的话,那么你的APP将会崩溃。所以只要结果不是null,那么就意味着可以安全使用这个Intent。
获取缩略图像
如果简单的拍照功能并不是APP的主要功能,那么你可能想通过相机应用获得一张照片,并且利用这张照片做点什么事情。
Android的相机应用会将照片作为一个小的 Bitmap 对象打包进Intent中,然后通过onActivityResult() 方法将该Intent返回。具体的Bitmap对象会附加在键”data”后。下面的代码段接收到这个图像,并将它展示到了一个 ImageView 中:
Note: 从”data”中获得的缩略图像可能适合用于图标上面,但不适用于很大的图标。处理全尺寸的图像需要花费更多一点的工作。
保存全尺寸照片
如果你提供了文件的保存路径的话,那么Android相机应用会将全尺寸的照片保存在这个地方。你必须提供文件的全路径,这个路径所指向的地方就是相机应用将要保存照片的地方。
通常情况下,用户通过相机应用所拍摄的任何照片都应该被保存在设备外部存储器上的一个公共文件夹中,这样就可以使所有的APP都可以访问。这个适用于存放共享照片的目录由getExternalStoragePublicDirectory() 提供,并需要传递 DIRECTORY_PICTURES 参数。因为由这个方法所提供的分享目录适用于所有的APP,读取以及写入分别需要READ_EXTERNAL_STORAGE 和WRITE_EXTERNAL_STORAGE 权限。写入权限隐式地包含了读取权限,所有如果你需要写入到外部存储上的话,只需要请求一个权限就可以:
然而,如果你希望这些照片只是被保存在APP的私有目录中的话,你可以使用getExternalFilesDir() 方法提供的目录来做替换方案。在Android 4.3之前,写入到这个目录需要 WRITE_EXTERNAL_STORAGE 权限。从Android 4.4开始,这个权限不再被需要,因为这个目录不再对其它APP可见,所以这里声明的权限只对低版本的Android适用,并需要添加maxSdkVersion 属性:
Note:当用户卸载了你的APP时,那么通过 getExternalFilesDir() 方法所提供的目录下的文件也会一并删除。
一旦你决定了文件存储的目录,需要创建一个防止冲突的文件名称。你可能还希望将路径保存到一个成员变量中,以便稍后再使用。这里在方法中有一个示例的解决办法,它会通过日期时间戳来对新照片返回一个唯一的文件名称:
随着这个方法可用来对照片创建文件,你现在可以创建并且调用Intent,就像这样:
添加照片到相册
当你通过一个意图创建了一张照片,你应该知道图像位于何处,因为在上面的代码中你告诉了照片保存的位置。对于其他人而言,可能使照片最简单的访问方式就是使照片对系统的媒体扫描器(Media Provider)是可访问的。
Note: 如果照片保存的文件目录是由 getExternalFilesDir() 所提供的,那么,媒体扫描器是不能访问这些文件的,因为照片对于你的APP来说是私有的。
下面的示例方法演示了如何调用系统的扫描器来添加你的照片到媒体扫描器(Media Provider)的数据库中,使得这些照片可以被系统的相册应用或者其它APP可访问:
解码缩放图像
在一定的内存中管理多个全尺寸的图像可能是很棘手的。如果你发现你的程序在展示了几张图片之后造成了内存溢出,那么可以通过将JPEG图像缩放到目标视图的尺寸大小的方式来显著的降低堆内存的使用量。
下面的示例方法演示了这一技术:
通过相机APP摄像
权限申请可参考请求相机权限,相关描述可以参考通过相机APP拍照,下面代码的功能用于调用一个意图来捕获视频:
查看视频
Android的相机应用会通过 onActivityResult() 方法将视频返回,视频位于 onActivityResult()方法的回调参数Intent中的Uri所指向的位置。下面的代码展示了接收这个视频并且在VideoView中播放它:
调用API
直接控制设备的相机拍照或者摄像的代码远比通过其他相机应用来完成要多得多。然而,如果你想构建一个专业的相机应用或者在APP的UI中完全集成相机的话,下面展示了如何去做。
开启相机对象
直接控制相机的第一步就是获得 Camera 对象的实例。和Android自身的相机应用相同,推荐访问相机的方式就是在独立的线程打开 Camera,这种方式是应对阻塞UI线程的一个好的解决方法。在更加基础化的实现当中,开启相机这一步操作可以推迟到onResume()方法中执行,这样可以促使代码重用并且保持简单的控制流。
如果相机已经正在被其它应用所使用,那么调用 Camera.open() 方法会抛出一个异常,所以我们需要使用try控制块包裹住它:
从API 9开始,相机框架支持多个相机。如果你使用的是过去的API,然后调用了没有参数的open()方法,那么你会获得后置面板的相机。
创建相机预览
拍照通常需要可以使用户能看到目标的预览图。你可以使用 SurfaceView 来绘制相机传感器捕获到的图像。
为了可以显示预览,你需要预览类。预览需要一个 android.view.SurfaceHolder.Callback 接口的实现,它被用来从相机硬件给应用传递图像数据:
在开始预览之前,必须将预览对象传递给Camera对象,就像下面部分展示的那样。
相机实例的创建于相关预览对象创建必须是以指定顺序进行的,从相机对象开始。在下面的代码中,实例化相机对象的过程被封装起来了,所以 Camera.startPreview() 是可以通过setCamera() 调用的,每当用户做了什么事情使相机发生了改变。预览也必须在预览类的surfaceChanged() 回调方法重新启动:
修改相机设置
相机设置可以改变相机拍照的方式,从缩放等级到曝光补偿等等。下面的示例只是更改了预览的大小;请查看相机应用的源代码获取更多可能:
设置预览方向
大多数的相机应用将展示锁定在了水平方向,因为这是相机传感器的自然方向。这个设置并不能阻止你在垂直方向上拍摄,因为相机的方向会被记录到EXIF的头部。setCameraDisplayOrientation() 方法允许你改变如何展示预览,而不受图像记录方向的影响。然而,在API14之前,在改变方向之前必须停止预览,然后在重新启动它。
拍照
一旦预览启动后,可以使用 Camera.takePicture() 方法来拍一张照片。你可以创建Camera.PictureCallback 对象和 Camera.ShutterCallback 对象然后将它们传递给Camera.takePicture() 方法。
重启预览
在拍了一张照片之后,你必须在用户拍另一张照片之前重新启动预览。在这个例子中,通过重写快门按钮来完成重启:
停止预览并释放相机
一旦你的程序不再需要使用相机,这时就需要执行清理工作。尤其是你需要释放相机对象,否则会使其它程序面临崩溃的风险,包括你自己程序中新的实例。
何时应该停止预览并释放相机呢?好吧,当预览界面被销毁的时候便是停止预览并释放相机的最佳时机,就像下面Preview类中显示的那样:
在上面创建相机预览中,这段程序也是 setCamera() 方法的一部分,所以实例化一个相机总是从停止这段预览开始的。
玩转Android拍摄功能的更多相关文章
- 利用FFmpeg玩转Android视频录制与压缩(二)<转>
转载出处:http://blog.csdn.net/mabeijianxi/article/details/72983362 预热 时光荏苒,光阴如梭,离上一次吹牛逼已经过去了两三个月,身边很多人的女 ...
- android全功能音乐播放器、基于MVP-Clean + Weex + RxJava2 + Retrofit + Dagger2 + MTRVA的综合应用、图片滤镜处理等源码
Android仿微信朋友圈查看图片下拽返回. Android图片滤镜处理,相机滤镜处理效果源码 Android自定义View源码:一个水平的进度条 基于MVP-Clean + Weex + RxJav ...
- [译]:Xamarin.Android平台功能——位置服务
返回索引目录 原文链接:Location Services. 译文链接:Xamarin.Android平台功能--位置服务 本部分介绍位置服务以及与如何使用位置提供商服务 Location Servi ...
- Android表情功能
Android表情功能 标签(空格分隔): 未分类 转载自:android edittext插入表情(基于socket方式),并对文中不正确的内容进行整理和修正 [TOC] 涉及知识点: Androi ...
- 玩转Android之数据库框架greenDAO3.0使用指南
用过ActiveAndroid.玩过ORMLite,穿过千山万水,最终还是发现greenDAO好用,ActiveAndroid我之前有一篇文章介绍过 玩转Android之数据库框架ActiveAndr ...
- Cocos2d-x使用android拍照功能加载照片内存过大,通过另存照片尺寸大小解决
使用2dx调用android拍照功能,拍照结束后在2dx界面显示拍照照片,如果不对照片做处理,会出现内存过大的问题,导致程序崩溃,如果仅仅另存拍照照片,则照片质量大小均下降,导致照片不够清晰,后来发现 ...
- Android定位功能
不说废话,直接说说实现android定位有关的API吧. 这些API都在android.location包下,一共有三个接口和八个类.它们配合使用即可实现定位功能. 三个接口: GpsStatus.L ...
- Android定位功能(二)
在前文Android定位功能(一)中,已经大致介绍了一下在Android平台中,和定位功能相关的类,并举例获取了位置信息.但是前文是基于Criteria定制了一个标准,通过getBestProvide ...
- Android P 功能和 API
Android P 功能和 API Android P 为用户和开发者引入众多新特性和新功能. 本文重点介绍面向开发者的新功能. 要了解新 API,请阅读 API 差异报告或访问 Android AP ...
随机推荐
- POJ 1036
#include<iostream> #include<algorithm> #define MAXN 205 using namespace std; struct node ...
- java对象流与序列化
Object流,直接把obj写入或读出. 前言: 比如 画图的程序,咣当画一个三角形出来,咣当画一正方形出来.然后存盘,当你下次再打开软件的时候三角形.方块还在原来的位置上.如果用面向对象的思维,三角 ...
- webpack使用来打包前端代码
使用webpack打包js文件(隔行变色案例) 1.webpack安装的两种方式 运行npm i webpack -g全局安装webpack,这样就能在全局使用webpack的命令 在项目根目录中运行 ...
- Android Studio打开项目提示找不到sdk路径的问题。
问题如图: 这是由于所打开的项目不是本机创建的,所使用的sdk路径不一致所导致. 解决方案: 打开项目所在目录,找到local.properties文件并打开,发现sdk.dir=D\:\\Andro ...
- Android 开发工具类 09_SPUtils
SharedPreferences 辅助类: 1.保存在手机里面的文件名: 2.保存数据的方法,我们需要拿到保存数据的具体类型,然后根据类型调用不同的保存方法: 3.得到保存数据的方法,我们根据默认值 ...
- CentOS命令行界面与图形界面切换(图文详解)
不多说,直接上干货! Ctrl + Alt +F1,到图形界面 Ctrl + Alt +F2,到命令行界面 欢迎大家,加入我的微信公众号:大数据躺过的坑 人工智能躺过的坑 同 ...
- js二维数组
1.判断是否为二维数组 function isMultiArr(arr){ return arr.every(function(element){ return element instanceof ...
- Spring MVC 实现web Socket向前端实时推送数据
最近项目中用到了webSocket服务,由后台实时向所有的前端推送消息,前端暂时是不可以发消息给后端的,数据的来源是由具体的设备数据收集器收集起来,然后通过socket推送给后端,后端收到数据后,再将 ...
- springweb flux 服务器推送事件
以前做服务器推送一般用轮询,后端主动给客户端推送不是很好解决.有时候也可以采用websocket 现在看了springwebflux,用它自带的方法做服务器推送方便多了. 代码如下: import o ...
- 理解Spring定时任务的fixedRate和fixedDelay
用过 Spring 的 @EnableScheduling 的都知道,我们用三种形式来部署计划任务,即 @Scheduled 注解的 fixedRate(fixedRateString), fixe ...