Android自定义相机之Camera1

一、概述

本章节主要讲述使用android.hardware.Camera类来实现自定义相机的流程,虽然在api21中该类已被废弃,有了一套新的CameraDevice方法,但是为了向下兼容我们还是可以学习一下Camera1的使用。

如有错误或优化之处,欢迎留言指导。

二、具体实现

首先介绍下如何判断是否支持照相机功能,代码如下:

private boolean checkCamera(Context context) {
    // 可修改参数来判断设备是否支持ble等其他功能
    if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
        return true; // 支持Camera功能
    } else {
        return false;
    }
}

获取一个Camera对象

private Camera getCamera() {
    Camera camera = null;
    try {
        camera = Camera.open();
    } catch (Exception e) {
        Log.e(TAG, "getCamera: Camera.open failed!");
    }
    return camera;
}

我们通过SurfaceView来预览照相机画面,需要获取SurfaceHolder并添加回调

mPreView = (SurfaceView) findViewById(R.id.preview);
mHolder = mPreView.getHolder();
mHolder.addCallback(this); // 实现SurfaceHolder.Callback接口
mPreView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (mCamera != null) {
            // 照相机自动对焦功能
            mCamera.autoFocus(null);
        }
    }
});

绑定Camera和SurfaceHolder,然后开始预览画面

private void setStartPreview(Camera camera, SurfaceHolder holder) {
    try {
        camera.setPreviewDisplay(holder); // 绑定Camera和SurfaceHolder
        camera.setDisplayOrientation(90); // 旋转90度才是正常画面
        camera.startPreview();
    } catch (IOException e) {
        Log.e(TAG, "setStartPreview: setPreviewDisplay error");
    }
}

在使用完相机后需要释放相机资源,同时我们要注意把相机和活动的生命周期绑定,在onResume中获取相机并预览,在onPause中释放相机资源,防止其他应用调用相机时出错

private void releaseCamera() {
    if (mCamera != null) {
        mCamera.setPreviewCallback(null);
        mCamera.stopPreview();
        mCamera.release();
        mCamera = null;
    }
}

SurfaceHolder.Callback接口有create,change,destroy三个方法,我们在create中初始化,在change中预览画面,在destroy前释放相机资源

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        if (mCamera != null && mHolder != null) {
            setStartPreview(mCamera, mHolder);
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        if (mCamera != null) {
            mCamera.stopPreview();
            if (mHolder != null) {
                setStartPreview(mCamera, mHolder);
            }
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        releaseCamera();
    }

最后就是点击拍照并获取图片,这里我们可以设置Camera.Parameters属性作用于我们要拍摄的照片,调用tackPicture方法进行拍照

Camera.Parameters parameters = mCamera.getParameters();
parameters.setPictureFormat(ImageFormat.JPEG); // 相片保存的格式
parameters.setPictureSize(800, 400); // 相片保存的尺寸,因为是横屏所以长>高
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO); // 自动对焦
mCamera.autoFocus(new Camera.AutoFocusCallback() {
    @Override
    public void onAutoFocus(boolean success, Camera camera) {
        if (success) {
             // 拍照
             mCamera.takePicture(null, null, mPictureCallback);
         }
    }
});

takePicture的第一个参数是快门的回调,后面两个都是图像数据的回调,通常我们只需要实现第三个存储照片的回调就可以了

private Camera.PictureCallback mPictureCallback = new Camera.PictureCallback() {
    @Override
    public void onPictureTaken(byte[] data, Camera camera) {
        // 存储的路径及文件名
        mFilePath = mFilePath + "/" + System.currentTimeMillis() + ".png";
        File tempFile = new File(mFilePath);
        try {
            if (tempFile != null) {
                FileOutputStream fos = new FileOutputStream(tempFile);
                fos.write(data);
                fos.close();
                Intent intent = new Intent(CameraActivity.this, PictureActivity.class);
                 // 传给下一个显示照片的activity
                intent.putExtra("picPath", tempFile.getAbsolutePath());
                startActivity(intent);
                finish();
            }
        } catch (FileNotFoundException e) {
            Log.e(TAG, "onPictureTaken: " + e.getMessage());
        } catch (IOException e) {
            Log.e(TAG, "onPictureTaken: " + e.getMessage() );
        }
    }
};

存储完照片后,我们可以从传递的路径获取到图片并显示

mImageView = (ImageView) findViewById(R.id.iv_picture);
mPath = getIntent().getStringExtra("picPath");
try {
    FileInputStream fis = new FileInputStream(new File(mPath));
    Bitmap bitmap = BitmapFactory.decodeStream(fis);
    // 因为画面预览时是旋转后显示的,所以保存的图片也需要旋转90度
    Matrix matrix = new Matrix();
    matrix.setRotate(90);
    // 创建一个新的Bitmap
    bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
    mImageView.setImageBitmap(bitmap);
} catch (FileNotFoundException e) {
    Log.e(TAG, "onCreate: FileInputStream error");
}

PS:自定义时请勿忘记申明权限

<uses-feature android:name="android.hardware.Camera" android:required="true" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />

三、后言

本章节实现步骤就介绍到这边,有错误欢迎指出,源码会在后续的综合demo中放出。下一章节准备学习记录一下Camera2的使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章