简单拍照与摄像

在富媒体开始流行之前,整个世界是一个灰暗且平淡无奇的地方。还记得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拍摄功能的更多相关文章

  1. 利用FFmpeg玩转Android视频录制与压缩(二)<转>

    转载出处:http://blog.csdn.net/mabeijianxi/article/details/72983362 预热 时光荏苒,光阴如梭,离上一次吹牛逼已经过去了两三个月,身边很多人的女 ...

  2. android全功能音乐播放器、基于MVP-Clean + Weex + RxJava2 + Retrofit + Dagger2 + MTRVA的综合应用、图片滤镜处理等源码

    Android仿微信朋友圈查看图片下拽返回. Android图片滤镜处理,相机滤镜处理效果源码 Android自定义View源码:一个水平的进度条 基于MVP-Clean + Weex + RxJav ...

  3. [译]:Xamarin.Android平台功能——位置服务

    返回索引目录 原文链接:Location Services. 译文链接:Xamarin.Android平台功能--位置服务 本部分介绍位置服务以及与如何使用位置提供商服务 Location Servi ...

  4. Android表情功能

    Android表情功能 标签(空格分隔): 未分类 转载自:android edittext插入表情(基于socket方式),并对文中不正确的内容进行整理和修正 [TOC] 涉及知识点: Androi ...

  5. 玩转Android之数据库框架greenDAO3.0使用指南

    用过ActiveAndroid.玩过ORMLite,穿过千山万水,最终还是发现greenDAO好用,ActiveAndroid我之前有一篇文章介绍过 玩转Android之数据库框架ActiveAndr ...

  6. Cocos2d-x使用android拍照功能加载照片内存过大,通过另存照片尺寸大小解决

    使用2dx调用android拍照功能,拍照结束后在2dx界面显示拍照照片,如果不对照片做处理,会出现内存过大的问题,导致程序崩溃,如果仅仅另存拍照照片,则照片质量大小均下降,导致照片不够清晰,后来发现 ...

  7. Android定位功能

    不说废话,直接说说实现android定位有关的API吧. 这些API都在android.location包下,一共有三个接口和八个类.它们配合使用即可实现定位功能. 三个接口: GpsStatus.L ...

  8. Android定位功能(二)

    在前文Android定位功能(一)中,已经大致介绍了一下在Android平台中,和定位功能相关的类,并举例获取了位置信息.但是前文是基于Criteria定制了一个标准,通过getBestProvide ...

  9. Android P 功能和 API

    Android P 功能和 API Android P 为用户和开发者引入众多新特性和新功能. 本文重点介绍面向开发者的新功能. 要了解新 API,请阅读 API 差异报告或访问 Android AP ...

随机推荐

  1. Spring Security构建Rest服务-0702-个性化用户认证流程2

    登录成功后的处理AuthenticationSuccessHandler: 认证成功后,默认情况下spring security会继续访问之前访问的url,如果想自定义处理逻辑,用默认的就不行了.此时 ...

  2. JVM-压缩指针

    什么是压缩指针: 通常64位JVM消耗的内存会比32位的最多会多用1.5倍,这是因为对象指针在64位架构下,对象指针长度会翻倍. 对于那些将要从32位平台移植到64位的应用来说,平白无辜多了1/2的内 ...

  3. 【Express系列】第2篇——主程序的改造

    上一篇对项目的目录结构和 app.js 等一些文件做了一些改造,然而那只是开始. 接下来将做进一步的改造和完善. 我们先看看几个主要的脚本文件,下面的代码是我稍微修改过并添加注释的,方便理解每句代码的 ...

  4. 数据库-SQLite简介

    SQLite是D.Richard Hipp用C语言编写的开源嵌入式数据库(轻型数据库). 由于资源占用少.性能良好和零管理成本,嵌入式数据库有了它的用武之地,像Android.iPhone都有内置的S ...

  5. tomcat安装以及常用配置

    目录 一 什么是tomcat 二 tomcat 的版本: 三 tomcat的下载 3.1 tomcat9版本下载链接 3.2 tomcat8.5版本下载链接 四 tomcat的安装 4.1 java环 ...

  6. Python——可变类型与不可变类型(即为什么函数默认参数要用元组而非列表)

    Python 的内建标准类型有一种分类标准是分为可变类型与不可变类型: 可变类型:列表.字典 不可变类型:数字.字符串.元组 因为变量保存的实际都是对象的引用,所以在给一个不可变类型(比如 int)的 ...

  7. Go 开发

    0.参数传递永远是值传递,地址也是一种值 1.go 开发环境的配置 2.import 包的几种形式: 1)_,默认导入一个包时,会将包中内容导入再执行包中的init()方法,有时并不需要某个包,只是想 ...

  8. C语言中函数返回字符串的四种方法

    在讨论着四种方法之前,首先要对函数有一个简单的认识,无论是在形实结合时,还是在return语句返回时,都有一个拷贝的过程.你传进来的参数是个值,自然函数在工作之前要把这个值拷贝一份供自己使用,你传进来 ...

  9. 深入SpringBoot:自定义Endpoint(转)

    本文转自:https://www.jianshu.com/p/9fab4e81d7bb 最近在研究改写actuator的方式,这些放这里已备忘 Endpoint SpringBoot的Endpoint ...

  10. vim---打造Python IDE

    1.文法高亮 为了能在Vim中支持Python文法需要用到插件python.vim,该插件默认位于(/usr/share/vim/vim72/)<Vim安装目录>/<$VIMRUNT ...