在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封裝起來的。