前言
在android我们可以调用系统的相机来进行照相,当然也可以设计自己的照相页面,通过surface进行预览,通过camera api来进行拍照即可。
在Android 5.0之后google推荐使用的Camera2 Api,但是由于兼容问题我们大部分还在使用Camera Api。这里我们一起简单介绍一下,仅仅是预览拍照,至于细节的设置,比如图像大小比例、对焦、图像旋转等就不一一细说了。
首先我们需要在布局中加入一个SurfaceView
Camera Api
var camera: Camera? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) //为surfaceholder添加监听 surface.holder.addCallback(object : SurfaceHolder.Callback{ override fun surfaceChanged(p0: SurfaceHolder?, p1: Int, p2: Int, p3: Int) { } override fun surfaceDestroyed(p0: SurfaceHolder?) { } override fun surfaceCreated(p0: SurfaceHolder?) { //当surface创建完成初始化相机 initCamera1() } }) take.setOnClickListener { //拍照 takePicture1() } } fun initCamera1(){ //打开相机,开启预览 camera = Camera.open() camera?.setPreviewDisplay(surface.holder) camera?.startPreview() } fun takePicture1(){ //拍照,通过回调获取图片数据 camera?.takePicture({}, {bytes, camera -> if(bytes == null){ return@takePicture } var b = BitmapFactory.decodeByteArray(bytes, 0, bytes.size) imageView.setImageBitmap(b) }, {bytes, camera -> if(bytes == null){ return@takePicture } var b = BitmapFactory.decodeByteArray(bytes, 0, bytes.size) imageView2.setImageBitmap(b) }) } 复制代码
可以看到使用Camera比较简单,打开相机开启预览,拍照在回调中处理即可。
Camera2 Api
Camera2的Api相比Camera要复杂很多
var mCameraDevice : CameraDevice? = null var mCameraCaptureSession : CameraCaptureSession? = null var childHandler : Handler? = null @SuppressLint("NewApi") var imageReader : ImageReader? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) //为surfaceholder添加监听 surface.holder.addCallback(object : SurfaceHolder.Callback{ override fun surfaceChanged(p0: SurfaceHolder?, p1: Int, p2: Int, p3: Int) { } override fun surfaceDestroyed(p0: SurfaceHolder?) { } override fun surfaceCreated(p0: SurfaceHolder?) { //当surface创建完成初始化相机 initCamera2() } }) take.setOnClickListener { //拍照 takePicture2() } } fun initCamera2(){ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { //因为处理图片比较耗时,所以这里新建一个线程的handler来处理 var handlerThread = HandlerThread("camera") handlerThread.start() childHandler = Handler(handlerThread.looper) //创建一个ImageReader,后面会通过这个获取拍照的图片 imageReader = ImageReader.newInstance(500, 500, ImageFormat.JPEG,2) //为ImageReader添加回调,这里使用我们新建的线程的handler处理图片 imageReader?.setOnImageAvailableListener(object : ImageReader.OnImageAvailableListener{ override fun onImageAvailable(imageReader: ImageReader) { //得到图片后,关闭相机,从ImageReader中获取图片数据 mCameraDevice?.close() var image = imageReader.acquireNextImage() var buffer = image.planes[0].buffer var bytes = ByteArray(buffer.remaining()) buffer.get(bytes) var bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size) //由于这部分是在子线程中处理的,所以对ui操作要放到主线程中 runOnUiThread { imageView.setImageBitmap(bitmap) } } }, childHandler) //通过CameraManager打开相机,注意这里的handler是主线程的。 var cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager cameraManager.openCamera(CameraCharacteristics.LENS_FACING_FRONT.toString(), object : CameraDevice.StateCallback() { override fun onOpened(cameraDevice: CameraDevice) { //相机打开后,得到CameraDevice并开启预览 mCameraDevice = cameraDevice camera2Preview() } override fun onDisconnected(p0: CameraDevice) { } override fun onError(p0: CameraDevice, p1: Int) { } }, Handler(Looper.getMainLooper())) } } fun camera2Preview(){ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { mCameraDevice?.let { //新建一个PRIVIEW的CaptureRequest,将surface添加进去 var builder = it.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW) builder.addTarget(surface.holder.surface) //通过CameraDevice新建一个CaptureSession事务,在回调中开启预览 it.createCaptureSession(Arrays.asList(surface.holder.surface, imageReader?.surface), object : CameraCaptureSession.StateCallback() { override fun onConfigureFailed(p0: CameraCaptureSession) { } override fun onConfigured(cameraCaptureSession: CameraCaptureSession) { //获得对应的CaptureSession事务,并设置预览的request开始预览 mCameraCaptureSession = cameraCaptureSession var previewRequest = builder.build() mCameraCaptureSession?.setRepeatingRequest(previewRequest, null ,childHandler) } }, childHandler) } } } //拍照 fun takePicture2(){ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { mCameraDevice?.let { //新建一个拍照请求,并添加之前创建的ImageReader的surface var builder = it.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE) builder.addTarget(imageReader!!.surface) var previewRequest = builder.build() //通过预览是获取的CaptureSession事务,执行拍照请求,结果会通过之前设置的ImageReader的回调返回 mCameraCaptureSession?.setRepeatingRequest(previewRequest, null ,childHandler) } } } 复制代码
使用Camera2 Api的流程基本与Camera一样,但是代码复杂很多,主要是需要通过事务CaptureSession来处理预览及拍照,而且为了处理图片还需要一个新建子线程,同时拍照结果也是通过ImageReader封装起来的。
Camera2拥有更多的能力,可以实现更复杂的拍照场景和设定,相对于Camera Api更专业,就好比单反和傻瓜相机的区别似的,所以使用起来也更加复杂一些。