zoukankan      html  css  js  c++  java
  • Android--使用Camera拍照

    前言

      在开发Android应用的时候,如果需要调用摄像头获取拍摄的照片,除了通过Intent调用系统现有相机应用拍摄照片之外,还可以通过直接调用Camera硬件去去获取摄像头拍摄的照片。本篇博客将讲解如何在Android应用中通过Camera拍摄照片,这个对开发相机类应用尤为重要,同样最后也将以一个简单的Demo演示。

      本篇博客的主要内容:

    1. Camera
    2. 验证设备是否配备摄像头硬件
    3. Camera捕获画面的预览
    4. 使用Camera拍照
    5. 使用Camera拍照的Demo
    6. Camera的回调监听
    7. Camera的参数设置

    Camera

      Camera是Android摄像头硬件的相机类,位于硬件包"android.hardware.Camera"下。它主要用于摄像头捕获图片、启动/停止预览图片、拍照、获取视频帧等,它是设备本地的服务,负责管理设备上的摄像头硬件。

      Camera既然用于管理设备上的摄像头硬件,那么它也为开发人员提供了相应的方法,并且这些方法大部分都是native的,用C++在底层实现,下面简单介绍一下Camera的一些方法:

    • static Camera open():打开Camera,返回一个Camera实例。
    • static Camera open(int cameraId):根据cameraId打开一个Camera,返回一个Camera实例。
    • final void release():释放掉Camera的资源。
    • static int getNumberOfCameras():获取当前设备支持的Camera硬件个数。
    • Camera.Parameters getParameters():获取Camera的各项参数设置类。
    • void setParameters(Camera.Parameters params):通过params把Camera的各项参数写入到Camera中。
    • final void setDisplayOrientation(int degrees):摄像预览的旋转度。
    • final void setPreviewDisplay(SurfaceHolder holder):设置Camera预览的SurfaceHolder。
    • final void starPreview():开始Camera的预览。
    • final void stopPreview():停止Camera的预览
    • final void autoFocus(Camera.AutoFocusCallback cb):自动对焦。
    • final takePicture(Camera.ShutterCallback shutter,Camera.PictureCallback raw,Camera.PictureCallback jpeg):拍照。
    • final void lock():锁定Camera硬件,使其他应用无法访问。
    • final void unlock():解锁Camera硬件,使其他应用可以访问。

      上面已经介绍了Camera的常用方法,下面根据这些方法详细讲解Android下使用Camera开发拍照应用最基本的过程:

    1. 使用open()方法获取一个Camera对象,鉴于Android设备可能配置了多个摄像头,open()方法可以通过摄像头Id开启指定的摄像头。
    2. 为Camera对象设置预览类,它是一个SurfaceHolder对象,通过setPreviewDisplay(SurfaceHolder)方法设置。
    3. 调用startPreview()方法开始Camera对象的预览。
    4. 调用takePicture()方法进行拍照,其中可以通过Camera.PictureCallback()回调获得拍摄的Image数据。
    5. 当拍摄完成后,需要调用stopPreview()方法停止预览,并使用release()释放Camera占用的资源。

      以上介绍的步骤都是最基本的过程,是必不可少的。Camera没有提供公开的构造函数,只能通过open()方法获取,并且必须设置一个预览类SurfaceHolder,如果不设置的话,将无法使用Camera。在使用完成Camera之后,必须使用release()释放Camera资源。

    验证设备是否配备摄像头硬件

      因为Android的开源,所以其支持的设备多而杂,在使用Camera的时候,最好验证一下当前运行的设备上是否配备了摄像头硬件。这里需要用到一个PackageManager,它用于检测应用安装设备的各种信息,可以通过Context.getPackageManager()方法获取。可以通过PackageManager.hasSystemFeature(String name)方法检测设备是否支持摄像头操作,它传递的是一个String类型的参数,被PackageManager定义为了常量,返回一个Boolean数据,验证是否检测通过,对于Camera而言,可以检测如下信息:

    • FEATURE_CAMERA:设备是否有摄像头。
    • FEATURE_CAMERA_ANY:设备至少有一个摄像头。
    • FEATURE_CAMERA_AUTOFOCUS:设备支持的摄像头是否支持自动对焦
    • FEATURE_CAMERA_FLASH:设备是否配备闪光灯。
    • FEATURE_CAMERA_FRONT:设备是否有一个前置摄像头。

      一般而言,检测FEATURE_CAMERA即可:

     1     /** 检测设备是否存在Camera硬件 */
     2     private boolean checkCameraHardware(Context context) {
     3         if (context.getPackageManager().hasSystemFeature(
     4                 PackageManager.FEATURE_CAMERA)) {
     5             // 存在
     6             return true;
     7         } else {
     8             // 不存在
     9             return false;
    10         }
    11     }

    Camera捕获画面的预览

      Camera的预览,需要放到一个SurfaceView中,出于安全的考虑,在设计Android的时候就规定,如果需要调用Camera,必须为其制定显示在屏幕上的SurfaceView预览,否则将无法使用Camera。关于SurfaceView和SurfaceHolder的内容,在之前的博客中已经详细讲解,这里不再赘述,不清楚的朋友可以看看之前的博客:Android--SurfaceView

      下面我们直接使用一个类去继承SurfaceView当做Camera的预览类:

     1 package cn.bgxt.camerapicturedemo;
     2 
     3 import java.io.IOException;
     4 import android.content.Context;
     5 import android.hardware.Camera;
     6 import android.util.Log;
     7 import android.view.SurfaceHolder;
     8 import android.view.SurfaceView;
     9 
    10 /**
    11  * 定义一个预览类
    12  */
    13 public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
    14     private static final String TAG = "main";
    15     private SurfaceHolder mHolder;
    16     private Camera mCamera;
    17 
    18     public CameraPreview(Context context, Camera camera) {
    19         super(context);
    20         mCamera = camera;
    21 
    22         // 通过SurfaceView获得SurfaceHolder
    23         mHolder = getHolder();
    24         // 为SurfaceHolder指定回调
    25         mHolder.addCallback(this);
    26         // 设置Surface不维护自己的缓冲区,而是等待屏幕的渲染引擎将内容推送到界面 在Android3.0之后弃用
    27         mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    28     }
    29 
    30     public void surfaceCreated(SurfaceHolder holder) {
    31         // 当Surface被创建之后,开始Camera的预览
    32         try {
    33             mCamera.setPreviewDisplay(holder);
    34             mCamera.startPreview();
    35         } catch (IOException e) {
    36             Log.d(TAG, "预览失败");
    37         }
    38     }
    39 
    40     public void surfaceDestroyed(SurfaceHolder holder) {
    41         
    42     }
    43 
    44     public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
    45         // Surface发生改变的时候将被调用,第一次显示到界面的时候也会被调用
    46         if (mHolder.getSurface() == null){
    47           // 如果Surface为空,不继续操作
    48           return;
    49         }
    50 
    51         // 停止Camera的预览
    52         try {
    53             mCamera.stopPreview();
    54         } catch (Exception e){
    55             Log.d(TAG, "当Surface改变后,停止预览出错");
    56         }
    57 
    58         // 在预览前可以指定Camera的各项参数
    59 
    60         // 重新开始预览
    61         try {
    62             mCamera.setPreviewDisplay(mHolder);
    63             mCamera.startPreview();
    64 
    65         } catch (Exception e){
    66             Log.d(TAG, "预览Camera出错");
    67         }
    68     }
    69 }

    使用Camera拍照

      当指定了Camera的预览类,并开始预览之后,就可以通过takePicture()方法进行拍照了,下面是它的完整签名:

         public final void taskPicture(Camera.ShuffterCallback shutter,Camera.PictureCallback raw,Camera.PictureCallback postview,Camera.PictureCallback jpeg)

      它将以异步的方式从Camera中获取图像,具有多个回调类作为参数,并且都可以为null,下面分别介绍这些参数的意义:

    • shutter:在按下快门的时候回调,这里可以播放一段声音。
    • raw:从Camera获取到未经处理的图像。
    • postview:从Camera获取一个快速预览的图片,不是所有设备都支持。
    • jpeg:从Camera获取到一个经过压缩的jpeg图片。

       虽然raw、postview、jpeg都是Camera.PictureCallback回调,但是一般我们只需要获取jpeg,其他传null即可,Camera.PictureCallback里需要实现一个方法onPictureTaken(byte[] data,Camera camera),data及为图像数据。值得注意的是,一般taskPicture()方法拍照完成之后,SurfaceView都会停留在拍照的瞬间,需要重新调用startPreview()才会继续预览。

      如果直接使用taskPicture()进行拍照的话,Camera是不会进行自动对焦的,这里需要使用Camera.autoFocus()方法进行对焦,它传递一个Camera.AutoFocusCallback参数,用于自动对焦完成后回调,一般会在它对焦完成在进行taskPicture()拍照。

    使用Camera拍照的Demo

       上面已经介绍了使用Camera的基本步骤以及涉及的内容,这里通过一个简单的Demo演示一下,使用上面附上代码的预览类进行Camera预览。代码中注释比较完整,这里不再赘述。

      布局代码:activity_main.xml

     1 <?xml version="1.0" encoding="utf-8"?>
     2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     3     android:orientation="horizontal"
     4     android:layout_width="fill_parent"
     5     android:layout_height="fill_parent"
     6     >
     7   <FrameLayout
     8     android:id="@+id/camera_preview"
     9     android:layout_width="fill_parent"
    10     android:layout_height="fill_parent"
    11     android:layout_weight="1"
    12     />
    13 
    14   <Button
    15     android:id="@+id/button_capture"
    16     android:text="拍照"
    17     android:layout_width="wrap_content"
    18     android:layout_height="wrap_content"
    19     android:layout_gravity="center"
    20     />
    21 </LinearLayout>
    View Code

      实现代码:MainActivity.java

      1 package cn.bgxt.camerapicturedemo;
      2 
      3 import java.io.File;
      4 import java.io.FileOutputStream;
      5 
      6 import android.os.Bundle;
      7 import android.util.Log;
      8 import android.view.View;
      9 import android.widget.Button;
     10 import android.widget.FrameLayout;
     11 import android.app.Activity;
     12 import android.content.Context;
     13 import android.content.pm.PackageManager;
     14 import android.hardware.Camera;
     15 import android.hardware.Camera.AutoFocusCallback;
     16 import android.hardware.Camera.PictureCallback;
     17 
     18 public class MainActivity extends Activity {
     19     protected static final String TAG = "main";
     20     private Camera mCamera;
     21     private CameraPreview mPreview;
     22 
     23     @Override
     24     protected void onCreate(Bundle savedInstanceState) {
     25         super.onCreate(savedInstanceState);
     26         setContentView(R.layout.activity_main);
     27 
     28         mCamera = getCameraInstance();
     29 
     30         // 创建预览类,并与Camera关联,最后添加到界面布局中
     31         mPreview = new CameraPreview(this, mCamera);
     32         FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
     33         preview.addView(mPreview);
     34 
     35 
     36         Button captureButton = (Button) findViewById(R.id.button_capture);
     37         captureButton.setOnClickListener(new View.OnClickListener() {
     38             @Override
     39             public void onClick(View v) {
     40                 // 在捕获图片前进行自动对焦
     41                 mCamera.autoFocus(new AutoFocusCallback() {
     42                     
     43                     @Override
     44                     public void onAutoFocus(boolean success, Camera camera) {
     45                         // 从Camera捕获图片
     46                         mCamera.takePicture(null, null, mPicture);
     47                     }
     48                 });                
     49             }
     50         });
     51     }
     52 
     53     /** 检测设备是否存在Camera硬件 */
     54     private boolean checkCameraHardware(Context context) {
     55         if (context.getPackageManager().hasSystemFeature(
     56                 PackageManager.FEATURE_CAMERA)) {
     57             // 存在
     58             return true;
     59         } else {
     60             // 不存在
     61             return false;
     62         }
     63     }
     64 
     65     /** 打开一个Camera */
     66     public static Camera getCameraInstance() {
     67         Camera c = null;
     68         try {
     69             c = Camera.open(); 
     70         } catch (Exception e) {
     71             Log.d(TAG, "打开Camera失败失败");
     72         }
     73         return c; 
     74     }
     75 
     76     private PictureCallback mPicture = new PictureCallback() {
     77 
     78         @Override
     79         public void onPictureTaken(byte[] data, Camera camera) {
     80             // 获取Jpeg图片,并保存在sd卡上
     81             File pictureFile = new File("/sdcard/" + System.currentTimeMillis()
     82                     + ".jpg");
     83             try {
     84                 FileOutputStream fos = new FileOutputStream(pictureFile);
     85                 fos.write(data);
     86                 fos.close();
     87             } catch (Exception e) {
     88                 Log.d(TAG, "保存图片失败");
     89             }
     90         }
     91     };
     92 
     93     @Override
     94     protected void onDestroy() {
     95         // 回收Camera资源
     96         if(mCamera!=null){
     97             mCamera.stopPreview();
     98             mCamera.release();
     99             mCamera=null;
    100         }
    101         super.onDestroy();
    102     }
    103     
    104 }

      效果展示:

    Camera的回调监听

      上面介绍Camera拍照的时候介绍了两个回调,Camera还提供了一些其他的回调的事件监听方法,这里简单介绍几个常用的:

    • final void setErrorCallback(Camera.ErrorCallback cb):Camera发送错误的时候回调,可以在其中进行错误的后续处理。
    • final void setPreviedCallback(Camera.PreviewCallback cb):Camera预览界面发生变化的时候回调,可以在其中获取到Camera捕获到的帧图像。

    Camera的参数设置

      Camera作为一个摄像头硬件的调用类,还为我们提供了详细的设置摄像头各项参数的方法,比如闪光灯的模式、自动对焦的模式等。步骤是使用Camera.getParameters()方法获取到Camera.Parameters对象,在其中设置好摄像头的各项参数后,再通过Camera.setParameters()方法写入到Camera对象中即可。这个设置必须在使用startPreview()之前完成。关于Camera参数的设置,比较细致,不在本篇博客的内容之中,以后有机会再详细介绍。

       源码下载

      

      

  • 相关阅读:
    Scrapy snippets
    HttpClient4的cookie rejected问题,以及如何消除该warning输出
    简单的python smtp发邮件代码
    FTP服务器:Proftpd
    “当HTML5来敲门”专题沙龙杂记
    Titanium Mobile 1.6版本发布
    给同学们分享一些面试经验
    用CSS3实现动画进度条
    3天内构建Facebook Web应用的经验之谈
    仅用CSS创建立体旋转幻灯片
  • 原文地址:https://www.cnblogs.com/plokmju/p/android_Camera.html
Copyright © 2011-2022 走看看