根据官方教程,翻译而来。

创建一个Camera App:

& 一般步骤

  • 检查并访问Camera —— 写代码检查是否存在cameras并请求使用
  • 创建一个Preview类 —— 创建一个preview类,继承自SurfaceView 并实现SurfaceHolder 接口。这个类是用来预览在拍照时的影像
  • 创建一个Preview布局 —— 接下去创建一个跟Preview类对应的布局文件,里面可以放一些你想要的交互控件
  • 为Capture(拍照)设置监听(Listener) —— 为你的交互控件设置监听,比如按下一个Button
  • 拍照并保存文件 —— 为拍照或录像写代码,并保存到输出流(output)
  • 释放Camera —— 在使用完后,必须要合理地释放,以供其他应用使用。

注意:要及时地释放Camera资源,通过调用Camera.release()来实现。否则其他应用,包括你自己的应用,如果想要使用Camera,都会被关闭。

*检查(detecting)并访问(access)Camera:

  如果应用没有在Manifest文件里声明要使用Camera硬件,那就应该在runtime时检查Camera是否可用。通过PackageManager.hasSystemFeature()检查。整体代码如下:

/** Check if this device has a camera */private boolean checkCameraHardware(Context context) {    if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){        // this device has a camera        return true;    } else {        // no camera on this device        return false;    }}

可能会有多个摄像头,在API9之后,可以通过Camera.getNumberOfCameras()得到有几个可用的摄像头。

* 访问Cameras:

  确定有设备之后,要通过得到一个Camera实例来访问它。(除非使用intent 来访问Camera),通过Camera.open()方法,可以得到主摄像头。代码如下:

/** A safe way to get an instance of the Camera object. */public static Camera getCameraInstance(){    Camera c = null;    try {        c = Camera.open(); // attempt to get a Camera instance    }    catch (Exception e){        // Camera is not available (in use or does not exist)    }    return c; // returns null if camera is unavailable}

注意:要用try/catch Camera.open()这个方法,防止其他应用在使用Camera,而导致本应用被系统关闭。在API Level 9以上,可以通过Camera.open(int)来打开特定的摄像头。

* 检查Camera特征

  可以通过Camera.getParameters()方法来得到更多的关于它的功能。在API Level 9及以上版本中,使用Camera.getCameraInfo()来确定这个摄像头是前面的还是后面的,以及这个图像的朝向(orientation)。

* 创建一个预览类(preview class)

  下面的代码演示了如何创建一个基本的可以包含在一个View 布局里的预览图,这个类实现了SurfaceHolder.Callback接口,来捕获创建/删除这个view的回调事件,这些事件是被安排Camera预览所需要的。

/** A basic Camera preview class */public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {    private SurfaceHolder mHolder;    private Camera mCamera;    public CameraPreview(Context context, Camera camera) {        super(context);        mCamera = camera;        // 把这个监听添加进去,这样可以监听到创建和销毁的事件了
mHolder = getHolder(); mHolder.addCallback(this); // deprecated setting, but required on Android versions prior to 3.0 mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } public void surfaceCreated(SurfaceHolder holder) { // The Surface has been created, now tell the camera where to draw the preview. try { mCamera.setPreviewDisplay(holder); mCamera.startPreview(); } catch (IOException e) { Log.d(TAG, "Error setting camera preview: " + e.getMessage()); } } public void surfaceDestroyed(SurfaceHolder holder) { // empty. Take care of releasing the Camera preview in your activity. } public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { // If your preview can change or rotate, take care of those events here. // Make sure to stop the preview before resizing or reformatting it. if (mHolder.getSurface() == null){ // preview surface does not exist return; } // stop preview before making changes try { mCamera.stopPreview(); } catch (Exception e){ // ignore: tried to stop a non-existent preview } // set preview size and make any resize, rotate or // reformatting changes here // start preview with new settings try { mCamera.setPreviewDisplay(mHolder); mCamera.startPreview(); } catch (Exception e){ Log.d(TAG, "Error starting camera preview: " + e.getMessage()); } }}

  如果自己想指定大小,在surfaceChanged()方法里可以写。设置之前,必须使用从getSupportedPreviewSizes()得到的值,不要在setPreviewSize()里使用合意值。注:supportedPreviewSize()是在camera.getParameters()后再得到的。

* 把预览图放在一个布局里

  一个Preview类,要放在一个布局里,这个布局还要包括其他用来控制拍照等行为的交互界面。这部分展示如何为预览图构建一个布局以及Activity。在这个例子里,FrameLayout元素用来包含预览图。使用这种布局,可以把多余的信息或控制覆盖到活动的Camera预览图上。

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="horizontal"    android:layout_width="fill_parent"    android:layout_height="fill_parent"    >  <FrameLayout    android:id="@+id/camera_preview"    android:layout_width="fill_parent"    android:layout_height="fill_parent"    android:layout_weight="1"    />  <Button    android:id="@+id/button_capture"    android:text="Capture"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:layout_gravity="center"    /></LinearLayout>

  在多数的设备上,默认的朝向是横向的。这个例子的布局指定一个水平的布局,下面的代码固定应用的朝向是横向的。在Manifest里如下指定,就可以简单地让应用保持横向。

<activity android:name=".CameraActivity"          android:label="@string/app_name"          android:screenOrientation="landscape">          <!-- configure this activity to use landscape orientation -->          <intent-filter>        <action android:name="android.intent.action.MAIN" />        <category android:name="android.intent.category.LAUNCHER" />    </intent-filter></activity>

  note:Preview没有必要一定是Landscape(横向)模式。从API Level 8开始,可以使用setDiaplayOrientation()方法来设置预览图的旋转。如果要改变朝向,在surfaceChanged()方法里,先用Camera.stopPreview()方法stop这个Preview,改变朝向,然后再用Camera.startPreview()来开始Preview。

  在你的CameraActivity中,把Preview类加入到这个Framelayout中。你的CameraActivity必须要保证在停止或关闭时释放这个camera。下面代码说明一切。

public class CameraActivity extends Activity {    private Camera mCamera;    private CameraPreview mPreview;    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);        // Create an instance of Camera        mCamera = getCameraInstance();        // Create our Preview view and set it as the content of our activity.        mPreview = new CameraPreview(this, mCamera);        FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);        preview.addView(mPreview);    }}

* 拍照

  一旦设置好Preview,就可以使用它去拍照了。在应用代码里,必须要为你的交互界面设置拍照的响应。

  为了得到图,要使用Camera.takePicture()方法。这个方法带了三个参数,用来接收来自Camera的数据。为了接收JPEG格式的数据,必须要实现Camera.PictureCallback接口,来接收图像数据,并写入到一个文件。下例展示了一个基本的实现,用来保存来自Camera的图片。

private PictureCallback mPicture = new PictureCallback() {    @Override    public void onPictureTaken(byte[] data, Camera camera) {        File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);        if (pictureFile == null){            Log.d(TAG, "Error creating media file, check storage permissions: " +                e.getMessage());            return;        }        try {            FileOutputStream fos = new FileOutputStream(pictureFile);            fos.write(data);            fos.close();        } catch (FileNotFoundException e) {            Log.d(TAG, "File not found: " + e.getMessage());        } catch (IOException e) {            Log.d(TAG, "Error accessing file: " + e.getMessage());        }    }};

“getOutputMediaFile(MEDIA_TYPE_IMAGE)”这个方法及这个常量会在之后“保存文件”一节中说到

  调用Camera.takePicture()方法来触发拍照事件。

// Add a listener to the Capture buttonButton captureButton = (Button) findViewById(id.button_capture);captureButton.setOnClickListener(    new View.OnClickListener() {        @Override        public void onClick(View v) {            // get an image from the camera            mCamera.takePicture(null, null, mPicture);        }    });

* 释放Camera

  当停止使用Camera时要及时把它释放掉。在Activity.onPause()方法中也要释放。通过Camera.release()方法来释放。代码如下:

public class CameraActivity extends Activity {    private Camera mCamera;    private SurfaceView mPreview;    private MediaRecorder mMediaRecorder;    ...    @Override    protected void onPause() {        super.onPause();        releaseMediaRecorder();       // if you are using MediaRecorder, release it first        releaseCamera();              // release the camera immediately on pause event    }    private void releaseMediaRecorder(){        if (mMediaRecorder != null) {            mMediaRecorder.reset();   // clear recorder configuration            mMediaRecorder.release(); // release the recorder object            mMediaRecorder = null;            mCamera.lock();           // lock camera for later use        }    }    private void releaseCamera(){        if (mCamera != null){            mCamera.release();        // release the camera for other applications            mCamera = null;        }    }}

* 保存文件

  Media文件应该被保存到设备的外部存储目录(SD Card),以此来节约系统空间。有很多可以存放文件的地方,但作为开发者,有两个标准的目录可以使用:

  • Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)这个方法返回一个标准的,分享的,并被推荐的目录,用来存放图片和Video。如果被用户卸载了,文件也会存在。为了防止与用户已存在的文件冲突,你应该再创建一个子目录用来存放自己应用的图片。如下面的例子。这个方法在API Level 8以上可以使用,更早的设备,可以查看其他方法。
  • Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES),这个方法返回一个标准的用来存放你的应用的图片和Video的地方。如果应用被卸载,这里的文件也会被卸载。其他应用也可以操作这里的文件。

  如下代码展示了如何创建一个File或者一个Uri,用来保存文件。适用于通过Intent或者自己构建的应用。

public static final int MEDIA_TYPE_IMAGE = 1;public static final int MEDIA_TYPE_VIDEO = 2;/** Create a file Uri for saving an image or video */private static Uri getOutputMediaFileUri(int type){      return Uri.fromFile(getOutputMediaFile(type));}/** Create a File for saving an image or video */private static File getOutputMediaFile(int type){    // To be safe, you should check that the SDCard is mounted    // using Environment.getExternalStorageState() before doing this.    File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(              Environment.DIRECTORY_PICTURES), "MyCameraApp");    // This location works best if you want the created images to be shared    // between applications and persist after your app has been uninstalled.    // Create the storage directory if it does not exist    if (! mediaStorageDir.exists()){        if (! mediaStorageDir.mkdirs()){            Log.d("MyCameraApp", "failed to create directory");            return null;        }    }    // Create a media file name    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());    File mediaFile;    if (type == MEDIA_TYPE_IMAGE){        mediaFile = new File(mediaStorageDir.getPath() + File.separator +        "IMG_"+ timeStamp + ".jpg");    } else if(type == MEDIA_TYPE_VIDEO) {        mediaFile = new File(mediaStorageDir.getPath() + File.separator +        "VID_"+ timeStamp + ".mp4");    } else {        return null;    }    return mediaFile;}

* Camera Features

  可以设置很多的特性,比如图片格式,闪光模式,焦点,还有其他。本节列出几个常用的特性,简单介绍如何使用它们。很多的特性可以通过访问Camera.Parameters对象来得到。但还是有一些重要的特性需要更多的设置。概括为以下几个部分:

  • 计量和焦点区域
  • 面部识别
  • 定时摄影

  关于如何使用这些由Camera.Parameters对象控制的特性,可以查看"使用Camera特性"一节。从API Level1到14,有很多的特性,便并不是所有的都可以被设备使用,使用前要先检查一下。

  & 检测特性是否可用

  要明确使用哪些特性,以及是哪个版本的,之后可以在代码里检查设备硬件是否支持这个特性,如果不行的话,要合理地处理。

  可以通过得到一个Camera Parameters对象还检查特性是否可用,以及相应的方法。下例演示如何得到一个Camera.Parameters对象,并检查是否支持自动对焦功能:

// get Camera parametersCamera.Parameters params = mCamera.getParameters();List<String> focusModes = params.getSupportedFocusModes();if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {  // Autofocus mode is supported}

  有些功能是要在Manifest里进行声明,比如闪光和自动对焦。可以在Manifest里的“Features Reference”中查到。

& 使用特性

  从camera中getParameters();然后进行setXXX();之后再setParameters()进Camera。

重要:有些参数的设置,可能需要先stop preview,变换preview size ,然后再重启preview。从4.0开始之后就不用再重启preview了。

  上文提到的三个部分,需要写一些更多的代码,下面会说到。

& Metering和对焦区域

跟其他的调用方法差不多

& 面部识别

  大多数包括人的照片里,脸部很重要,拍照时应该被焦点或者白平衡。4.0提供了API来确定脸部,并利用面部识别来捕捉照片。

注意:当使用面部识别时,setWhiteBalance(String),setFocusAreas(List)以及setMeteringAreas(List)就没有用了。

使用这个通常需要几个步骤:

    • 检查这个功能是是否被设备支持
    • 创建一个面部监测的监听
    • 把这个监听添加到Camera对象里
    • 在preview之后开始面部监测

面部识别不被所有设备支持,通过调用getMaxNumDetectedFaces()来检查是否可以使用。在下面的startFAceDetection()例子中,展示一个这个检测。

  为了能被提醒并且响应面部识别的监测,需要对面部检测加一个监听。创建一个实现了Camera.FaceDetectionListener接口的监听。如下代码所示:

class MyFaceDetectionListener implements Camera.FaceDetectionListener {    @Override    public void onFaceDetection(Face[] faces, Camera camera) {        if (faces.length > 0){            Log.d("FaceDetection", "face detected: "+ faces.length +                    " Face 1 Location X: " + faces[0].rect.centerX() +                    "Y: " + faces[0].rect.centerY() );        }    }}

创建完之后,把它加入到Camera对象里。

mCamera.setFaceDetectionListener(new MyFaceDetectionListener()

  应用应该在每次开始(或重启)Camera Preview时开启这个监听方法。创建一个用来开启面部识别的方法,如下所示:

public void startFaceDetection(){    // Try starting Face Detection    Camera.Parameters params = mCamera.getParameters();    // start face detection only *after* preview has started    if (params.getMaxNumDetectedFaces() > 0){        // camera supports face detection, so can start it:        mCamera.startFaceDetection();    }}

  必须在每次打开(或重启)preview时,开启面部监测。把上面这个方法添加到你的Preview类里的surfaceCreated()和surfaceChanged()方法中。如下所示:

public void surfaceCreated(SurfaceHolder holder) {    try {        mCamera.setPreviewDisplay(holder);        mCamera.startPreview();        startFaceDetection(); // start face detection feature    } catch (IOException e) {        Log.d(TAG, "Error setting camera preview: " + e.getMessage());    }}public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {    if (mHolder.getSurface() == null){        // preview surface does not exist        Log.d(TAG, "mHolder.getSurface() == null");        return;    }    try {        mCamera.stopPreview();    } catch (Exception e){        // ignore: tried to stop a non-existent preview        Log.d(TAG, "Error stopping camera preview: " + e.getMessage());    }    try {        mCamera.setPreviewDisplay(mHolder);        mCamera.startPreview();        startFaceDetection(); // re-start face detection feature    } catch (Exception e){        // ignore: tried to stop a non-existent preview        Log.d(TAG, "Error starting camera preview: " + e.getMessage());    }}

 注意:记住要在调用startPreview()之后调用这个方法。不要尝试在mainActivity 的onCreate()方法里启动面部识别。

更多相关文章

  1. Android(安卓)判断当前的界面是否是桌面的方法
  2. Android之jni深入
  3. Android(安卓)Fragment学习之二、Fragement的使用方法
  4. Android使用GPS获取用户地理位置并监听位置变化的方法
  5. android 快捷键
  6. 使android dialog点击按钮dialog不消失的方法
  7. Android(安卓)NestedScrolling机制完全解析 带你玩转嵌套滑动
  8. Android完全退出的4种方法
  9. SONY 系列手机 Android(安卓)5.1 系统 Root 方法

随机推荐

  1. Android之Android studio动态搜索添加依
  2. 《Android》Lesson19-广播1
  3. Android之常用开发框架
  4. Android项目中使用MVP模式
  5. Android studio 自定义打包APK名称
  6. Android中ActivityManager学习笔记(3)
  7. Android学习笔记-1.Android工程结构
  8. android暂停或停止其他音乐播放器的播放
  9. Android的四种启动模式(launchModel)
  10. 【搜集】Android permission 访问权限(附