Android照相和录音功能的使用


本文主要分为两大部分,首先介绍了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)停止使用Surface
public 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" />
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值