Android Camera 简单封装 思路 1、按照官方的API,简单封装个可行的CameraView 2、能够简单的设置部分相机配置 3、由于需要,所以添加原始帧数据的预览获取 4、优化代码,更简单的调用 实现 CameraView代码 先来展示代码,之后再进行说明: package com.syxrobot.cameratest import android.annotation.SuppressLint import android.content.Context import android.graphics.ImageFormat import android.graphics.Rect import android.graphics.YuvImage import android.hardware.Camera import android.view.SurfaceHolder import android.view.SurfaceView import java.io.ByteArrayOutputStream @SuppressLint("ViewConstructor") /** * @author:JinXuDong * @date:2018/7/3 */ class CameraView(context: Context?) : SurfaceView(context), SurfaceHolder.Callback, Camera.PreviewCallback { private var mCamera: Camera? = null private var mHolder: SurfaceHolder = holder private var mListener: ((ByteArray, Camera) -> Unit)? = null init { mHolder.addCallback(this) mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS) } override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) { if (mHolder.surface == null) { return } mCamera?.stopPreview() mCamera?.setPreviewCallback(this) mCamera?.setPreviewDisplay(mHolder) mCamera?.startPreview() } override fun surfaceDestroyed(holder: SurfaceHolder?) { mCamera?.stopPreview() releaseCamera() } override fun surfaceCreated(holder: SurfaceHolder?) { mCamera?.setPreviewCallback(this) mCamera?.setPreviewDisplay(holder) mCamera?.startPreview() } override fun onPreviewFrame(data: ByteArray, camera: Camera) { if (mListener != null) { mListener?.invoke(data, camera) } } /** * 预览数据 * 数据为原始数据,需要拍摄图片时需要自己转换格式 * 或可以调用其他方法转换 如 [onPreviewJpeg] 或 [onPreviewNV21ToJpeg] */ fun onPreview(l: ((data: ByteArray, camera: Camera) -> Unit)) { this.mListener = l } /** * 预览数据 * NV21 转 JPEG * @param width 宽 * @param height 高 * @param quality 图片质量 * @param l 回调函数 调用者需要在此做自己的操作 */ fun onPreviewNV21ToJpeg(width: Int, height: Int, quality: Int, l: ((data: ByteArray) -> Unit)) { onPreviewJpeg(ImageFormat.NV21, width, height, quality, l) } /** * 预览数据转换成JPEG格式 */ fun onPreviewJpeg(format: Int, width: Int, height: Int, quality: Int, l: ((data: ByteArray) -> Unit)) { onPreview { data, _ -> val yuv = YuvImage(data, format, width, height, null) val ops = ByteArrayOutputStream() yuv.compressToJpeg(Rect(0, 0, width, height), quality, ops) l.invoke(ops.toByteArray()) } } /** * 释放Camera资源 * 释放之前 可以判断是否已经被释放 [isReleased] */ fun releaseCamera() { if (mCamera != null) { synchronized(mCamera!!) { mCamera?.setPreviewCallback(null) mCamera?.stopPreview() mCamera?.release() mCamera = null } } } fun isReleased(): Boolean = mCamera == null /** * 获取Camera */ fun openCamera(cameraId: Int): CameraView { mCamera = Camera.open(cameraId) return this } /** * 相机参数设置 */ fun cameraConfig(cameraParameters: CameraParameters) { cameraParameters.commit() } /** * 返回相机 */ fun getCamera(): Camera? { return mCamera } /** * @author:JinXuDong * @date:2018/7/3 * 相机参数 */ inner class CameraParameters { private val mParameters: Camera.Parameters? = mCamera?.parameters fun setPreViewSize(width: Int, height: Int): CameraParameters { mParameters?.setPreviewSize(width, height) return this } /** * 设置预览图片图像格式。 * @param format 预览格式 */ fun setPreviewFormat(format: Int): CameraParameters { mParameters?.previewFormat = format return this } /** * 设置白平衡 */ fun setWhiteBalance(value: String): CameraParameters { mParameters?.whiteBalance = value return this } /** * 设置场景模式 */ fun setSceneMode(value: String): CameraParameters { mParameters?.sceneMode = value return this } /** * 设置曝光补偿指数。 * @param value 曝光值 */ fun setExposureCompensation(value: Int): CameraParameters { mParameters?.exposureCompensation = value return this } /** * 设置相机旋转角度 */ fun setDisplayOrientation(degrees: Int): CameraParameters { mCamera?.setDisplayOrientation(degrees) return this } /** * 提交设置 */ fun commit() { mCamera?.parameters = mParameters } } } CameraView代码说明 首先继承自SurfaceView,因为我们需要将预览的数据展示到SurfaceView上。 所以要实现以下三个方法 surfaceChanged() surfaceDestroyed() surfaceCreated() 我们在SurfaceView被创建时设置预览,在被销毁时同时释放相机资源,在状态改变时先停止预览,再重新预览。 openCamera(cameraId: Int) 用来打开相机,cameraId 代表前置或后置相机 0 后置 1 前置 可以直接调用CameraInfo的前置或后置摄像机的常量 cameraConfig() 设置相机参数 onPreview(l: ((data: ByteArray, camera: Camera) -> Unit)) 获取预览数据(原始数据) onPreviewJpeg(format: Int, width: Int, height: Int, quality: Int, l: ((data: ByteArray) -> Unit)) 获取预览数据(转换后的数据,JPEG) onPreviewNV21ToJpeg(width: Int, height: Int, quality: Int, l: ((data: ByteArray) -> Unit)) 获取预览数据(原始数据为NV21,转换后为JPEG) releaseCamera() 释放相机资源 isReleased() 相机资源是否被释放 getCamera() 获取相机实例,可以做更多自己的操作 CameraParameters 代码说明 setPreViewSize(width: Int, height: Int) 设置预览分辨率大小 setPreviewFormat(format: Int) 设置预览图片图像格式。 setWhiteBalance(value: String) 设置白平衡 setSceneMode(value: String) 设置场景模式 setExposureCompensation(value: Int) 设置曝光补偿指数 setDisplayOrientation(degrees: Int) 设置相机旋转角度 commit() 提交设置 使用方法 activity_main.xml布局文件 xmlns:app="http://schemas./apk/res-auto" xmlns:tools="http://schemas./tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> android:id="@+id/mFC" android:layout_width="match_parent" android:layout_height="match_parent" /> android:id="@+id/mBtn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="拍照" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" /> package com.syxrobot.cameratest import android.graphics.ImageFormat import android.hardware.Camera import android.os.Bundle import android.support.v7.app.AppCompatActivity import android.util.Log import kotlinx.android.synthetic.main.activity_main.* import java.io.File class MainActivity : AppCompatActivity() { private lateinit var cameraPreview: CameraView private var isTakePic: Boolean = false override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) if (checkCameraHardware(this)) { /** * 开启相机预览页 */ cameraPreview = CameraView(this) .openCamera(Camera.CameraInfo.CAMERA_FACING_BACK) /** * 相机参数设置 */ cameraPreview.cameraConfig(cameraPreview.CameraParameters() .setPreViewSize(1280, 720) .setPreviewFormat(ImageFormat.NV21) .setWhiteBalance(Camera.Parameters.WHITE_BALANCE_AUTO) .setSceneMode(Camera.Parameters.SCENE_MODE_AUTO) .setExposureCompensation(3) ) cameraPreview.onPreviewNV21ToJpeg(1280, 720, 70) { if (isTakePic) { isTakePic = false val file = File("/storage/emulated/0/test.jpg") file.writeBytes(it) Log.e("TestCamera", "保存成功") } } /** * 添加相机预览 */ mFC.addView(cameraPreview) } else { Log.e("TestCamera", "摄像机不能用") } mBtn.setOnClickListener { isTakePic = true } } override fun onDestroy() { super.onDestroy() if (!cameraPreview.isReleased()) { cameraPreview.releaseCamera() } } } /** * 检测相机是否可用 */ fun checkCameraHardware(context: Context): Boolean { return context.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA) } 代码说明 首先开启相机 然后设置相机参数 最后处理相机预览(如果有需要的话) 再退出程序后,为了避免会出问题,所以谨慎点可以再onDestroy()时手动调用一次释放资源。 注意 0、别忘了权限 1、由于本人开发环境需要兼容API19,为避免Camera2不适配,所以使用的是Camera1 2、由于本人的开发环境不需要动态申请权限,所以没写动态申请权限,需要的可以自己写 3、为避免由于硬件问题产生的错误,可以使用检测相机的那个方法,那是我从官方API中复制来的 。。。O(∩_∩)O哈哈~ 4、部分功能还没实现,若问为什么,本宝是个菜鸟,还在努力学习... |
|