精华内容
下载资源
问答
  • Android相机

    千次阅读 2016-03-24 08:41:57
    java.lang.Object ↳ ...android.hardware.camera ...相机类用于设置图像捕获设置,启动/停止预览、...访问设备的相机,你必须申报相机在你的Android Manifest许可。当然也要包括manifest元素声明应用程序所使用的
    java.lang.Object
    android.hardware.camera

    类概述

    相机类用于设置图像捕获设置,启动/停止预览、拍照、和检索视频帧的编码。这个类是一个为相机服务客户,管理实际相机硬件。

    访问设备的相机,你必须申报相机在你的Android Manifest许可。当然也要包括<uses-feature>manifest元素声明应用程序所使用的相机功能。例如,如果您使用的相机和自动对焦功能,你的清单应包括以下内容:

    <uses-permissionAndroid的:名称=“Android的许可。相机”/>
    <uses-featureAndroid的:名称=“Android的硬件。相机”/>
    <uses-featureAndroid的:名称=“Android的硬件。相机自动对焦”/>

    拍照这一类,使用以下步骤:

    1. 从获取摄像机的一个实例打开(int)
    2. 把现有的(默认)设置getparameters()
    3. 如果有必要,修改回来相机参数对象并调用setparameters(相机参数)
    4. 如果需要,打电话setdisplayorientation(int)
    5. 重要通过一个完全初始化:SurfaceHolder.lockCanvassetpreviewdisplay(surfaceholder)。没有表面,相机将无法启动预览。
    6. 重要电话:startpreview()开始更新预览表。预览之前你必须拍一张照片开始。
    7. 想你的时候,电话(camera.shuttercallback TakePicture、拍照、产生图片时触发,拍照、产生图片时触发,相机。picturecallback)捕捉到的照片。等提供实际的图像数据的回调。
    8. 拍照后,预览显示将停止。拍些照片,电话startpreview()再来一次
    9. 呼叫stoppreview()停止更新预览表
    10. 重要:呼叫release()释放相机由其他应用程序使用。应用程序应该立即释放相机onpause()(再—open()它在onresume()

    快速切换到录像模式,使用这些步骤:

    1. 获取并初始化一个摄像头和上述开始预览。
    2. 呼叫unlock()让媒体过程访问摄像机。
    3. 通过相机setcamera(相机)。看到mediarecorder视频记录信息
    4. 当录制完成,调用reconnect()重新获得和重新锁定相机。
    5. 如果需要,重新启动预览拍更多的照片或视频。
    6. 呼叫stoppreview()release()如上所述

    这类不是线程安全的,并且是用一个事件的线程。最耗时的操作(预览,焦点,照片拍摄,等)并调用回调异步发生的必要。回调将在事件线程中调用打开(int)被称为从。这个类中的方法不能被多个线程同时。

    谨慎:不同的Android设备可能有不同的硬件规格,如万像素的评级和自动对焦能力。为了能够与更多的设备兼容的应用程序,你不应该把关于设备的摄像头规格的假设。

    开发者指南

    关于使用相机的更多信息,阅读相机开发指南

    概要



    公共方法
    最后的空隙 addcallbackbuffer(byte [] callbackbuffer)
    增加了一个预分配的缓冲区来预览回调缓冲队列。
    最后的空隙 自动对焦camera.autofocuscallbackCB)
    启动相机自动对焦和注册一个回调函数运行时,相机聚焦。
    最后的空隙 cancelautofocus()
    取消任何自动对焦功能的进展。
    最后布尔 enableshuttersound(布尔启用)

    启用或禁用默认的快门声拍照的时候。

    static void getcamerainfo(int cameraid,camera.camerainfocamerainfo)
    返回有关特定相机的信息。
    静态变量 getnumberofcameras()
    返回物理相机可以在数本装置。
    相机参数 得到相机的参数()
    返回此相机服务的当前设置。
    最后的空隙 锁具()
    重新锁定相机防止其他进程访问它。
    静态相机 开放(int cameraid)
    创建一个新的相机对象访问特定硬件的相机。
    静态相机 开放()
    创建一个新的相机对象访问第一个背对着设备上的照相机。
    最后的空隙 重新连接()
    重新连接到相机服务后,另一个进程使用它。
    最后的空隙 发布()
    断开并释放相机的对象资源。
    无效 setautofocusmovecallbackcamera.autofocusmovecallbackCB)
    设置相机自动对焦移回调。
    最后的空隙 setdisplayorientation(int度)
    设置预览显示顺时针旋转度。
    最后的空隙 seterrorcallbackcamera.errorcallbackCB)
    注册一个回调函数被调用时发生错误。
    最后的空隙 setfacedetectionlistenercamera.facedetectionlistener听众)
    注册一个监听器被通知在预览框中检测到的面孔。
    最后的空隙 setoneshotpreviewcallbackcamera.previewcallbackCB)

    安装一个回调是为下一个预览除了显示在屏幕框架调用。

    无效 setparameters相机参数参数)
    更改此相机服务设置
    最后的空隙 setpreviewcallbackcamera.previewcallbackCB)

    安装一个回调是除了显示屏幕上的每个预览框架调用。

    最后的空隙 setpreviewcallbackwithbuffercamera.previewcallbackCB)

    安装一个回调是为每个预览框架调用,使用缓冲区提供addcallbackbuffer(byte []),除了显示在屏幕上

    最后的空隙 setpreviewdisplaySurfaceHolder.lockCanvas持有人)
    表面用于实时预览
    最后的空隙 setpreviewtexture表面结构表面结构)
    表面结构用于实时预览
    最后的空隙 setzoomchangelistenercamera.onzoomchangelistener听众)
    注册侦听器时要通知缩放值更新摄像头驱动在平滑缩放。
    最后的空隙 startfacedetection()
    从the Face detection。
    最后的空隙 开始预览()
    开始捕捉和绘制到屏幕预览框。
    最后的空隙 startsmoothzoom(int值)
    放大到要求值平稳
    最后的空隙 stopfacedetection()
    the Face检测停止。
    最后的空隙 停止预览()
    停止捕获和图纸预览框的表面,并将相机未来的呼叫startpreview()
    最后的空隙 stopsmoothzoom()
    停止平滑缩放
    最后的空隙 TakePicturecamera.shuttercallback快门,拍照、产生图片时触发原,拍照、产生图片时触发JPEG)
    相当于TakePicture(快门,原,null,JPEG)。
    最后的空隙 TakePicturecamera.shuttercallback快门,拍照、产生图片时触发原,拍照、产生图片时触发postview,拍照、产生图片时触发JPEG)
    触发器的异步图像捕捉。
    最后的空隙 解锁()
    打开相机允许另一个进程访问它。
         
    保护的方法
    无效 完成()
    当调用垃圾收集器发现这个实例不可达。
    [扩展]
    继承的方法
    从类java.lang.Object

    常数


    public static final字符串action_new_picture

    广播操作:新图片是由相机拍摄,和图片的条目被添加到媒体商店。GetData()是URI的图片

    常数值:“android.hardware.action.new _图片”

    public static final字符串action_new_video

    广播操作:一个新的视频被摄像机记录,和视频的入口已被添加到媒体商店。GetData()是URI的视频

    常数值:“android.hardware.action.new _视频”

    public static final intcamera_error_server_died

    加入API级别1

    媒体服务器死了。在这种情况下,应用程序必须释放相机对象实例化一个新的。

    常数值:100(0x00000064)

    public static final intcamera_error_unknown

    加入API级别1

    未指定摄像机误差

    常数值:1(0x00000001)

    公共方法


    公众最后的空白addcallbackbuffer(byte [] callbackbuffer)

    增加了一个预分配的缓冲区来预览回调缓冲队列。应用程序可以添加一个或多个缓冲区队列。当一个预览帧到达时,至少还有一个可用的缓冲区,缓冲区将用于从队列中移除。然后预览回调与缓冲区调用。如果一个帧到达时,没有缓冲的左边,帧将被丢弃。应用程序应加缓冲器回来当他们完成他们的数据处理。

    格式除了YV12,缓冲区的大小乘以预览图像的宽度,确定的高度,和每像素的字节。宽度和高度可以读getpreviewsize()。每像素的字节可以计算getbitsperpixel(int)8、利用图像格式getpreviewformat()

    如果使用YV12格式,大小可以用列方程计算setpreviewformat(int)

    此方法时才有必要setpreviewcallbackwithbuffer(previewcallback)使用。什么 时候setpreviewcallback(previewcallback)setoneshotpreviewcallback(previewcallback)使用缓冲区自动分配。当提供的缓冲区太小,将预览帧数据,预览回调将返回null,缓冲区将从缓冲队列中移除。

    参数
    callbackbuffer 缓冲区添加到队列。缓冲区的大小必须符合上述值。

    公众最后的空白自动对焦camera.autofocuscallbackCB)

    加入API级别1

    启动相机自动对焦和注册一个回调函数运行时,相机聚焦。这种方法是唯一有效的预览时主动(之间startpreview()和之前stoppreview()

    用户应该检查getfocusmode()为了确定这种方法被称为。如果相机不支持自动对焦,这是一个没有OPonautofocus(布尔,相机)回调会被立刻

    如果你的应用程序不应安装在设备没有自动对焦,你必须声明你的应用程序使用自动对焦的<uses-feature>表现元素

    如果当前的闪光模式是不闪光灯关闭模式_ _Flash可以被解雇,在自动对焦,根据司机和相机硬件。

    自动曝光锁定getautoexposurelock()自动白平衡锁getautowhitebalancelock()不在自动对焦和之后的变化。但自动对焦程序可能停止自动曝光和自动白平衡瞬时在聚焦。

    停止预览stoppreview(),或触发静止图像捕捉(camera.shuttercallback TakePicture、拍照、产生图片时触发,相机。picturecallback),不会改变焦点位置。应用程序必须调用cancelautofocus重置的焦点。

    如果对焦成功,可以考虑使用mediaactionsound正确播放自动对焦成功的声音给用户。

    参数
    CB 回调运行

    公众最后的空白cancelautofocus()

    加入5级的API

    取消任何自动对焦功能的进展。是否自动对焦是目前正在进行中,该函数将返回到默认的焦点位置。如果相机不支持自动对焦,这是一个空操作。

    public final布尔enableshuttersound(布尔启用)

    启用或禁用默认的快门声拍照的时候。

    默认情况下,相机是系统定义的相机的快门声时(camera.shuttercallback TakePicture、拍照、产生图片时触发,相机。picturecallback)被称为。使用这种方法,快门声可以被禁用。这是强烈建议选择快门声音了camera.shuttercallback当系统的快门声被禁用。

    请注意,设备可能不总是允许禁用相机快门声。如果快门声音状态不能设置为所需的值,此方法将返回false。candisableshuttersound可以用来确定设备是否会让快门声被禁用。

    参数
    启用 相机是否应该发挥系统的快门声时TakePicture被称为
    退货
    • 真正的如果快门声音状态更改成功。如果快门声音状态是不能改变的。真正的也就是如果快门声音播放已设置为请求的状态恢复。

    public static voidgetcamerainfo(int cameraid,camera.camerainfocamerainfo)

    加入API级别9

    返回有关特定相机的信息。如果getnumberofcameras()返回N、有效的ID是0到n-1。

    public static intgetnumberofcameras()

    加入API级别9

    返回物理相机可以在数本装置。

    公共相机参数得到相机的参数()

    加入API级别1

    返回此相机服务的当前设置。如果修改了返回的参数,它们必须通过setparameters(相机参数)生效

    公众最后的空白锁具()

    加入5级的API

    重新锁定相机防止其他进程访问它。相机的默认对象锁定unlock()被称为。通常reconnect()代替

    由于API级别14,相机会自动锁定应用程序start()。应用程序可以使用相机(如:缩放)后开始录音。没有必要把这记录开始或停止后。

    如果你不能录制视频,你可能不需要这种方法。

    抛出
    运行期异常 如果相机不能重新锁定(例如,如果相机仍然被另一个进程使用)。

    公共静态相机开放(int cameraid)

    加入API级别9

    创建一个新的相机对象访问特定硬件的相机。如果相同的相机是由其他应用程序打开,这将抛出一个RuntimeException。

    你必须调用release()当你使用完相机,否则将被锁定,无法使用其他应用程序。

    你的程序应该只有一个相机对象主动在一个特定的硬件相机时间。

    从其他方法回调传递到线程称为open()事件循环。如果线程没有事件循环,然后回调传递到主应用程序事件循环。如果没有主应用程序事件循环,回调不交付。

    谨慎:在一些设备上,这个方法可能需要很长的时间来完成。最好是叫一个工作线程(可能使用这种方法异步任务)以避免阻塞UI线程的主要应用。

    参数
    cameraid 相机的硬件访问,在0getnumberofcameras()- 1
    退货
    • 一个新的相机连接,锁定对象,并准备使用。
    抛出
    运行期异常 如果打开相机失败(例如,如果相机在使用由另一个进程或设备策略管理器禁用了相机)。

    公共静态相机开放()

    加入API级别1

    创建一个新的相机对象访问第一个背对着设备上的照相机。如果设备没有后置摄像头,这将返回null。

    公众最后的空白重新连接()

    重新连接到相机服务后,另一个进程使用它。后unlock()被称为另一个进程可以使用相机;当过程完成后,您必须重新连接到相机,这将重新获取锁并允许你继续使用相机。

    由于API级别14,相机会自动锁定应用程序start()。应用程序可以使用相机(如:缩放)后开始录音。没有必要把这记录开始或停止后。

    如果你不能录制视频,你可能不需要这种方法。

    抛出
    IOException 如果无法连接重新建立(例如,如果相机仍然被另一个进程使用)。

    公众最后的空白发布()

    加入API级别1

    断开并释放相机的对象资源。

    你必须把这当你完成摄像机的对象。

    公共无效setautofocusmovecallbackcamera.autofocusmovecallbackCB)

    设置相机自动对焦移回调。

    参数
    CB 回调运行

    公众最后的空白setdisplayorientation(int度)

    设置预览显示顺时针旋转度。这会影响预览框显示在快照图片。这种方法适用于纵向模式的应用。注意,前置摄像头预览显示水平翻转在旋转,即图像反映在相机传感器的中心垂直轴。因此,用户可以看到自己的镜子。

    这并不影响字节数组传入的顺序onpreviewframe(byte [],相机)JPEG图片或录制的视频。这种方法不允许被称为预览时。

    如果你想使摄像机的图像显示在同一方向的显示,你可以使用下面的代码。

    公共静态无效setcameradisplayorientation活动活动
    intcameraid安卓硬件相机相机{
    安卓硬件相机camerainfo信息=
    安卓硬件相机camerainfo();
    安卓硬件相机getcamerainfocameraid信息);
    int旋转=活动getwindowmanager()getdefaultdisplay()
    getrotation();
    int=
    开关旋转{
    案例表面旋转_ 0=打破
    案例表面_旋转90=九十打破
    案例表面_旋转180=一百八十打破
    案例表面旋转_ 270=二百七十打破
    }
    
    int结果
    如果信息面对==相机camerainfocamera_facing_front{
    结果=信息取向 %三百六十
    结果=三百六十结果%三百六十/ /补偿镜
    }其他的{/ /背面
    结果=信息取向 三百六十%三百六十
    }
    相机setdisplayorientation结果);
    }
    

    从API级别14,这种方法可以称为当预览活动。

    参数
    这幅画将顺时针旋转的角度。有效值是0,90,180,和270。起始位置是0(景观)。

    公众最后的空白seterrorcallbackcamera.errorcallbackCB)

    加入API级别1

    注册一个回调函数被调用时发生错误。

    参数
    CB 回调运行

    公众最后的空白setfacedetectionlistenercamera.facedetectionlistener听众)

    注册一个监听器被通知在预览框中检测到的面孔。

    参数
    听众 听者通知

    公众最后的空白setoneshotpreviewcallbackcamera.previewcallbackCB)

    安装一个回调是为下一个预览除了显示在屏幕框架调用。之后的一个调用,回调了。这种方法可以称为“任何时候,即使是住在预览。其他预览回调可以重写。

    如果您使用的是预览数据创建视频或静止图像,强烈考虑使用mediaactionsound为了正确反映图像捕捉或记录开始/停止给用户。

    参数
    CB 回调对象,收到下一个预览框,或空停止接收回调。

    公共无效setparameters相机参数参数)

    加入API级别1

    更改此相机服务设置

    参数
    参数 该参数用于此相机服务
    抛出
    运行期异常 如果任何参数无效或不被支持。

    公众最后的空白setpreviewcallbackcamera.previewcallbackCB)

    加入API级别1

    安装一个回调是除了显示屏幕上的每个预览框架调用。回调将多次呼吁只要预览活动。这种方法可以在任何时间,甚至在预览是活的。其他预览回调可以重写。

    如果您使用的是预览数据创建视频或静止图像,强烈考虑使用mediaactionsound为了正确反映图像捕捉或记录开始/停止给用户。

    参数
    CB 回调对象的副本的预览框,或空停止接收回调。

    公众最后的空白setpreviewcallbackwithbuffercamera.previewcallbackCB)

    安装一个回调是为每个预览框架调用,使用缓冲区提供addcallbackbuffer(byte []),除了显示在屏幕上。回调将多次呼吁只要预览活动,缓冲区可用。其他预览回调可以重写。

    这种方法的目的是提高效率,通过允许预览预览帧存储器复用帧速率。你必须调用addcallbackbuffer(byte [])在一些点,之前或调用此方法后,没有回调将收到。

    缓冲区队列将被清除,如果这种方法被称为一个空的回调,setpreviewcallback(相机。previewcallback)叫,或setoneshotpreviewcallback(camera.previewcallback)被称为

    如果您使用的是预览数据创建视频或静止图像,强烈考虑使用mediaactionsound为了正确反映图像捕捉或记录开始/停止给用户。

    参数
    CB 回调对象收到一份预览框,或空停止接收回调和清除缓冲区队列。

    公众最后的空白setpreviewdisplaySurfaceHolder.lockCanvas持有人)

    加入API级别1

    表面用于实时预览。一个表面或表面纹理是必要的预览,预览是必要的拍照。相同的表面可以不伤害再定。设置预览将不设置任何表面的表面纹理,并通过设置预览setpreviewtexture(表面)

    这个SurfaceHolder.lockCanvas必须已经包含一个表面时,调用此方法。如果你使用的是SurfaceView,你都需要登记回调SurfaceHolder。addCallback(SurfaceHolder。回调)等待surfacecreated(SurfaceHolder)在启动呼叫setpreviewdisplay(或预览)。

    这个方法必须调用之前startpreview()。唯一的例外是,如果没有设置预览表面(或设置为null)在startpreview()叫,那么这种方法可能是一个非空参数设置预览表面曾经称。(这让相机设置和表面生成发生在平行的,节省时间。)预览表面不得同时预览运行变化。

    参数
    持有人 含有表面上放置的预览,或空删除预览表
    抛出
    IOException 如果这个方法失败(例如,如果表面不可用或不适合)。

    公众最后的空白setpreviewtexture表面结构表面结构)

    表面结构用于实时预览。一个表面或表面纹理是必要的预览,预览是必要的拍照。相同的表面纹理可以不伤害再定。设置预览表面纹理将联合国设置任何,通过预览表setpreviewdisplay(surfaceholder)

    这个方法必须调用之前startpreview()。唯一的例外是,如果没有设置预览表面纹理(或设置为null)在startpreview()叫,那么这种方法可能是一个非空参数设置预览表面曾经称。(这让相机设置和表面生成发生在平行的,节省时间。)预览表面纹理可能没有在预览运行变化。

    提供的时间戳gettimestamp()一个表面设置为预览纹理有未知的零点,并不能直接比较不同的相机或同一相机的不同实例之间,或在多个运行相同的程序。

    如果您使用的是预览数据创建视频或静止图像,强烈考虑使用mediaactionsound为了正确反映图像捕捉或记录开始/停止给用户。

    参数
    表面结构 这个表面结构该预览图像将被发送或空删除当前预览的表面纹理
    抛出
    IOException 如果这个方法失败(例如,如果表面纹理不可用或不适合)。

    公众最后的空白setzoomchangelistenercamera.onzoomchangelistener听众)

    注册侦听器时要通知缩放值更新摄像头驱动在平滑缩放。

    参数
    听众 听者通知

    公众最后的空白startfacedetection()

    开始的人脸检测。这应该是预览之后开始叫。相机会通知camera.facedetectionlistener在预览框中检测出人脸的。检测到的面部会像以往一样。应用程序应该调用stopfacedetection()停止的人脸检测。这种方法支持如果getmaxnumdetectedfaces()返回一个数大于0。如果人脸检测已经开始,应用程序不应该叫了。

    当人脸检测运行,setwhitebalance(字符串)setfocusareas(list),和setmeteringareas(名单)有没有影响。该相机采用的人脸进行自动白平衡,自动曝光和自动对焦。

    如果应用程序调用自动对焦(autofocuscallback),相机会停止发送面临回调。最后面临回调表明用来做自动对焦区域。重点完成人脸检测后,将继续发送面临回调。如果应用程序调用cancelautofocus()面对回调,也将恢复

    在调用(camera.shuttercallback TakePicture、拍照、产生图片时触发,相机。picturecallback)stoppreview(),然后恢复预览startpreview(),应用程序应该调用这个方法恢复人脸检测。

    抛出
    illegalargumentexception 如果人脸检测是不支持的。
    运行期异常 如果这个方法失败或人脸检测已经运行。

    公众最后的空白开始预览()

    加入API级别1

    公众最后的空白startsmoothzoom(int值)

    放大到要求值平稳。司机会通知camera.onzoomchangelistener的缩放值是否和变焦停止时间。例如,假设当前缩放为0,startsmoothzoom叫做价值3。这个onzoomchange(int,boolean,相机)方法将被调用三次缩放值1,2,和3。应用程序可以调用stopsmoothzoom()停止变焦早。应用程序不应再打startsmoothzoom或改变缩放值在变焦停止。如果提供的缩放值等于当前的缩放值,没有放大回调将产生。这种方法支持如果issmoothzoomsupported()返回true

    参数
    价值 缩放值。有效的范围是0到getmaxzoom()
    抛出
    illegalargumentexception 如果缩放值是无效的
    运行期异常 如果这个方法失败

    公众最后的空白stopfacedetection()

    the Face检测停止。

    公众最后的空白停止预览()

    加入API级别1

    停止捕获和图纸预览框的表面,并将相机未来的呼叫startpreview()

    公众最后的空白stopsmoothzoom()

    停止平滑缩放。应用程序应该等待camera.onzoomchangelistener知道变焦其实是停下来的时候。这种方法支持如果issmoothzoomsupported()是真的

    抛出
    运行期异常 如果这个方法失败

    公众最后的空白TakePicturecamera.shuttercallback快门,拍照、产生图片时触发原,拍照、产生图片时触发JPEG)

    加入API级别1

    相当于TakePicture(快门,原,null,JPEG)。

    公众最后的空白TakePicturecamera.shuttercallback快门,拍照、产生图片时触发原,拍照、产生图片时触发postview,拍照、产生图片时触发JPEG)

    加入5级的API

    触发器的异步图像捕捉。摄像服务将引发一系列的回调应用作为图像采集的进展。快门回调发生后的图像被捕获。这可以用来触发一个声音让用户知道图像已被抓获。原回调发生时,原始图像数据是可用的(注:数据将是null,如果没有原图像回调缓冲区可用或原始图像回调缓冲区不足以容纳原图像)。的postview回调时发生的比例,完全处理postview图像资料(注意:不是所有的硬件支持)。JPEG压缩图像的回调发生时可用。如果应用程序不需要一个特定的回调,空可以通过代替回调方法。

    这种方法是唯一有效的预览活动时(后startpreview())。预览将图片后停止;用户必须调用startpreview()如果他们想重新开始预览或拍更多的照片。这不应该被称为之间start()停止()

    调用此方法后,你必须不叫startpreview()或采取另一张图片到JPEG回调回来了。

    参数
    快门 图像捕捉瞬间的回调,或空
    原回调(压缩)的图像数据,或空
    postview 与postview图像数据的回调,可能是空的
    JPEG JPEG图像数据的回调,或空

    公众最后的空白解锁()

    加入5级的API

    打开相机允许另一个进程访问它。通常情况下,镜头锁定在与主动摄像机目标前的过程release()被称为。允许进程间快速切换,你可以调用这个方法来释放相机暂时的另一个过程使用;一旦其他进程完成后你可以打电话reconnect()回收相机

    这必须在调用setcamera(相机)。这不能称为记录后开始。

    如果你不能录制视频,你可能不需要这种方法。

    抛出
    运行期异常 如果相机不能解锁

    保护的方法

    protected void完成()

    加入API级别1

    当调用垃圾收集器发现这个实例不可达。默认实现不执行任何操作,但可以重写该方法来释放资源。

    注意,对象覆盖完成比的对象,没有终结器的对象后,可以运行很长一段时间更昂贵的不再是可达的,这取决于内存的压力,所以它是依靠他们清理一个坏主意。注意,程序运行在一台虚拟机宽终结器线程,这样堵在终结器的工作是一个坏主意。终结器通常只需要一个类有一个本地的同行和需要调用本地方法来消灭同行。即使这样,最好是提供一个明确的关闭(与实现方法闭合),坚持用户手动处理实例。这对于类似的文件,但更像一个BigInteger其中的典型调用代码会处理很多临时性的问题。不幸的是,代码创建很多临时性的问题是最严重的单一的终结器线程的角度编码。

    如果你必须使用终结器,至少考虑提供自己的并拥有自己的线程队列。

    与构造函数,析构函数不会自动锁。你是负责调用super.finalize()你自己

    未捕获的异常被忽略和不终止程序终结器线程。看到通用程序设计第7项“避免finalizers”更多。



    展开全文
  • Android相机开发

    千次阅读 2019-03-21 00:29:02
    文章目录Android相机开发申请权限创建一个可以预览的界面1.创建一个新工程2.在新创建的工程中activity中布局文件3.创建一个相机预览的view 继承SurfaceView4.在activity中添加添加偏好设置预览分辨率预览格式照片...

    Android相机开发

    申请权限

    <uses-permission android:name="android.permission.CAMERA" />
    <!--可以防止APP被安装到没有相机的Android设备上(目前仅Google Play支持)-->
    <uses-feature android:name="android.hardware.camera" />
    

    动态权限不要忘了

    创建一个可以预览的界面

    1.创建一个新工程

    2.在新创建的工程中activity中布局文件

    <FrameLayout
        android:id="@+id/preview_f"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
    

    3.创建一个相机预览的view 继承SurfaceView

    最新都开始用TextureView,关于SurfaceView/TextureView

    • SurfaceView是一个有自己Surface的View。界面渲染可以放在单独线程而不是主线程中。它更像是一个Window,自身不能做变形和动画。
    • TextureView同样也有自己的Surface。但是它只能在拥有硬件加速层层的Window中绘制,它更像是一个普通View,可以做变形和动画。

    更多关于SurfaceView与TextureView区别的内容可以参考这篇文章Android 5.0(Lollipop)中的SurfaceTexture,TextureView, SurfaceView和GLSurfaceView.

    官方给出的方案,图片来自于Android平台Camera开发实践指南:

    官方给出的方案

    public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
    
        private final SurfaceHolder mHolder;
        private Camera mCamera;
    
        public CameraPreview(Context context) {
            super(context);
            mHolder = getHolder();
            mHolder.addCallback(this);
        }
    
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
        	//surface第一次创建时回调
            //打开相机
            mCamera = Camera.open();
            try {
                mCamera.setPreviewDisplay(holder);
                mCamera.startPreview();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    		//surface变化的时候回调(格式/大小)
        }
    
        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
        	//surface销毁的时候回调
            mHolder.removeCallback(this);
            mCamera.setPreviewCallback(null);
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
    }
    

    4.在activity中添加

    public class MainActivity extends AppCompatActivity {
    
        private FrameLayout mFrameLayout;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initView();
            initCamera();
        }
    
        private void initCamera() {
            CameraPreview preview = new CameraPreview(this);
            mFrameLayout.addView(preview);
        }
    
        private void initView() {
            mFrameLayout = (FrameLayout) findViewById(R.id.preview_f);
        }
    }
    

    至此可以显示一个相机界面,并有图像显示。

    添加偏好设置

    如分辨率、闪光灯、对焦等。

    通过当前界面的相机camera对象获取起设置的参数getParameters()

    预览分辨率

    • parameters.getSupportedPreviewSizes()获取相机支持的所有预览分辨率

    预览格式

    具体参照ImageFormat或者自己Google

    • parameters.getSupportedPreviewFormats()获取相机支持的所有预览格式

    照片分辨率

    • parameters.getSupportedPictureSizes()获取相机支持的所有图片分辨率

    图片格式

    具体参照ImageFormat或者自己Google

    • parameters.getSupportedPictureFormats()获取相机支持的所有图片格式

    视频分辨率

    parameters.getSupportedVideoSizes()获取相机支持的所有视频分辨率

    对焦模式

    • parameters.getSupportedFocusModes()获取相机支持的所有对焦模式

    曝光补偿

    • parameters.getMinExposureCompensation()获取相机支持的最低曝光补偿
    • parameters.getMaxExposureCompensation()获取相机支持的最高曝光补偿

    闪光灯模式

    • parameters.getSupportedFlashModes()获取相机支持的闪光灯模式

    白平衡

    • parameters.getSupportedWhiteBalance()获取相机支持的白平衡

    场景

    parameters.getSupportedSceneModes()获取相机支持的场景

    声明GPS权限

    想要拍到的照片中包含GPS信息

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

    代码如下

    int numberOfCameras = Camera.getNumberOfCameras();
    Log.d("123===", "相机个数===" + numberOfCameras);
    Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
    for (int i = 0; i < numberOfCameras; i++) {
        Camera.getCameraInfo(i, cameraInfo);
        //后置:0 CAMERA_FACING_BACK; 前置:1 CAMERA_FACING_FRONT
        Log.d("123===", "当前相机信息=" + cameraInfo.facing);
    }
    
    
    Camera camera = mPreview.getCamera();
    Camera.Parameters parameters = camera.getParameters();
    
    Log.d("123===", "预览分辨率-----------");
    List<Camera.Size> supportedPreviewSizes = parameters.getSupportedPreviewSizes();
    for (Camera.Size previewSize : supportedPreviewSizes) {
        Log.d("123===", previewSize.width + "---" + previewSize.height);
    }
    Log.d("123===", "预览分辨率-----------");
    Log.d("123===", "");
    Log.d("123===", "");
    
    
    Log.d("123===", "获得相机支持的图片预览格式-----------ImageFormat");
    List<Integer> supportedPreviewFormats = parameters.getSupportedPreviewFormats();
    for (Integer supportedPreviewFormat : supportedPreviewFormats) {
        Log.d("123===", supportedPreviewFormat + "");
    }
    Log.d("123===", "获得相机支持的图片预览格式-----------");
    Log.d("123===", "");
    Log.d("123===", "");
    
    
    Log.d("123===", "照片分辨率-----------");
    List<Camera.Size> supportedPictureSizes = parameters.getSupportedPictureSizes();
    for (Camera.Size pictureSize : supportedPictureSizes) {
        Log.d("123===", pictureSize.width + "---" + pictureSize.height);
    }
    Log.d("123===", "照片分辨率-----------");
    Log.d("123===", "");
    Log.d("123===", "");
    
    
    Log.d("123===", "获得相机支持的图片格式-----------ImageFormat");
    List<Integer> supportedPictureFormats = parameters.getSupportedPictureFormats();
    for (Integer supportedPreviewFormat : supportedPictureFormats) {
        Log.d("123===", supportedPreviewFormat + "");
    }
    Log.d("123===", "获得相机支持的图片格式-----------");
    Log.d("123===", "");
    Log.d("123===", "");
    
    
    Log.d("123===", "视频分辨率-----------");
    List<Camera.Size> supportedVideoSizes = parameters.getSupportedVideoSizes();
    for (Camera.Size supportedVideoSize : supportedVideoSizes) {
        Log.d("123===", supportedVideoSize.width + "---" + supportedVideoSize.height);
    
    }
    Log.d("123===", "视频分辨率-----------");
    Log.d("123===", "");
    Log.d("123===", "");
    
    Log.d("123===", "对焦模式-----------ImageFormat");
    List<String> supportedFocusModes = parameters.getSupportedFocusModes();
    for (String supportedFocusMode : supportedFocusModes) {
        Log.d("123===", supportedFocusMode + "");
    }
    Log.d("123===", "对焦模式-----------");
    Log.d("123===", "");
    Log.d("123===", "");
    
    Log.d("123===", "曝光补偿-----------");
    int minExposureCompensation = parameters.getMinExposureCompensation();
    int maxExposureCompensation = parameters.getMaxExposureCompensation();
    Log.d("123===", "最高=" + maxExposureCompensation);
    Log.d("123===", "最低=" + minExposureCompensation);
    Log.d("123===", "曝光补偿-----------");
    Log.d("123===", "");
    Log.d("123===", "");
    
    
    Log.d("123===", "支持闪光灯模式-----------");
    List<String> supportedFlashModes = parameters.getSupportedFlashModes();
    for (String supportedFlashMode : supportedFlashModes) {
        Log.d("123===", "闪光模式--->" + supportedFlashMode);
    }
    Log.d("123===", "支持闪光灯模式-----------");
    Log.d("123===", "");
    Log.d("123===", "");
    
    
    Log.d("123===", "支持白平衡-----------");
    List<String> supportedWhiteBalance = parameters.getSupportedWhiteBalance();
    for (String s : supportedWhiteBalance) {
        Log.d("123===", "--->" + s);
    }
    Log.d("123===", "支持白平衡-----------");
    Log.d("123===", "");
    Log.d("123===", "");
    
    Log.d("123===", "场景-----------");
    List<String> supportedSceneModes = parameters.getSupportedSceneModes();
    for (String s : supportedSceneModes) {
        Log.d("123===", "--->" + s);
    }
    Log.d("123===", "场景-----------");
    Log.d("123===", "");
    Log.d("123===", "");
    

    拍照,视频,对焦

    假如需要使用原生拍照功能的话,Camera#takePicture()

    拍照和视频都是网上那一套流程,具体的因为时间原因暂时先这样了。

    相机预览和保存注意事项

    下面三张图片引用自:Android: Camera相机开发详解(中) ——实现预览、拍照、保存照片等功能

    • 相机预览方向

    相机预览方向

    • 采集的图像方向

    采集的图像方向

    • 前置摄像头预览与保存一致

    前置摄像头预览与保存一致

    参考

    展开全文
  • Android相机屏幕适配

    千次阅读 2018-07-11 14:17:56
    本文默认你已经会的Android相机开发,但是苦恼于相机屏幕适配 如果不会相机开发,可以参考以下作者的文章: Tong ZHAN Android相机开发(一):最简单的相机 Android相机开发(二): 给相机加上偏好设置 Android...

    前言

    本文默认你已经会的Android相机开发,但是苦恼于相机屏幕适配
    如果不会相机开发,可以参考以下作者的文章:


    现在出现了越来越多的屏幕,再也不是那个到处充满1920x1080的时代了,所以给身为手机开发者的我们带来了不少麻烦,从而给我们带来了相机屏幕适配这块的问题,大家都知道,在使用相机的时候,如果不对屏幕做适配,那么在预览的时候,界面就会拉伸,这对用户可不是个好体验。

    普通解决方案

    先说说普通解决方法,我们可以通过camera得到手机摄像头的参数,比如曝光度、照片尺寸、预览尺寸等等的很多信息。所以我们通过以下代码可以得知摄像头到底支持哪些预览分辨率:

    // 相机所支持的分辨率
    Camera.Parameters parameters = mCamera.getParameters();
    List<Camera.Size> supportedPreviewSizes = parameters.getSupportedPreviewSizes();

    然后我们还可以得到手机的分辨率是多少

    // 手机分辨率
    DisplayMetrics dm = context.getResources().getDisplayMetrics();
    int w_screen = dm.widthPixels;
    int h_screen = dm.heightPixels;
    Point screenRelsolution = new Point(w_screen, h_screen);

    所以,我们可以用已知的手机分辨率和相机所支持的预览分辨率做对比,找出合适的预览分辨率为相机做适配。

    选择最佳分辨率可以参考yanzi1225627方案,也可以参考我的方案[笑]:

        /**
         * 选择最佳相机预览分辨率
         *
         * @param parameters 相机参数
         * @param point      手机分辨率 宽度:point.x,高度point.y
         * @return 最佳相机分辨率
         */
        public static Camera.Size getBestPreviewSize(Camera.Parameters parameters, Point point) {
    
            double x = point.x; //1080
            double y = point.y; //1920
    
            List<Camera.Size> supportedPreviewSizes = parameters.getSupportedPreviewSizes();
    
            List<Camera.Size> sameWidthList = new ArrayList<>();
    
            for (Camera.Size size : supportedPreviewSizes) {
    
                if (x == size.height) {
                    sameWidthList.add(size);
                }
    
            }
    
            int index = -1;
    
            int min = -1;
    
            for (int i = 0; i < sameWidthList.size(); i++) {
    
                Camera.Size s = sameWidthList.get(i);
    
                int abs = Math.abs(s.width - point.y);
    
                if (min == -1) {
                    min = abs;
                }
    
                if (abs <= min) {
                    min = abs;
                    index = i;
                }
    
            }
    
            return sameWidthList.get(index);
    
        }

    然后把最佳支持的分辨率设置给相机就行了

    mParams.setPreviewSize(bestPreviewSize.width, bestPreviewSize.height);

    终极适配方案

    使用普通适配方案,其实仔细想想,还是会存在一些问题,比如相机支持的预览分辨率里面,可能支持的分辨率里面没有跟手机分辨率相同,所以还是会导致预览的时候界面被拉伸,那么要怎样才能做到完美贴合般的适配呢,先说说思路

    假设手机分辨率为1770*1080,但是相机预览分辨率为1920*1080,也就是这个surfaceview为1920*1080,所以可能是这些样子:
    这里写图片描述
    其中红色框为手机分辨率,绿色框为相机预览分辨率,如果把这样SurfaceView放置到手机屏幕中(绿框填充红框),界面肯定会被拉长吧,还有可能会是如下情况:
    这里写图片描述
    这样界面肯定就被收窄了(绿框填充红框
    所以还是要适配一波,不过这个适配,主要目的是为了在预览的时候,让预览图不变形。那么我们就先来说说原理吧。

    原理
    由于相机预览分辨率中,没有跟手机一样的分辨率,所以,我们的点在于修改手机的界面,让界面妥协。

    比如手机的分辨率为1000*2000,而相机预览分辨率为1000*3000,那么我们就要将界面的布局设置为1000*3000,这样一来,在预览的时候,就不会出现界面拉伸的现象了。

    如果手机分辨率是1000*1000,而预览分辨率为500*700,我们就要将手机界面的长宽比例设置跟预览分辨率比例一样的,设置为5:7,这里要提一点的就是,不能将手机的界面大小设置为500*700,因为手机分辨率为1000*1000,如果只展示500*700的界面,虽然预览图像是正常了,没有拉伸现象了,但是会出现白边。

    所以我们应该将手机的界面大小设置为1000*1400,这样一来,手机整个界面就都是预览界面了。

    既然知道了原理解决起来就很容易了。

    实现

    实现步骤:

    1. 得到Camera对象,得到Camera.Parameters对象
    2. 得到相机支持的分辨率,在没有跟手机分辨率相同的情况下,选择最大预览分辨率
    3. 修改界面大小,将界面比例跟预览分辨率比例设置相同
    4. 开启预览

    可以使用接口回调的方式,在得到相机预览分辨率的时候,将预览分辨率回调给Activity,然后修改界面大小即可。

    所以现在我们获取最佳分辨率的方法应该如下:

        /**
         * 选择最佳相机预览分辨率
         *
         * @param parameters 相机参数
         * @param point      手机分辨率 宽度:point.x,高度point.y
         * @return 最佳相机分辨率
         */
        public static Camera.Size getBestPreviewSize(Camera.Parameters parameters, Point point) {
    
            List<Camera.Size> list = parameters.getSupportedPreviewSizes();
    
            int max = 0;
            int index = -1;
    
            for (int i = 0; i < list.size(); i++) {
                int height = list.get(i).height;
                int width = list.get(i).width;
    
                // 当手机分辨率和预览分辨率相同的时候,直接返回该预览分辨率
                // 否则选择最大分辨率
                if (point.x == width && point.y == height) {
                    return list.get(index);
                }
    
                Log.e(TAG, "getBestPreviewSize: 相机分辨率 = " + height + " x " + width);
    
                int i1 = height * width;
                if (i1 > max) {
                    max = i1;
                    index = i;
                }
            }
            return list.get(index);
    
        }

    获取手机分辨率的方法:

    public static Point getScreenMetrics(Context context) {
            DisplayMetrics dm = context.getResources().getDisplayMetrics();
            int w_screen = dm.widthPixels;
            int h_screen = dm.heightPixels;
            return new Point(w_screen, h_screen);
    }

    然后我们可以拟定一个接口,当得到最佳预览分辨率的时候,将该分辨率告知给Activity:

    public interface CamStartPreviewCallback{
        void postPreview(int width, int height);
    }

    所以告知给Activity的主要方式应该就是:(省略了无数代码)

    public void startPreview(SurfaceHolder holder, CamStartPreviewCallback callback) {
        ...
        mParams = mCamera.getParameters();
        Camera.Size bestPreviewSize = getBestPreviewSize(mParams, point);
        callback.postPreview(bestPreviewSize.width, bestPreviewSize.height); 
        ...       
    }

    postPreview()的具体实现如下:

    @Override
    public void postPreview(int width, int height) {
    
        cameraSurfaceView.post(new Runnable() {
                @Override
                public void run() {
    
                    Point screenMetrics = getScreenMetrics(context);
    
                    Log.e(TAG, "run: 手机分辨率 = " + screenMetrics.x + " x " + screenMetrics.y);
                    Log.e(TAG, "run: 相机预览分辨率 = " + width + " x " + height);
    
                    ViewGroup.LayoutParams layoutParams1 = root.getLayoutParams();
                    ViewGroup.LayoutParams layoutParams2 = cameraSurfaceView.getLayoutParams();
    
                    int fixWidth = 1080;
                    int fixHeight = 1920;
    
                    float screenRate = screenMetrics.y * 1.0f / screenMetrics.x;
                    float previewRate = width * 1.0f / height;
    
                    Log.e(TAG, "run: screenRate = " + screenRate);
                    Log.e(TAG, "run: previewRate = " + previewRate);
    
                    if (screenRate > previewRate) {
                        fixWidth = screenMetrics.y * screenMetrics.x / width;
                        fixHeight = screenMetrics.y;
                    } else if (screenRate < previewRate) {
                        fixWidth = screenMetrics.x;
                        fixHeight = (int) (screenMetrics.x * previewRate);
                    } else if (screenRate == previewRate) {
                        fixWidth = height;
                        fixHeight = width;
                    }
    
                    layoutParams1.width = fixWidth;
                    layoutParams1.height = fixHeight;
                    root.setLayoutParams(layoutParams1);
    
                    layoutParams2.width = fixWidth;
                    layoutParams2.height = fixHeight;
                    cameraSurfaceView.setLayoutParams(layoutParams2);
    
                }
            });
    
    }

    说明一下里面的root跟cameraSurfaceView是啥,root就是根布局,而cameraSurfaceView自然就是相机预览界面的surfaceview了
    布局界面如下:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/root"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <SurfaceView
                android:id="@+id/cameraSurfaceView"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />
    
        </RelativeLayout>
    
    </RelativeLayout>
    

    这样一来,我们的相机屏幕适配就完毕了。

    结语

    其实根据以上内容,大家也看得出来,其实我们在做相机屏幕适配的时候,并不是那种完美的适配,而是由于相机的预览分辨率无法修改,手机界面布局大小做了修改而已,其实这只是手机布局界面的妥协。谁让你不能变呢,那我变就行了呗!

    我整理了一下代码,供大家参考
    源码:https://github.com/qixuefeng/CameraAdaptDemo

    展开全文
  • Android相机开发实战

    万次阅读 多人点赞 2015-09-07 11:28:03
    开源分享二(Android相机开发实战教程) 上篇博文给大家分享了两个非常实用的项目功能模块,不知道大伙感觉如何?有木有一种臭袜子味扑鼻,酸爽的赶脚!!!贱笑贱笑了~ ~ OK!不扯淡了,言归正传。本文将主要为...

    开源分享二(Android相机开发实战)



    开源分享 一(StickerCamera + 仿微信多图选择)

    开源分享三(炫酷的Android Loading动画)


    前言


    上篇博文给大家分享了两个非常实用的项目功能模块,不知道大伙感觉如何?有木有一种臭袜子味扑鼻,酸爽的赶脚!!!贱笑贱笑了~ ~

    OK!不扯淡了,言归正传。本文将主要为大家介绍Android中自定义相机的开发,做Android应用的童鞋应该都知道,在应用中使用相机功能有两种方式:

    • 调用Camera API 自定义相机
    • 调用系统相机

    由于需求不同,所以选择的方案固然也不同,至于第二种调用系统相机,这里就不过多讲解了,使用Intent对象设置一个Action动作即可,跳转时使用startActivityForResult,然后在onActivityResult处理相关数据便可,关键代码:

    intent.setAction("android.media.action.STILL_IMAGE_CAMERA");

    至于使用,较常见的一般是应用中用户上传头像的时候调用,然后返回处理图像数据。


    而第一种自定义相机的方式使用也十分普遍,但是要做好这个模块,相对来说还是有一定难度的,之前分享过一个Github上的开源相机的项目,项目由美国的一个团队开发,集 拍照、摄影、各种特效动画 等功能与一身,本人之前研究了下,发现功能比较全面也很强大,抠出来单独拍照那一个模块,我滴妈呀,真TM费劲!相机不管是预览还是拍摄图像都还是很清晰的,自己当时也写了一个,比较操蛋,只能怪自己对这一块的优化了解浅显吧!特别是预览的时候,聚焦完成后,焦点周边会出现很多白色的噪点,密密麻麻,特别严重,头疼的很。不过也总算解决了,灰常感谢USA的那个什么什么团队的开源相机程序。经过自己改造后的预览效果图:




    下面看下这个项目的效果图,我也把地址甩底,大伙感兴趣的自行Clone研究(或者闲的蛋疼也可以抽时间剥离开每一个模块学习,作为日后的知识储备),里面也用到了这个Android中读取图片EXIF元数据之metadata-extractor的使用


    GitHub:https://github.com/xplodwild/android_packages_apps_Focal



    相机开发简介

    下面说说在Android中调用Camera来定义相机的最基本步骤:

    1. 打开相机 —— 调用Camera的open()方法。
    2. 获取拍照参数 —— 调用Camera的getParameters()方法,返回Camera.Parameters对象。
    3. 拍照参数设置 —— 调用Camera.Parameters对象。
    4. 拍照参数控制 —— 调用Camera的setParameters(),并将Camera.Parameters对象作为参数传入。注:Android2.3.3之后不用设置。
    5. 预览取景 —— 调用Camera的startPreview()方法,在之前注意调用Camera的setPreviewDisplay(SurfaceHolder holder)设置使用哪个SurfaceView来显示取得的图片。
    6. 拍照 —— 调用Camera的takePicture()
    7. 停止预览 —— 调用Camera的stopPreview()方法
    8. 资源释放 —— Camera.release()

    开启和关闭预览的联系如下:Camera ---- SurfaceHolder ------ SurfaceView

    关于SurfaceHolder.Callback必须实现的3个方法:

    surfaceCreated() 该方法在surfaceView被Create时调用
    surfaceChanged() 该方法是当surfaceView发生改变后调用
    surfaceDestroyed() 这个不用说了,销毁时调用

    surfaceHolder通过addCallBack()方法将响应的接口绑定


    注:必要Camera权限,例如:

    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
    
    <uses-permission android:name="android.permission.CAMERA"/>
    
    <uses-feature android:name="android.hardware.camera" />
    
    <uses-permission android:name="android.hardware.camera.autofocus" />
    
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    
    


    关于Camera下的Parameters类,其中封装了我们需要的大部分功能,下面做个简单介绍:

    1. setPictureFormat() 方法用于设置相机照片的格式,其参数是一个字符型参数,位于PixelFormat类中,如:PixelFormat.JPEG。
    2. setSceneMode() 方法用于设置相机场景类型,其参是是一个字符型参数,位于Parameters类中,以SCENE_MODE_开头。
    3. setZoom() 方法用于设置相机焦距,其参数是一个整型的参数,该参数的范围是0到Camera.getParameters().getMaxZoom()。
    4. setPictureSize() 方法用于设置相机照片的大小,参数为整型。
    5. setWhiteBalance() 方法用于设置相机照片白平衡,其参数是一个字符型,位于Parameters类中,以WHITE_BALANCE开头。
    6. setJpegQuality() 方法用于设置相机照片的质量,其参数是一个整型参数,取值范围为1到100。
    7. setFlashMode() 方法用于设置闪光灯的类型,其参数是一个字符型参数,位于Parameters类中,以FLASH_MODE_开头。
    8. setColorEffect() 方法用于设置照片颜色特效的类型,其参数是一个字符型参数,位于Parameters类中,以EFFECT_开头。


    本程序模块效果图及示例


    下面分享本篇Blog的示例相机模块,此功能模块并非上面开源项目中的剥离出来的,看下效果图咯:

             


             



    效果看着还可以吧(不点赞也太不给面子了吧  - . - ),下面个出主界面的布局代码:

    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    
        <!-- 预览画布 -->
        <SurfaceView
            android:id="@+id/surfaceView"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    
        <!-- 闪光灯、前置摄像头、后置摄像头、聚焦 -->
    
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent" >
    
            <org.gaochun.camera.CameraGrid
                android:id="@+id/camera_grid"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_alignParentTop="true" />
    
            <View
                android:id="@+id/focus_index"
                android:layout_width="40dp"
                android:layout_height="40dp"
                android:background="@drawable/camera_focus"
                android:visibility="invisible" />
    
            <ImageView
                android:id="@+id/flash_view"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentLeft="true"
                android:onClick="onClick"
                android:padding="15dp"
                android:scaleType="centerCrop"
                android:src="@drawable/camera_flash_off" />
    
            <ImageView
                android:id="@+id/camera_flip_view"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentRight="true"
                android:onClick="onClick"
                android:padding="15dp"
                android:scaleType="centerCrop"
                android:src="@drawable/camera_flip" />
    
            <!-- 底部按钮 -->
    
            <RelativeLayout
                android:layout_width="fill_parent"
                android:layout_height="70dp"
                android:layout_alignParentBottom="true"
                android:background="#a0000000"
                android:padding="5dp" >
    
                <Button
                    android:id="@+id/search"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="30dp"
                    android:background="@null"
                    android:drawablePadding="3dp"
                    android:drawableTop="@drawable/ic_search_selector"
                    android:onClick="onClick"
                    android:text="搜图"
                    android:textColor="@drawable/row_selector_text" />
    
                <ImageView
                    android:id="@+id/action_button"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_centerInParent="true"
                    android:clickable="true"
                    android:onClick="onClick"
                    android:src="@drawable/btn_shutter_photo" />
    
                <Button
                    android:id="@+id/takephoto"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_alignParentRight="true"
                    android:layout_marginRight="30dp"
                    android:background="@null"
                    android:drawablePadding="3dp"
                    android:drawableTop="@drawable/ic_takephoto_selector"
                    android:onClick="onClick"
                    android:text="拍照"
                    android:textColor="@drawable/row_selector_text" />
            </RelativeLayout>
        </RelativeLayout>
    
    </FrameLayout>


    下面是核心模块 CameraPreview 类:

    public class CameraPreview extends ViewGroup implements SurfaceHolder.Callback, Camera.AutoFocusCallback {
    
    	private SurfaceView mSurfaceView;
    	private SurfaceHolder mHolder;
    	private Size mPreviewSize;
    	private Size adapterSize;
    	//private List<Size> mSupportedPreviewSizes;
    	private Camera mCamera;
    	private boolean isSupportAutoFocus = false;
    	private Camera.Parameters parameters = null;
    	private Context mContext;
    	//private int mCurrentCameraId = 0;
    	private int screenWidth;
    	private int screenHeight;
    
    	CameraPreview(Context context, SurfaceView sv) {
    		super(context);
    		mContext = context;
    		mSurfaceView = sv;
    		mHolder = mSurfaceView.getHolder();
    		mHolder.addCallback(this);
    		mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    		mHolder.setKeepScreenOn(true);
    		isSupportAutoFocus = context.getPackageManager().hasSystemFeature(
    				PackageManager.FEATURE_CAMERA_AUTOFOCUS);
    		DisplayMetrics dm = new DisplayMetrics();
    		((Activity) mContext).getWindowManager().getDefaultDisplay().getMetrics(dm);
    		screenWidth = dm.widthPixels;
    		screenHeight = dm.heightPixels;
    	}
    
    	public void setCamera(Camera camera) {
    		mCamera = camera;
    		initCamera();
    	}
    
    	public void initCamera() {
    		if (mCamera != null) {
    			Camera.Parameters params = mCamera.getParameters();
    			//mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
    			requestLayout();
    			if (mPreviewSize == null) {
    				mPreviewSize = findBestPreviewResolution();
    			}
    			if (adapterSize == null) {
    				adapterSize = findBestPictureResolution();
    			}
    			if (adapterSize != null) {
    				params.setPictureSize(adapterSize.width, adapterSize.height);
    			}
    			if (mPreviewSize != null) {
    				params.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
    			}
    			params.setPictureFormat(PixelFormat.JPEG);
    			List<String> focusModes = params.getSupportedFocusModes();
    			if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
    				// set the focus mode
    				params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
    				// set Camera parameters
    				mCamera.setParameters(params);
    			}
    			setDispaly(params, mCamera);
    			//setCameraDisplayOrientation((Activity) mContext, mCurrentCameraId, mCamera);
    			mCamera.setParameters(params);
    		}
    	}
    
    	//控制图像的正确显示方向
    	private void setDispaly(Camera.Parameters parameters, Camera camera) {
    		if (Build.VERSION.SDK_INT >= 8) {
    			setDisplayOrientation(camera, 90);
    		} else {
    			parameters.setRotation(90);
    		}
    	}
    
    	//实现的图像的正确显示
    	private void setDisplayOrientation(Camera camera, int i) {
    		Method downPolymorphic;
    		try {
    			downPolymorphic = camera.getClass().getMethod("setDisplayOrientation",
    					new Class[]{int.class});
    			if (downPolymorphic != null) {
    				downPolymorphic.invoke(camera, new Object[]{i});
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    
    	public static void setCameraDisplayOrientation(Activity activity,
    			int cameraId, android.hardware.Camera camera) {
    		android.hardware.Camera.CameraInfo info =
    				new android.hardware.Camera.CameraInfo();
    		android.hardware.Camera.getCameraInfo(cameraId, info);
    		int rotation = activity.getWindowManager().getDefaultDisplay()
    				.getRotation();
    		int degrees = 0;
    		switch (rotation) {
    		case Surface.ROTATION_0:
    			degrees = 0;
    			break;
    		case Surface.ROTATION_90:
    			degrees = 90;
    			break;
    		case Surface.ROTATION_180:
    			degrees = 180;
    			break;
    		case Surface.ROTATION_270:
    			degrees = 270;
    			break;
    		}
    
    		int result;
    		if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
    			result = (info.orientation + degrees) % 360;
    			result = (360 - result) % 360;  // compensate the mirror
    		} else {  // back-facing
    			result = (info.orientation - degrees + 360) % 360;
    		}
    		camera.setDisplayOrientation(result);
    	}
    
    	@Override
    	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    		final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
    		final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
    		setMeasuredDimension(width, height);
    		//        if (mSupportedPreviewSizes != null) {
    		//             mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
    		//        }
    	}
    
    	@Override
    	protected void onLayout(boolean changed, int l, int t, int r, int b) {
    		if (changed && getChildCount() > 0) {
    			final View child = getChildAt(0);
    
    			final int width = r - l;
    			final int height = b - t;
    
    			int previewWidth = width;
    			int previewHeight = height;
    			if (mPreviewSize != null) {
    				previewWidth = mPreviewSize.width;
    				previewHeight = mPreviewSize.height;
    			}
    
    			// Center the child SurfaceView within the parent.
    			if (width * previewHeight > height * previewWidth) {
    				final int scaledChildWidth = previewWidth * height / previewHeight;
    				child.layout((width - scaledChildWidth) / 2, 0,
    						(width + scaledChildWidth) / 2, height);
    			} else {
    				final int scaledChildHeight = previewHeight * width / previewWidth;
    				child.layout(0, (height - scaledChildHeight) / 2,
    						width, (height + scaledChildHeight) / 2);
    			}
    		}
    	}
    
    	public void surfaceCreated(SurfaceHolder holder) {
    		// The Surface has been created, acquire the camera and tell it where
    		// to draw.
    		try {
    			if (mCamera != null) {
    				mCamera.setPreviewDisplay(holder);
    			}
    		} catch (IOException e) {
    			if (null != mCamera) {
    				mCamera.release();
    				mCamera = null;
    
    			}
    			e.printStackTrace();
    		}
    	}
    
    	public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
    		if (holder.getSurface() == null) {
    			return;
    		}
    		if (mCamera != null) {
    			Camera.Parameters parameters = mCamera.getParameters();
    			parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
    			mCamera.setParameters(parameters);
    			try {
    				mCamera.setPreviewDisplay(holder);
    			} catch (IOException e) {
    				e.printStackTrace();
    			}
    			mCamera.startPreview();
    			reAutoFocus();
    		}
    	}
    
    	public void surfaceDestroyed(SurfaceHolder holder) {
    		// Surface will be destroyed when we return, so stop the preview.
    		if (mCamera != null) {
    			mCamera.stopPreview();
    		}
    	}
    
    	/**
    	 * 最小预览界面的分辨率
    	 */
    	private static final int MIN_PREVIEW_PIXELS = 480 * 320;
    	/**
    	 * 最大宽高比差
    	 */
    	private static final double MAX_ASPECT_DISTORTION = 0.15;
    
    	/**
    	 * 找出最适合的预览界面分辨率
    	 *
    	 * @return
    	 */
    	private Camera.Size findBestPreviewResolution() {
    		Camera.Parameters cameraParameters = mCamera.getParameters();
    		Camera.Size defaultPreviewResolution = cameraParameters.getPreviewSize();
    
    		List<Camera.Size> rawSupportedSizes = cameraParameters.getSupportedPreviewSizes();
    		if (rawSupportedSizes == null) {
    			return defaultPreviewResolution;
    		}
    
    		// 按照分辨率从大到小排序
    		List<Camera.Size> supportedPreviewResolutions = new ArrayList<Camera.Size>(rawSupportedSizes);
    		Collections.sort(supportedPreviewResolutions, new Comparator<Size>() {
    			@Override
    			public int compare(Camera.Size a, Camera.Size b) {
    				int aPixels = a.height * a.width;
    				int bPixels = b.height * b.width;
    				if (bPixels < aPixels) {
    					return -1;
    				}
    				if (bPixels > aPixels) {
    					return 1;
    				}
    				return 0;
    			}
    		});
    
    		StringBuilder previewResolutionSb = new StringBuilder();
    		for (Camera.Size supportedPreviewResolution : supportedPreviewResolutions) {
    			previewResolutionSb.append(supportedPreviewResolution.width).append('x').append(supportedPreviewResolution.height)
    			.append(' ');
    		}
    
    
    		// 移除不符合条件的分辨率
    		double screenAspectRatio = (double) screenWidth
    		/ screenHeight;
    		Iterator<Size> it = supportedPreviewResolutions.iterator();
    		while (it.hasNext()) {
    			Camera.Size supportedPreviewResolution = it.next();
    			int width = supportedPreviewResolution.width;
    			int height = supportedPreviewResolution.height;
    
    			// 移除低于下限的分辨率,尽可能取高分辨率
    			if (width * height < MIN_PREVIEW_PIXELS) {
    				it.remove();
    				continue;
    			}
    
    			// 在camera分辨率与屏幕分辨率宽高比不相等的情况下,找出差距最小的一组分辨率
    			// 由于camera的分辨率是width>height,我们设置的portrait模式中,width<height
    			// 因此这里要先交换然preview宽高比后在比较
    			boolean isCandidatePortrait = width > height;
    			int maybeFlippedWidth = isCandidatePortrait ? height : width;
    			int maybeFlippedHeight = isCandidatePortrait ? width : height;
    			double aspectRatio = (double) maybeFlippedWidth / (double) maybeFlippedHeight;
    			double distortion = Math.abs(aspectRatio - screenAspectRatio);
    			if (distortion > MAX_ASPECT_DISTORTION) {
    				it.remove();
    				continue;
    			}
    
    			// 找到与屏幕分辨率完全匹配的预览界面分辨率直接返回
    			if (maybeFlippedWidth == screenWidth
    					&& maybeFlippedHeight == screenHeight) {
    				return supportedPreviewResolution;
    			}
    		}
    
    
    		// 如果没有找到合适的,并且还有候选的像素,则设置其中最大比例的,对于配置比较低的机器不太合适
    		if (!supportedPreviewResolutions.isEmpty()) {
    			Camera.Size largestPreview = supportedPreviewResolutions.get(0);
    			return largestPreview;
    		}
    
    
    		// 没有找到合适的,就返回默认的
    
    		return defaultPreviewResolution;
    	}
    
    
    	private Camera.Size findBestPictureResolution() {
    		Camera.Parameters cameraParameters = mCamera.getParameters();
    		List<Camera.Size> supportedPicResolutions = cameraParameters.getSupportedPictureSizes(); // 至少会返回一个值
    
    		StringBuilder picResolutionSb = new StringBuilder();
    		for (Camera.Size supportedPicResolution : supportedPicResolutions) {
    			picResolutionSb.append(supportedPicResolution.width).append('x')
    			.append(supportedPicResolution.height).append(" ");
    		}
    
    		Camera.Size defaultPictureResolution = cameraParameters.getPictureSize();
    
    		// 排序
    		List<Camera.Size> sortedSupportedPicResolutions = new ArrayList<Camera.Size>(
    				supportedPicResolutions);
    		Collections.sort(sortedSupportedPicResolutions, new Comparator<Camera.Size>() {
    			@Override
    			public int compare(Camera.Size a, Camera.Size b) {
    				int aPixels = a.height * a.width;
    				int bPixels = b.height * b.width;
    				if (bPixels < aPixels) {
    					return -1;
    				}
    				if (bPixels > aPixels) {
    					return 1;
    				}
    				return 0;
    			}
    		});
    
    
    		// 移除不符合条件的分辨率
    		double screenAspectRatio = screenWidth
    		/ (double) screenHeight;
    		Iterator<Camera.Size> it = sortedSupportedPicResolutions.iterator();
    		while (it.hasNext()) {
    			Camera.Size supportedPreviewResolution = it.next();
    			int width = supportedPreviewResolution.width;
    			int height = supportedPreviewResolution.height;
    
    			// 在camera分辨率与屏幕分辨率宽高比不相等的情况下,找出差距最小的一组分辨率
    			// 由于camera的分辨率是width>height,我们设置的portrait模式中,width<height
    			// 因此这里要先交换然后在比较宽高比
    			boolean isCandidatePortrait = width > height;
    			int maybeFlippedWidth = isCandidatePortrait ? height : width;
    			int maybeFlippedHeight = isCandidatePortrait ? width : height;
    			double aspectRatio = (double) maybeFlippedWidth / (double) maybeFlippedHeight;
    			double distortion = Math.abs(aspectRatio - screenAspectRatio);
    			if (distortion > MAX_ASPECT_DISTORTION) {
    				it.remove();
    				continue;
    			}
    		}
    
    		// 如果没有找到合适的,并且还有候选的像素,对于照片,则取其中最大比例的,而不是选择与屏幕分辨率相同的
    		if (!sortedSupportedPicResolutions.isEmpty()) {
    			return sortedSupportedPicResolutions.get(0);
    		}
    
    		// 没有找到合适的,就返回默认的
    		return defaultPictureResolution;
    	}
    
    	private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {
    		final double ASPECT_TOLERANCE = 0.1;
    		double targetRatio = (double) w / h;
    		if (sizes == null)
    			return null;
    
    		Size optimalSize = null;
    		double minDiff = Double.MAX_VALUE;
    
    		int targetHeight = h;
    
    		// Try to find an size match aspect ratio and size
    		for (Size size : sizes) {
    			double ratio = (double) size.width / size.height;
    			if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
    				continue;
    			if (Math.abs(size.height - targetHeight) < minDiff) {
    				optimalSize = size;
    				minDiff = Math.abs(size.height - targetHeight);
    			}
    		}
    
    		// Cannot find the one match the aspect ratio, ignore the requirement
    		if (optimalSize == null) {
    			minDiff = Double.MAX_VALUE;
    			for (Size size : sizes) {
    				if (Math.abs(size.height - targetHeight) < minDiff) {
    					optimalSize = size;
    					minDiff = Math.abs(size.height - targetHeight);
    				}
    			}
    		}
    		return optimalSize;
    	}
    
    
    	public void reAutoFocus() {
    		if (isSupportAutoFocus) {
    			mCamera.autoFocus(new Camera.AutoFocusCallback() {
    				@Override
    				public void onAutoFocus(boolean success, Camera camera) {
    				}
    			});
    		}
    	}
    
    	public List<Size> getResolutionList() {
    		return mCamera.getParameters().getSupportedPreviewSizes();
    	}
    
    	public Camera.Size getResolution() {
    		Camera.Parameters params = mCamera.getParameters();
    		Camera.Size s = params.getPreviewSize();
    		return s;
    	}
    
    	/*public void setCurrentCameraId(int current) {
    		mCurrentCameraId = current;
    	}*/
    
    	//定点对焦的代码
    	public void pointFocus(MotionEvent event) {
    		mCamera.cancelAutoFocus();
    		parameters = mCamera.getParameters();
    		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
    			//showPoint(x, y);
    			focusOnTouch(event);
    		}
    		mCamera.setParameters(parameters);
    		autoFocus();
    	}
    
    	//实现自动对焦
    	public void autoFocus() {
    		new Thread() {
    			@Override
    			public void run() {
    				try {
    					sleep(100);
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    				if (mCamera == null) {
    					return;
    				}
    				mCamera.autoFocus(new Camera.AutoFocusCallback() {
    					@Override
    					public void onAutoFocus(boolean success, Camera camera) {
    						if (success) {
    							initCamera();//实现相机的参数初始化
    						}
    					}
    				});
    			}
    		};
    	}
    
    	@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    	private void showPoint(int x, int y) {
    		if (parameters.getMaxNumMeteringAreas() > 0) {
    			List<Camera.Area> areas = new ArrayList<Camera.Area>();
    			WindowManager wm = (WindowManager) getContext()
    					.getSystemService(Context.WINDOW_SERVICE);
    			//xy变换了
    			int rectY = -x * 2000 / wm.getDefaultDisplay().getWidth() + 1000;
    			int rectX = y * 2000 / wm.getDefaultDisplay().getHeight() - 1000;
    			int left = rectX < -900 ? -1000 : rectX - 100;
    			int top = rectY < -900 ? -1000 : rectY - 100;
    			int right = rectX > 900 ? 1000 : rectX + 100;
    			int bottom = rectY > 900 ? 1000 : rectY + 100;
    			Rect area1 = new Rect(left, top, right, bottom);
    			areas.add(new Camera.Area(area1, 800));
    			parameters.setMeteringAreas(areas);
    		}
    
    		parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
    	}
    
    	@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    	public void focusOnTouch(MotionEvent event) {
    		Rect focusRect = calculateTapArea(event.getRawX(), event.getRawY(), 1f);
    		Rect meteringRect = calculateTapArea(event.getRawX(), event.getRawY(), 1.5f);
    
    		Camera.Parameters parameters = mCamera.getParameters();
    		parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
    
    		if (parameters.getMaxNumFocusAreas() > 0) {
    			List<Camera.Area> focusAreas = new ArrayList<Camera.Area>();
    			focusAreas.add(new Camera.Area(focusRect, 1000));
    
    			parameters.setFocusAreas(focusAreas);
    		}
    
    		if (parameters.getMaxNumMeteringAreas() > 0) {
    			List<Camera.Area> meteringAreas = new ArrayList<Camera.Area>();
    			meteringAreas.add(new Camera.Area(meteringRect, 1000));
    
    			parameters.setMeteringAreas(meteringAreas);
    		}
    		mCamera.setParameters(parameters);
    		mCamera.autoFocus(this);
    	}
    
    	/**
    	 * Convert touch position x:y to {@link Camera.Area} position -1000:-1000 to 1000:1000.
    	 */
    	private Rect calculateTapArea(float x, float y, float coefficient) {
    		float focusAreaSize = 300;
    		int areaSize = Float.valueOf(focusAreaSize * coefficient).intValue();
    
    		int centerX = (int) (x / getResolution().width * 2000 - 1000);
    		int centerY = (int) (y / getResolution().height * 2000 - 1000);
    
    		int left = clamp(centerX - areaSize / 2, -1000, 1000);
    		int right = clamp(left + areaSize, -1000, 1000);
    		int top = clamp(centerY - areaSize / 2, -1000, 1000);
    		int bottom = clamp(top + areaSize, -1000, 1000);
    
    		return new Rect(left, top, right, bottom);
    	}
    
    	private int clamp(int x, int min, int max) {
    		if (x > max) {
    			return max;
    		}
    		if (x < min) {
    			return min;
    		}
    		return x;
    	}
    
    	@Override
    	public void onAutoFocus(boolean success, Camera camera) {
    
    	}
    
    	public void setNull() {
    		adapterSize = null;
    		mPreviewSize = null;
    	}
    
    }
    


    以下是CameraActivity类:

    public class CameraActivity extends Activity implements View.OnTouchListener,OnClickListener {
    
    	public static final String CAMERA_PATH_VALUE1 = "PHOTO_PATH";
    	public static final String CAMERA_PATH_VALUE2 = "PATH";
    	public static final String CAMERA_TYPE = "CAMERA_TYPE";
    	public static final String CAMERA_RETURN_PATH = "return_path";
    
    	private int PHOTO_SIZE_W = 2000;
    	private int PHOTO_SIZE_H = 2000;
    	public static final int CAMERA_TYPE_1 = 1;
    	public static final int CAMERA_TYPE_2 = 2;
    	private final int PROCESS = 1;
    	private CameraPreview preview;
    	private Camera camera;
    	private Context mContext;
    	private View focusIndex;
    	private ImageView flashBtn;
    	private int mCurrentCameraId = 0; // 1是前置 0是后置
    	private SurfaceView mSurfaceView;
    	private CameraGrid mCameraGrid;
    
    	private int type = 1;	//引用的矩形框
    
    	private Button mBtnSearch;
    	private Button mBtnTakePhoto;
    
    	@Override
    	public void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		mContext = this;
    
    		//requestWindowFeature(Window.FEATURE_NO_TITLE);
    		//getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);//全屏
    		//getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);//拍照过程屏幕一直处于高亮
    		setContentView(R.layout.camera_home);
    		type = getIntent().getIntExtra(CAMERA_TYPE, CAMERA_TYPE_2);
    		initView();
    		InitData();
    
    	}
    
    	private void initView() {
    		focusIndex = (View) findViewById(R.id.focus_index);
    		flashBtn = (ImageView) findViewById(R.id.flash_view);
    		mSurfaceView = (SurfaceView) findViewById(R.id.surfaceView);
    		mCameraGrid = (CameraGrid) findViewById(R.id.camera_grid);
    		mBtnSearch = (Button) findViewById(R.id.search);
    		mBtnTakePhoto = (Button) findViewById(R.id.takephoto);
    	}
    
    
    	private void InitData() {
    		preview = new CameraPreview(this, mSurfaceView);
    		preview.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
    				LayoutParams.MATCH_PARENT));
    		((FrameLayout) findViewById(R.id.layout)).addView(preview);
    		preview.setKeepScreenOn(true);
    		mSurfaceView.setOnTouchListener(this);
    		mCameraGrid.setType(type);
    	}
    
    
    
    
    	private Handler handler = new Handler();
    
    	private void takePhoto() {
    		try {
    
    			camera.takePicture(shutterCallback, rawCallback, jpegCallback);
    
    		} catch (Throwable t) {
    			t.printStackTrace();
    			Toast.makeText(getApplication(), "拍照失败,请重试!", Toast.LENGTH_LONG)
    			.show();
    			try {
    				camera.startPreview();
    			} catch (Throwable e) {
    
    			}
    		}
    	}
    
    
    
    	@Override
    	protected void onResume() {
    		super.onResume();
    		int numCams = Camera.getNumberOfCameras();
    		if (numCams > 0) {
    			try {
    				mCurrentCameraId = 0;
    				camera = Camera.open(mCurrentCameraId);
    				camera.startPreview();
    				preview.setCamera(camera);
    				preview.reAutoFocus();
    			} catch (RuntimeException ex) {
    				Toast.makeText(mContext, "未发现相机", Toast.LENGTH_LONG).show();
    			}
    		}
    
    	}
    
    
    
    	@Override
    	protected void onPause() {
    		if (camera != null) {
    			camera.stopPreview();
    			preview.setCamera(null);
    			camera.release();
    			camera = null;
    			preview.setNull();
    		}
    		super.onPause();
    
    	}
    
    
    	private void resetCam() {
    		camera.startPreview();
    		preview.setCamera(camera);
    	}
    
    
    	ShutterCallback shutterCallback = new ShutterCallback() {
    		public void onShutter() {
    		}
    	};
    
    
    	PictureCallback rawCallback = new PictureCallback() {
    		public void onPictureTaken(byte[] data, Camera camera) {
    		}
    	};
    
    
    	PictureCallback jpegCallback = new PictureCallback() {
    		public void onPictureTaken(byte[] data, Camera camera) {
    
    			new SaveImageTask(data).execute();
    			resetCam();
    		}
    	};
    
    
    	@Override
    	public boolean onTouch(View v, MotionEvent event) {
    		try {
    			if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
    				preview.pointFocus(event);
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    
    		RelativeLayout.LayoutParams layout = new RelativeLayout.LayoutParams(
    				focusIndex.getLayoutParams());
    		layout.setMargins((int) event.getX() - 60, (int) event.getY() - 60, 0,0);
    
    		focusIndex.setLayoutParams(layout);
    		focusIndex.setVisibility(View.VISIBLE);
    
    		ScaleAnimation sa = new ScaleAnimation(3f, 1f, 3f, 1f,
    				ScaleAnimation.RELATIVE_TO_SELF, 0.5f,
    				ScaleAnimation.RELATIVE_TO_SELF, 0.5f);
    		sa.setDuration(800);
    		focusIndex.startAnimation(sa);
    		handler.postAtTime(new Runnable() {
    			@Override
    			public void run() {
    				focusIndex.setVisibility(View.INVISIBLE);
    			}
    		}, 800);
    		return false;
    	}
    
    
    	@Override
    	public void onClick(View v) {
    		switch (v.getId()) {
    
    		/*case R.id.camera_back:
    			setResult(0);
    			finish();
    			break;*/
    
    		case R.id.camera_flip_view:
    			switchCamera();
    			break;
    
    		case R.id.flash_view:
    			turnLight(camera);
    			break;
    
    		case R.id.action_button:
    			takePhoto();
    			break;
    
    		case R.id.search:	//处理选中状态
    			mBtnSearch.setSelected(true);
    			mBtnTakePhoto.setSelected(false);
    			break;
    
    		case R.id.takephoto:	//处理选中状态
    			mBtnTakePhoto.setSelected(true);
    			mBtnSearch.setSelected(false);
    			break;
    		}
    	}
    
    	private static String getCameraPath() {
    		Calendar calendar = Calendar.getInstance();
    		StringBuilder sb = new StringBuilder();
    		sb.append("IMG");
    		sb.append(calendar.get(Calendar.YEAR));
    		int month = calendar.get(Calendar.MONTH) + 1; // 0~11
    		sb.append(month < 10 ? "0" + month : month);
    		int day = calendar.get(Calendar.DATE);
    		sb.append(day < 10 ? "0" + day : day);
    		int hour = calendar.get(Calendar.HOUR_OF_DAY);
    		sb.append(hour < 10 ? "0" + hour : hour);
    		int minute = calendar.get(Calendar.MINUTE);
    		sb.append(minute < 10 ? "0" + minute : minute);
    		int second = calendar.get(Calendar.SECOND);
    		sb.append(second < 10 ? "0" + second : second);
    		if (!new File(sb.toString() + ".jpg").exists()) {
    			return sb.toString() + ".jpg";
    		}
    
    		StringBuilder tmpSb = new StringBuilder(sb);
    		int indexStart = sb.length();
    		for (int i = 1; i < Integer.MAX_VALUE; i++) {
    			tmpSb.append('(');
    			tmpSb.append(i);
    			tmpSb.append(')');
    			tmpSb.append(".jpg");
    			if (!new File(tmpSb.toString()).exists()) {
    				break;
    			}
    
    			tmpSb.delete(indexStart, tmpSb.length());
    		}
    
    		return tmpSb.toString();
    	}
    
    
    
    	//处理拍摄的照片
    	private class SaveImageTask extends AsyncTask<Void, Void, String> {
    		private byte[] data;
    
    		SaveImageTask(byte[] data) {
    			this.data = data;
    		}
    
    		@Override
    		protected String doInBackground(Void... params) {
    			// Write to SD Card
    			String path = "";
    			try {
    
    				showProgressDialog("处理中");
    				path = saveToSDCard(data);
    
    			} catch (FileNotFoundException e) {
    				e.printStackTrace();
    			} catch (IOException e) {
    				e.printStackTrace();
    			} finally {
    			}
    			return path;
    		}
    
    
    		@Override
    		protected void onPostExecute(String path) {
    			super.onPostExecute(path);
    
    			if (!TextUtils.isEmpty(path)) {
    
    				Log.d("DemoLog", "path=" + path);
    
    				dismissProgressDialog();
    				Intent intent = new Intent();
    				intent.setClass(CameraActivity.this, PhotoProcessActivity.class);
    				intent.putExtra(CAMERA_PATH_VALUE1, path);
    				startActivityForResult(intent, PROCESS);
    			} else {
    				Toast.makeText(getApplication(), "拍照失败,请稍后重试!",
    						Toast.LENGTH_LONG).show();
    			}
    		}
    	}
    
    	private AlertDialog mAlertDialog;
    
    	private void dismissProgressDialog() {
    		this.runOnUiThread(new Runnable() {
    			@Override
    			public void run() {
    				if (mAlertDialog != null && mAlertDialog.isShowing()
    						&& !CameraActivity.this.isFinishing()) {
    					mAlertDialog.dismiss();
    					mAlertDialog = null;
    				}
    			}
    		});
    	}
    
    	private void showProgressDialog(final String msg) {
    		this.runOnUiThread(new Runnable() {
    			@Override
    			public void run() {
    				if (mAlertDialog == null) {
    					mAlertDialog = new GenericProgressDialog(
    							CameraActivity.this);
    				}
    				mAlertDialog.setMessage(msg);
    				((GenericProgressDialog) mAlertDialog)
    				.setProgressVisiable(true);
    				mAlertDialog.setCancelable(false);
    				mAlertDialog.setOnCancelListener(null);
    				mAlertDialog.show();
    				mAlertDialog.setCanceledOnTouchOutside(false);
    			}
    		});
    	}
    
    
    	/**
    	 * 将拍下来的照片存放在SD卡中
    	 */
    	public String saveToSDCard(byte[] data) throws IOException {
    		Bitmap croppedImage;
    		// 获得图片大小
    		BitmapFactory.Options options = new BitmapFactory.Options();
    		options.inJustDecodeBounds = true;
    		BitmapFactory.decodeByteArray(data, 0, data.length, options);
    		// PHOTO_SIZE = options.outHeight > options.outWidth ? options.outWidth
    		// : options.outHeight;
    		PHOTO_SIZE_W = options.outWidth;
    		PHOTO_SIZE_H = options.outHeight;
    		options.inJustDecodeBounds = false;
    		Rect r = new Rect(0, 0, PHOTO_SIZE_W, PHOTO_SIZE_H);
    		try {
    			croppedImage = decodeRegionCrop(data, r);
    		} catch (Exception e) {
    			return null;
    		}
    		String imagePath = "";
    		try {
    			imagePath = saveToFile(croppedImage);
    		} catch (Exception e) {
    
    		}
    		croppedImage.recycle();
    		return imagePath;
    	}
    
    
    
    	private Bitmap decodeRegionCrop(byte[] data, Rect rect) {
    		InputStream is = null;
    		System.gc();
    		Bitmap croppedImage = null;
    		try {
    			is = new ByteArrayInputStream(data);
    			BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(is,false);
    			try {
    				croppedImage = decoder.decodeRegion(rect,
    						new BitmapFactory.Options());
    			} catch (IllegalArgumentException e) {
    			}
    		} catch (Throwable e) {
    			e.printStackTrace();
    		} finally {
    
    		}
    		Matrix m = new Matrix();
    		m.setRotate(90, PHOTO_SIZE_W / 2, PHOTO_SIZE_H / 2);
    		if (mCurrentCameraId == 1) {
    			m.postScale(1, -1);
    		}
    		Bitmap rotatedImage = Bitmap.createBitmap(croppedImage, 0, 0,
    				PHOTO_SIZE_W, PHOTO_SIZE_H, m, true);
    		if (rotatedImage != croppedImage)
    			croppedImage.recycle();
    		return rotatedImage;
    	}
    
    
    
    	// 保存图片文件
    	public static String saveToFile(Bitmap croppedImage)
    			throws FileNotFoundException, IOException {
    		File sdCard = Environment.getExternalStorageDirectory();
    		File dir = new File(sdCard.getAbsolutePath() + "/DCIM/Camera/");
    		if (!dir.exists()) {
    			dir.mkdirs();
    		}
    		String fileName = getCameraPath();
    		File outFile = new File(dir, fileName);
    		FileOutputStream outputStream = new FileOutputStream(outFile); // 文件输出流
    		croppedImage.compress(Bitmap.CompressFormat.JPEG, 70, outputStream);
    		outputStream.flush();
    		outputStream.close();
    		return outFile.getAbsolutePath();
    	}
    
    
    	/**
    	 * 闪光灯开关 开->关->自动
    	 *
    	 * @param mCamera
    	 */
    	private void turnLight(Camera mCamera) {
    		if (mCamera == null || mCamera.getParameters() == null
    				|| mCamera.getParameters().getSupportedFlashModes() == null) {
    			return;
    		}
    		Camera.Parameters parameters = mCamera.getParameters();
    		String flashMode = mCamera.getParameters().getFlashMode();
    		List<String> supportedModes = mCamera.getParameters()
    				.getSupportedFlashModes();
    		if (Camera.Parameters.FLASH_MODE_OFF.equals(flashMode)
    				&& supportedModes.contains(Camera.Parameters.FLASH_MODE_ON)) {// 关闭状态
    			parameters.setFlashMode(Camera.Parameters.FLASH_MODE_ON);
    			mCamera.setParameters(parameters);
    			flashBtn.setImageResource(R.drawable.camera_flash_on);
    		} else if (Camera.Parameters.FLASH_MODE_ON.equals(flashMode)) {// 开启状态
    			if (supportedModes.contains(Camera.Parameters.FLASH_MODE_AUTO)) {
    				parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
    				flashBtn.setImageResource(R.drawable.camera_flash_auto);
    				mCamera.setParameters(parameters);
    			} else if (supportedModes
    					.contains(Camera.Parameters.FLASH_MODE_OFF)) {
    				parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
    				flashBtn.setImageResource(R.drawable.camera_flash_off);
    				mCamera.setParameters(parameters);
    			}
    		} else if (Camera.Parameters.FLASH_MODE_AUTO.equals(flashMode)
    				&& supportedModes.contains(Camera.Parameters.FLASH_MODE_OFF)) {
    			parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
    			mCamera.setParameters(parameters);
    			flashBtn.setImageResource(R.drawable.camera_flash_off);
    		}
    	}
    
    
    	// 切换前后置摄像头
    	private void switchCamera() {
    		mCurrentCameraId = (mCurrentCameraId + 1) % Camera.getNumberOfCameras();
    		if (camera != null) {
    			camera.stopPreview();
    			preview.setCamera(null);
    			camera.setPreviewCallback(null);
    			camera.release();
    			camera = null;
    		}
    		try {
    			camera = Camera.open(mCurrentCameraId);
    			camera.setPreviewDisplay(mSurfaceView.getHolder());
    			preview.setCamera(camera);
    			camera.startPreview();
    		} catch (Exception e) {
    			Toast.makeText(mContext, "未发现相机", Toast.LENGTH_LONG).show();
    		}
    
    	}
    
    	@Override
    	public boolean onKeyDown(int keyCode, KeyEvent event) {
    		if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
    			setResult(0);
    			finish();
    			return true;
    		}
    		return super.onKeyDown(keyCode, event);
    	}
    
    
    	@Override
    	public void onActivityResult(int requestCode, int resultCode, Intent data) {
    		if (requestCode == PROCESS) {
    			if (resultCode == RESULT_OK) {
    				Intent intent = new Intent();
    				if (data != null) {
    					intent.putExtra(CAMERA_RETURN_PATH,
    							data.getStringExtra(CAMERA_PATH_VALUE2));
    				}
    				setResult(RESULT_OK, intent);
    				finish();
    			} else {
    				if (data != null) {
    					File dir = new File(data.getStringExtra(CAMERA_PATH_VALUE2));
    					if (dir != null) {
    						dir.delete();
    					}
    				}
    			}
    		}
    	}
    }
    


    总结

    1、网上有些示例代码,担心相机初始化及开启时间较长,将初始化及启动工作单独放在子线程中,偶尔出现黑屏的情况,但也不是经常出现。

    导致原因:由于单独开辟了线程去初始化启动相机,导致相机的初始化和开启工作已完成,而找不到画布控件。若出现此情况,可调试或者将线程睡眠500毫秒。


    2、按下home键后,再次进入时,为毛黑屏了,如何破?

    导致原因:在onCreate中find了SurfaceView,按下Home后程序再次进入时,找不到预览的画布了,可将find的工作放入onResume中,再就是别忘了在onPause中做如下操作:

    @Override
    	protected void onPause() {
    		if (camera != null) {
    			camera.stopPreview();
    			preview.setCamera(null);
    			camera.release();
    			camera = null;
    			preview.setNull();
    		}
    		super.onPause();
    
    	}

    本项目源码(Eclipse版):http://download.csdn.net/download/gao_chun/9084853

    注:测试机-------> 小米2A、红米、华为P8、华为荣耀3C,魅蓝note2


    附:有些小伙伴经常问手机Gif动画如何制作的,在此也分享下:

    动画制作小软件GifMaker:http://download.csdn.net/detail/gao_chun/9077023



    【转载注明gao_chun的Blog:http://blog.csdn.net/gao_chun/article/details/48246871】



    展开全文
  • Android相机Camera基础

    千次阅读 2016-12-30 15:35:17
    本章节主要讲述的Android相机Camera的相关知识点,主要包含三个方面,Android启动系统相机生成缩略图、Android启动系统相机生成原图以及Android自定义相机等。二、Android启动系统相机 核心代码:package ...
  • Android 相机 III-相机功能, 信息来自官网;Android支持很多种相机功能, 我们可以在自己的APP中控制它, 比如图片格式, 闪光模式, 焦点设置, 还有很多别的
  • Android相机开发和遇到的坑

    万次阅读 2016-11-26 16:55:11
    Android相机开发那些坑Android开发实践:掌握Camera的预览方向和拍照方向Android 开发之解决相机预览上下颠倒问题【腾讯优测干货分享】Android 相机预览方向及其适配探索关于Android Camera几点须知在Android相机...
  • Android相机的角度问题

    2018-03-06 10:20:16
    本文以Android相机预览方向为例,探索在Android机型适配上的一些思路。1. android相机简介由于Android系统的开放策略,Android手机呈现碎片化的趋势,兼容性问题一直是Android App 开发者头疼的难题。本文以Android...
  • Android相机的使用

    千次阅读 2017-06-03 15:04:24
    android相机的编程,主要介绍如何使用Camera1和Camera2接口进行Camera的编程
  • Camera2 Android相机Demo

    千次阅读 2017-12-03 22:09:21
    基于Camera2 API的Android相机
  • android 相机编程

    2015-05-12 10:24:47
    android相机编程中经常会遇到问题,比如connect camera failed, setparameters failedd等, 这里讲讲setParameters failed 这个原因是设置参数失败,因为有些手机的摄像头不支持这些参数,比如你所设定的预览大小...
  • android相机自定义圆形框拍照,保留圆形照片。
  • 自动对焦的android相机代码

    热门讨论 2012-03-11 16:33:27
    自动对焦的android相机代码,解决了横竖屏90°的问题,对焦也比较好
  • Android相机开发那些坑

    千次阅读 2016-10-25 11:13:08
    最近我负责开发了一个跟Android相机有关的需求,新功能允许用户使用手机摄像头,快速拍摄特定尺寸(1:1或3:4)的照片,并支持在拍摄出的照片上做贴纸相关的操作。由于之前没有接触过Android相机开发,所以在整个...
  • 完美实现Android相机的所有功能,包括拍照,录像,人脸识别。
  • Android相机之人脸识别

    千次阅读 2017-01-05 15:32:20
    Android相机 人脸识别 第⼀章 Camera基础⼊入⻔门 第⼆章 基于Google自带算法实时检测人脸  摘要 有人玩过三星Galaxy Camera吗?一款采用Android的智能相机,不过似乎没人知 道Android最早是为...
  • 深入理解Android相机体系结构之十一

    千次阅读 2020-07-05 13:50:35
    Android相机发展至今,通过开发者对框架的不断优化,算法人员对图像处理算法的不断提升,硬件工程师对硬件性能地不断调教,换来了在某些领域完全可以媲美专业相机的成像效果,这些成绩是有目共睹的,但是我们不能...
  • Unity调用Android 相机和相册

    万次阅读 2016-10-26 14:37:47
    Unity调用Android 相机和相册
  • 非商业性使用-禁止演绎 4.0 国际》协议 https://blog.csdn.net/bluewindtalker/article/details/54563910相机开发现在有2个类,分别为android.hardware.camera2和android.hard...
  • Android 相机Camera API 使用

    千次阅读 2016-11-01 09:41:00
    Android 相机Camera API 使用 保证预览和存储的都是正方向 Camera2 API
  • Android相机实时自动对焦的完美实现

    万次阅读 多人点赞 2016-05-12 11:29:56
    Android相机实时自动对焦的完美实现 由于android碎片化严重,而且各大厂商极有可能去修改相关API的实现,其中遇到了不少坑,包括实时相机高斯模糊,自动对焦的兼容问题,以及一系列性能问题。换过很多搜索引擎,访问...
  • Android 相机 II-实现自己的相机APP

    千次阅读 2016-01-02 16:54:59
    Android 相机 II-实现自己的相机APP; 信息来自官网. 有些开发者可能会需要一个自定义的相机用户接口, 以实现自己独特样式的相机和特殊的功能. 创建一个自定义相机activity比调用系统相机需要更多的代码, 但是它可以...
  • Android相机基础基于camera2API 前言 最近,在使用Android做一个照相机的开发。因为不能使用系统提供的相机应用,所以只能自己写一个。Android以前提供相机的api叫camera,不过在level 21被...
  • 深入理解Android相机体系结构之十

    千次阅读 2020-07-05 13:48:28
    Android 相机体系庞大且复杂,在我刚开始接触到该框架的时候,如盲人摸象一般,一点一点地在代码的世界中探索,在很长的一段时间内,都只能局限于某一个特定的区域,而且在解决问题的过程中,虽然通过对代码的深入...
  • H5 调用android本地相机进行拍照,支持android 5.0, 6.0等操作系统,均已适配;
  • Android 相机拍摄界面有波纹原因

    千次阅读 2019-03-19 20:04:06
    Android 相机拍摄界面有波纹原因相机拍摄界面有波纹的三种原因手机预览刷新频率与所拍摄物体刷新帧率不同步莫尔条纹影响摩尔纹现象 相机拍摄界面有波纹的三种原因 相机在拍摄画面时往往在预览界面出现波纹现象,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 12,150
精华内容 4,860
关键字:

android相机