本文主要分为两大部分,首先介绍了Camera API和SurfaceView,然后介绍了在Android中使用照相功能和录像功能的方法。

Camera API 和 SurfaceView介绍

Camera API 是Android提供的用于拍照的类,Camera实例提供了对设备相机硬件级别的调用。相机是一种独占性资源,一次只能有一个Activity调用相机。
也就是说,在使用Camera时,需要时使用,用完需立即释放,若忘记释放,除非重启设备,否则其他应用将无法使用相机。
管理Camera实例有如下几种方法:

//从API 9开始引入,cameraId = 0时默认打开后置摄像头,cameraId = 1时默认打开前置摄像头public static Camera open(int cameraId)//API 8及以下版本初始化Camera实例,默认打开后置摄像头public static Camera open()//Activity/Fragment被销毁时,应及时调用下面方法释放相机资源public final void release()

SurfaceView实例是相机的取景器,SurfaceView是一种特殊的视图,可直接将要显示的内容渲染输出到设备的屏幕上。
简单地说,SurfaceView可以完成单位时间内大量界面变化的需求,比如视频播放器,游戏画面,照相机取景等。
SurfaceView的内部利用了双缓冲机制实现了画面的快速刷新,所谓的双缓冲机制,就是利用SurfaceView内部包含的两个子线程(假设为Thread A和Thread B)交替工作,其工作示意图如下:

Thread A : 解码图像—>前台显示—>解码图像—>前台显示 。。。
Thread B : —(空)—- 解码图像—>前台显示—>解码图像 。。。

也就是说,Thread A 和Thread B交替工作,当A线程解码图像的时候,B线程在同一时刻把刚刚解码的图像切换至UI线程(主线程)并进行显示,以保证在任意时刻总有解码完毕的图像显示在界面上,这样,就达到了流畅的视频播播放效果。

在SurfaceView中了实现SurfaceHolder.Callback接口,SurfaceHolder是用户与Surface对象联系的纽带,而Surface对象代表原始像素数据的缓冲区。

当SurfaceView出现在屏幕上时,会创建Surface;当SurfaceView从屏幕上消失时候,Surface随即被销毁。Surface不存在时候,必须保证没有任何内容要在它上面绘制

不同于其他视图对象,SurfaceView及其协同工作对象都不会自我绘制内容,对于任何想将内容绘制到Surface缓冲区的对象,我们将其称之为Surface客户端,Camera对象就是一个Surface客户端。

也就是说,只有当Surface对象创建完成后,Surface的客户端(如Camera)才能在Surface的缓冲区绘制,而当Surface不存在时,Surface的缓冲区不能有任何绘制的内容。SurfaceHolder.Callback接口就是用于监听Surface的生命周期事件,以便控制Surface与其客户端协同工作,其三个回调方法如下(当Surface和其客户端关联/不再关联时回调):

//包含SurfaceView的视图层级结构被放到屏幕上时调用该方法,也是Surface与其客户端(如Camera)关联的地方public abstract void surfaceCreated(SurfaceHolder holder)//Surface首次出现在屏幕上时调用,该方法通知Surface客户端(如Camera),有多大的绘制区域可以使用public abstract void surfaceChanged(SurfaceHolder holder, int format, int width, int height)//SurfaceView从屏幕移除时,Surface也被随即销毁,通过该方法通知Surface的客户端(如Camera)停止使用Surfacepublic abstract void surfaceDestoryed(SurfaceHolder holder)

下面三个Camera中的方法用于响应相应的Surface生命周期事件:

//用于连接Surface客户端(如Camera)和Surface,在surfaceCreated()中调用public final void setPreviewDisplay(SurfaceHolder holder)//用于在Surface上绘制,在surfaceChanged()中调用public final void startPreview()//用于停止在Surface上绘制,在surfaceDestroyed()中调用public final void stopPreview()

使用Intent调用系统相机拍照

实现一个相机拍照功能,最简单的方式是使用隐式Intent调用系统自带的照相机拍照功能,要实现此功能,需要为Intent设置一个action和一个extra,代码片段如下:

//在Intent中设置action为MediaStore.ACTION_IMAGE_CAPTURE,表示启动一个包含照相功能的组件Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);//为Intent添加一个extra,键为MediaStore.EXTRA_OUTPUT,值为SD卡根目录。表示将相机捕捉的图像保存在该位置file = new File(Environment.getExternalStorageDirectory(),System.currentTimeMillis() + ".jpg");intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));

在回调方法onActivityResult方法中,将目标intent携带的返回信息(即拍摄的照片)显示在imageView中:

mImageView.setImageURI(Uri.fromFile(file));

为捕捉的图像添加预览功能:

Intent intent = new Intent(Intent.ACTION_VIEW);intent.setDataAndType(Uri.fromFile(file), "image/*");startActivity(intent);

使用Camera和SurfaceView自定义照相机

根据第一部分的介绍,下面将简介一个自定义的照相机应用。
实现步骤:

  1. 检测设备上是否有摄像头;
  2. 检测设备版本,根据版本型号调用相应的open方法,以创建Camera对象;
  3. 为Camera对象设置Parameter参数;
  4. 定制实现了SurfaceHolder.Callback接口的SurfaceView类,并在回调方法中连接/断开Surface客户端(本例中为Camera);
  5. 将定制的SurfaceView添加至承载布局容器中;
  6. 调用Camera的takePicture方法进行拍照。

下面将对如上所述的各个步骤以代码的方式结合介绍:

//第一步//检测设备是否有摄像头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;        }
//第二步,判断设备版本,创建Camera实例if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD){    //若设备的版本API>=9(Android 2.3 代号姜饼),则调用open(int)方法实例化Camera对象    mCamera = Camera.open(0);}else{    //否则调用open()方法实例化Camera对象    mCamera = Camera.open();}
//第三步,为Camera设置参数Parameters parameters = camera.getParameters();// 设置闪光灯强制打开parameters.setFlashMode(parameters.FLASH_MODE_AUTO);// 设置白平衡,WHITE_BALANCE          parameters.setWhiteBalance(Parameters.WHITE_BALANCE_AUTO);// 设置照片颜色特效,EFFECT  parameters.setColorEffect(parameters.EFFECT_SEPIA);// 设置拍摄照片的尺寸parameters.setPictureSize(1280, 720);// 设置照片的预览尺寸parameters.setPreviewSize(1280, 720);// 设置照片的质量parameters.setJpegQuality(100);// Android 2.2及以后(>=API 80 水平 90垂直方向camera.setDisplayOrientation(90);
//第四步//定制SurfaceView类//得到SurfaceHolder对象SurfaceHolder holder = mSurfaceView.getHolder();//兼容Android3.0以下版本设备的相机预览holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);//添加SurfaceHolder.Callback接口,用于监听Surface的生命周期,并在特定的时机创建、改变、销毁Surface的客户端(Camera实例)holder.addCallback(new SurfaceHolder.Callback(){    //通知Camera可以使用Surface作为其预览区域时回调    @override    public void surfaceCreated(SurfaceHolder holder)    {        try        {            if(mCamera != null)            {                mCamera.setPreviewDisplay(holder);                mCamera.startPreview();            }           }        catch (IOException e) {            Log.d(TAG, "Error setting camera preview: " + e.getMessage());        }    }    //通知Camera需释放其资源时回调    @override    public void surfaceDestroyed(SurfaceHolder holder) {        if(mCamera != null)        {            mCamera.stopPreview();        }    }    //当预览界面发生改变时(如屏幕旋转、全屏切换等),通知Camera重绘    @override    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {        if (holder.getSurface() == null) {            // 若surface已被释放,直接返回            return;        }        // 在重绘之前,先调用stopPreview停止预览        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        // 重绘预览        try {            mCamera.setPreviewDisplay(holder);            mCamera.startPreview();        } catch (Exception e) {            Log.d(TAG, "Error starting camera preview: " + e.getMessage());        }    }});
//第五步:将定制的SurfaceView添加至承载布局容器中CustomSurfaceView preview = new CustomSurfaceView(this, mCamera);mContainLayout.addView(preview);

第六步:调用下面方法实现拍摄一张照片

public final void takePicture(Camera.ShutterCallback shutter, Camera.PictureCallback raw, Callback.PictureCallback jpeg)
  • 参数1:Camera.ShutterCallback

在接口Camera.ShutterCallback中的回调方法

public abstract void onShutter();

会在相机捕获图像时调用,但此时,图像还未处理完成,所以,在该回调方法中,一般会显示一个进度条,告知用户保存图片的处理进度。

  • 参数2和参数3:Camera.PictureCallback
    在接口Camera.PictureCallback中的回调方法
public abstract void onPictureToken(byte[] data, Camera camera)

参数2和参数3接口都会回调上述方法,但参数2一般是在加工处理原始图像数据且没有存储之前;而参数3回调是在JPEG版本的图像可用时。

//调用Camera.takePicture()mCamera.takePicture(new ShutterCallback() {    @Override    public void onShutter() {       Toast.makeText(getApplicationContext(),"点击快门", 0).show();                            }    }, null,     new PictureCallback() {    @Override    public void onPictureTaken(byte[] data,Camera camera) {        try {            //将图片保存在SD根目录下,文件名设置为开机时的时间            File file = new File(Environment                                        .getExternalStorageDirectory(),                                         SystemClock.uptimeMillis()+ ".jpg");            FileOutputStream fos = new FileOutputStream(file);            fos.write(data);            fos.close();            Toast.makeText(getApplicationContext(),"拍照成功", 0).show();            mCamera.startPreview();            }             catch (Exception e) {                    e.printStackTrace();                                }    }});

注意事项

  • 使用相机拍摄照片与访问用户的外置存储卡都涉及侵犯用户隐私,故需要在AndroidManifest中向系统声明权限:
<!-- 申请访问设备摄像头 --><uses-permission android:name="android.permission.CAMERA" /><!-- 申请访问设备的外置存储卡 --><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  • 使用uses-feature标签告知应用商店(如Google Play)本应用将使用设备的摄像头,若设备上没有摄像头,设备将无法在应用商店(如Google Play)中搜索到该应用:
<uses-feature android:name="android.hardware.camera" />

使用Intent调用系统相机录像

与调用系统相机拍照相仿,使用隐式Intent调用系统相机录像只是Intent的action不同:

//设置action为MediaStore.ACTION_VIDEO_CAPTURE,表示启动一个可以进行录像组件Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);file = new File(Environment.getExternalStorageDirectory(), SystemClock.uptimeMillis() + ".mp4");// 创建一个文件存储file,并设置文件名,设置uri为存储录像的路径intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file))startActivityForResult(intent, 200);

在onActivityResult方法中接收启动的目标activity的返回信息:

@Override    protected void onActivityResult(int requestCode, int resultCode, Intent data) {        super.onActivityResult(requestCode, resultCode, data);        switch (requestCode) {        case 200:            Toast.makeText(MainActivity.this, "录像成功", 0).show();            break;        default:            break;        }    }

启动系统播放器,播放拍摄的视频

Intent intent = new Intent(Intent.ACTION_VIEW);intent.setDataAndType(Uri.fromFile(file), "video/*");startActivity(intent);

使用Camera、MediaRecorder和SurfaceView自定义摄像机

自定义摄像机功能如下:

  1. 检测设备上是否有摄像头;(此步在下面的示例代码中省略)
  2. 检测设备版本,根据版本型号调用相应的open方法,以创建Camera对象;(此步在下面的示例代码中省略)
  3. 定制实现了SurfaceHolder.Callback接口的SurfaceView类,并在回调方法中连接/断开Surface客户端(本例中为Camera);
  4. 解锁摄像头(Camera.unlock());
  5. 创建MediaRecorder对象,并为其设置参数;
  6. 依次调用MediaRecorder.prepare()和MediaRecorder.start()方法开始录像;
  7. 当触发终止录像操作时,分别调用MediaRecorder.stop()、MediaRecorder.release()释放MediaRecorder对象,并将对象置为空(null),最后调用Camera.lock()锁定Camera。
public class MainActivity extends Activity {    private Button bt_take_video;    private SurfaceView sView;    private Camera camera;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        bt_take_video = (Button) findViewById(R.id.bt_take_video);        sView = (SurfaceView) findViewById(R.id.id_sv);//为兼容Android3.0以下版本,需设置下面的语句 sView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);        sView.getHolder().addCallback(new MycallBack());        bt_take_video.setOnClickListener(new OnClickListener() {            private MediaRecorder recorder;            @Override            public void onClick(View v) {                Button button = (Button) v;                if ("开始录像".equals(button.getText())) {                    button.setText("暂停录像");                    // 解锁摄像头                    camera.unlock();                    // 创建一个多媒体记录器对象                    recorder = new MediaRecorder();                    // 给记录器设置摄像头                    recorder.setCamera(camera);                    // 设置音频源                    recorder.setAudioSource(AudioSource.MIC);                    //设置视频源recorder.setVideoSource(VideoSource.CAMERA);                     // 设置输出格式和编码,Android2.2版本及以上。                    recorder.setProfile(CamcorderProfile                            .get(CamcorderProfile.QUALITY_HIGH));                    // 设置文件输出路径                    recorder.setOutputFile("/mnt/sdcard/"                            + System.currentTimeMillis() + ".mp4");                    // 设置预览                    recorder.setPreviewDisplay(sView.getHolder().getSurface());                    try {                        // 准备                        recorder.prepare();                    } catch (Exception e) {                        e.printStackTrace();                    }                    // 开始录像                    recorder.start();                } else {                    button.setText("开始录像");                    if (recorder != null) {                        // 停止多媒体记录器                        recorder.stop();                        // 释放                        recorder.release();                        // 将MediaRecorder引用置空,Garbage Collection将回收对象占用的内存                        recorder = null;                        // 锁定摄像头                        camera.lock();                    }                }            }        });    }    // 定制SurfaceView的回调接口SurfaceHolder.Callback    private class MycallBack implements SurfaceHolder.Callback {        @Override        public void surfaceChanged(SurfaceHolder holder, int format, int width,                int height) {        }        @Override        public void surfaceCreated(SurfaceHolder holder) {            try {                camera = Camera.open();                camera.setPreviewDisplay(sView.getHolder());                camera.startPreview();            } catch (IOException e) {                e.printStackTrace();            }        }        @Override        public void surfaceDestroyed(SurfaceHolder holder) {            if (camera != null) {                camera.stopPreview();                camera.release();                camera = null;            }        }    }}

注意事项

在AndroidManifest中设置应用需要访问的系统权限:

<!-- 向系统申请应用程序访问摄像头的权限 --><uses-permission android:name="android.permission.CAMERA" /><!-- 向系统申请应用程序访问麦克风的权限 --><uses-permission android:name="android.permission.RECORD_AUDIO" /><!-- 向系统申请应用程序访问外置存储卡的权限 --><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

更多相关文章

  1. Android中HttpURLConnection使用详解
  2. Android中的ListView内容刷新问题
  3. Android(安卓)Data Binding简单示例
  4. Xamarin for android:为button设置click事件的几种方法
  5. Android(安卓)使用Intent传递数据的实现思路与代码
  6. Android自定义View,你必须知道的几点
  7. Android面试题(基础部分1)
  8. 自定义控件(一)
  9. ADB使用方法及常用命令

随机推荐

  1. 安卓版本和Api Level
  2. Android(安卓)P WiFi自动连接评分机制
  3. Android(安卓)activity的生命周期
  4. android > 布局文件 > 背景圆角
  5. ScrollView嵌套webview因焦点问题导致自
  6. [转]Iperf tool for Android
  7. 判断客户端类型
  8. 【Android(安卓)Demo】Android中取得手机
  9. Android中JNI实现
  10. android map