简单拍照与摄像

在富媒体开始流行之前,整个世界是一个灰暗且平淡无奇的地方。还记得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. Java虚拟机执行引擎

    执行引擎 关于执行引擎相关的部分, 在之前的博文里 Java内存区域中已经有所提及. 回顾一下: 也只有几个概念, JVM方法调用和执行的基础数据结构是 栈帧, 是内存区域中 虚拟机栈中的栈元素, 每 ...

  2. L09-Linux系统修改网卡名称(eth1修改为eth0)

    一.环境 VirtualBox + CentOS6.5 二.问题 有时候在克隆服务器之后配置网络时,或者在维护别人建好的服务器时,会遇到这样一种情况.如下图所示:   即:在接口配置文件ifcfg-e ...

  3. jq02--基础函数

    jq是一个js函数库,主要是为事件处理特别设计的,现在我们继续学习一些jq函数. 1.jq效果: 显示与隐藏: $().hide(speed,callback) speed--"slow&q ...

  4. 模拟登陆+数据爬取 (python+selenuim)

    以下代码是用来爬取LinkedIn网站一些学者的经历的,仅供参考,注意:不要一次性大量爬取会被封号,不要问我为什么知道 #-*- coding:utf-8 -*- from selenium impo ...

  5. Tomcat 访问manager app报403 解决方案(虚拟机可以正常使用,外面访问报错)

    虚拟机中Tomcat启动后,可以访问项目(虚拟机里面和外面都可以).虚拟机中能够正常进入manager app进行热部署工作,但是在外面能访问tomcat首页,点击manager app报403错误. ...

  6. 笔记五:python字符串

    一:学习内容 字符串类型 字符串类型判断 字符串类型互转 字符串小练习 二:字符串类型 1. basestring 在python中和字符串相关的数据类型为:str和unicode,他们都是bases ...

  7. VUE自定义指令生命周期,VUE生命周期

    一.自定义指令的生命周期 自定义指令有五个生命周期(也叫钩子函数),分别是 bind,inserted,update,componentUpdated,unbind bind:只调用一次,指令第一次绑 ...

  8. ARM的体系结构与编程系列博客——ARM的历史与应用范围

    前言 最近我感觉自己比较浮躁,重来没有好好地沉下心来做一件事情,而且针对自己在专业水平上仍然还有很多欠缺,于是我想我应该为自己做些什么来证明一下自己真的是潜心研究东西的人,于是我萌生了一个想法,真正地 ...

  9. 配置jenkins slave 问题,ERROR: Couldn't find any revision to build. Verify the repository and branch configuration for this job.

    因为需要需要搭建一套自动化环境的windows电脑作为slave,简单的把原来用来mac上的job配置一模一样的配置了下,运行时遇到如上问题,google半天百思不得其解, 后来尝试把SCM里面的br ...

  10. 用Shell编写项目发布脚本

    1.首先在github上创建一个测试用的仓库 2.本地编写一个可以运行的测试项目,上传至github 3.链接服务器,编写脚本如下:注意:编写前需要在服务器上安装git和maven 执行build_c ...