自定义相机CameraView与踩坑记录

这篇博客详细记录了使用Camera1 API实现自定义CameraView的过程,包括申请权限、自定义SurfaceView、拍照功能的实现及遇到的问题。在SurfaceView的使用中,博主遇到了按home键后预览黑屏的问题,以及在切换前后置摄像头时,预览尺寸错误和相机设置参数失败的问题。通过调整初始化方式和检查设备支持参数解决了这些问题。在拍照环节,博主发现华为Nexus 6P拍照角度错误,通过获取相机方向信息并结合Exif信息修正了图片角度。最后提供了自定义CameraView的布局实现和在Activity中的使用方法。
摘要由CSDN通过智能技术生成

自定义相机CameraView与踩坑记录

记录下在做自定义CameraView相机功能的过程中的思路和遇到的问题以及解决方案,用的是Camera1 API(考虑到Camera2在部分手机上支持还是比较弱的)

知识储备

没有接触过Camera API的同学强烈建议阅读下面的文章

Android: Camera相机开发详解(上) —— 知识储备
Android: Camera相机开发详解(中) ——实现预览、拍照、保存照片等功能 代码实现过程中也有借鉴过这篇文章

实现思路

  1. 在xml布局中定义一个SurfaceView,用于预览相机采集的数据
  2. 给SurfaceHolder添加回调,在surfaceCreated(holder: SurfaceHolder?)回调中打开相机
  3. 成功打开相机后,设置相机参数。比如:对焦模式,预览大小,照片保存大小等等
  4. 设置相机预览时的旋转角度,然后调用startPreview()开始预览
  5. 通过Camera的setOneShotPreviewCallback回调,获取字节数组进行解析成Bitmap 调用takePicture方法拍照 或者 是在Camera的预览回调中 保存照片
  6. 对保存的照片进行旋转处理,使其为"自然方向"
  7. 关闭页面,释放相机资源
    Android: Camera相机开发详解(中)不同的是第5点,其余大致思路基本相同

具体实现步骤

一、申请权限

这里用到了前置摄像头,自动对焦,闪关灯

	<uses-permission android:name="android.permission.CAMERA" />
    <uses-feature
        android:name="android.hardware.camera"
        android:required="true" />
    <uses-feature
        android:name="android.hardware.camera.autofocus"
        android:required="true" />
    <uses-feature
        android:name="android.hardware.camera.front"
        android:required="true" />
    <uses-feature
        android:name="android.hardware.camera.flash"
        android:required="true" />

二、自定义View用来做Camera的预览View

这里也不同于Android: Camera相机开发详解(中),上文是在布局文件中写了一个SurfaceView来操作,这样虽然也能成功开启预览,但是在这种实现方式下开启预览之后手机按home键SurfaceView就黑屏了,预览就再也起不来了。所以这是第一个坑

解决思路是:将SurfaceView单独抽出来,布局文件中写一个FrameLayout(或其他Layout)的容器,在Activity/Fragment中new这个SurfaceView出来addView到容器中。
谷歌官方的指引中也是这样的写法:
camera-preview
preview-layout

class CameraPreview(private val activity: Activity, context: Context?) : SurfaceView(context),
    SurfaceHolder.Callback {
   

    //Camera对象
    private var mCamera: Camera? = null
    //Camera对象的参数
    private lateinit var mParameters: Camera.Parameters
    //SurfaceHolder对象
    private var mSurfaceHolder: SurfaceHolder = holder
    //摄像头方向
    var mCameraFacing = Camera.CameraInfo.CAMERA_FACING_BACK
    var mFlashStatus = Camera.Parameters.FLASH_MODE_OFF
    //预览旋转的角度
    var mDisplayOrientation: Int = 0

    var isPreviewing = false


    init {
   
        mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS)
        mSurfaceHolder.addCallback(this)
    }

    override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) {
   
    }

    override fun surfaceDestroyed(holder: SurfaceHolder?) {
   
        releaseCamera()
    }

    override fun surfaceCreated(holder: SurfaceHolder?) {
   
        openCameraWithFacing()
    }
 }

CameraPreview在创建后走init初始化,初始化的时候就对holder设置回调mSurfaceHolder.addCallback(this),在surfaceCreated中打开相机,设置参数等一系列的操作

    /**
     * 开启相机并进行预览
     */
    private fun openCameraWithFacing() {
   
        val supportCameraFacing = CameraUtil.isSupportCameraFacing(mCameraFacing)
        if (supportCameraFacing) {
   
            mCamera = getCameraInstance(mCameraFacing)
            if (mCamera == null) {
   
                CameraLogger.log("相机实例获取失败")
                return
            }
            CameraLogger.log("相机实例获取成功")
            initParameters(mCamera!!)
            startPreview()
        } else {
   
            CameraLogger.log("不支持的相机摄像头:$mCameraFacing")
        }
    }
    
    /**
     * 获取相机实例
     */
    private fun getCameraInstance(cameraFacing: Int): Camera? {
   
        return try {
   
            Camera.open(cameraFacing) // attempt to get a Camera instance
        } catch (e: Exception) {
   
            // Camera is not available (in use or does not exist)
            null // returns null if camera is unavailable
        }
    }

需要注意的是设置参数的initParameters方法,一开始的是写法是这样的,简单粗暴

  private fun initParameters(camera: Camera) {
   
        try {
   
            mParameters = camera.parameters
            mParameters.previewFormat = ImageFormat.NV21   //设置预览图片的格式

            //获取与指定宽高相等或最接近的尺寸
            //设置预览尺寸
            val bestPreviewSize = getBestSize(mSurfaceView.width, mSurfaceView.<
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值