「这是我参与2022首次更文挑战的第31天,活动详情查看:2022首次更文挑战」
前言
在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更专业,就好比单反和傻瓜相机的区别似的,所以使用起来也更加复杂一些。