이 과정에서는 프레임워크 API를 사용하여 직접 카메라 하드웨어를 제어하는 방법을 논의합니다.
기기의 카메라를 직접 제어하는 것은 기존 카메라 애플리케이션에 사진이나 동영상을 요청하는 것보다 더 많은 코드가 필요합니다. 그러나, 전문 카메라 애플리케이션 또는 앱 UI에 완전히 통합된 어떤 것을 빌드한다면 이 과정에서 방법을 보여줍니다.
다음 관련 리소스를 참조하세요.
카메라 객체 열기
Camera
개체에서 인스턴스를 가져오는 것이 카메라를 직접 제어하는 절차의 첫 번째 단계입니다. Android의 자체 카메라 애플리케이션이 하는 것처럼 onCreate()
에서 시작된 별도의 스레드에 Camera
를 열어 카메라에 접근하는 방법을 권장합니다. 이 접근 방식은 시간이 걸릴 수 있고 UI 스레드를 멈출 수도 있기 때문에 좋습니다. 더 기본적인 구현에서 카메라를 여는 것은 onResume()
메서드로 연기되어 코드 재사용을 용이하게 하고 제어 흐름을 간단하게 유지할 수 있습니다.
Camera.open()
을 호출할 때 이미 다른 애플리케이션에서 카메라를 사용 중이라면 예외가 발생하므로 try
블록으로 호출 부분을 래핑합니다.
Kotlin
private fun safeCameraOpen(id: Int): Boolean { return try { releaseCameraAndPreview() mCamera = Camera.open(id) true } catch (e: Exception) { Log.e(getString(R.string.app_name), "failed to open Camera") e.printStackTrace() false } } private fun releaseCameraAndPreview() { preview?.setCamera(null) mCamera?.also { camera -> camera.release() mCamera = null } }
자바
private boolean safeCameraOpen(int id) { boolean qOpened = false; try { releaseCameraAndPreview(); camera = Camera.open(id); qOpened = (camera != null); } catch (Exception e) { Log.e(getString(R.string.app_name), "failed to open Camera"); e.printStackTrace(); } return qOpened; } private void releaseCameraAndPreview() { preview.setCamera(null); if (camera != null) { camera.release(); camera = null; } }
API 수준 9 이후로 카메라 프레임워크는 다중 카메라를 지원합니다. 기존 API를 사용하고 인수 없이 open()
을 호출하면 첫 번째 후면 카메라를 가져옵니다.
카메라 미리보기 만들기
일반적으로 사진을 촬영하려면 사용자가 셔터를 클릭하기 전에 피사체를 미리 봐야 합니다. 그렇게 하려면 SurfaceView
를 사용하여 카메라 센서에 포착된 피사체의 미리보기를 생성하면 됩니다.
미리보기 클래스
미리보기를 표시하려면 미리보기 클래스가 필요합니다. 미리보기에는 android.view.SurfaceHolder.Callback
인터페이스의 구현이 필요하며 이 인터페이스는 이미지 데이터를 카메라 하드웨어에서 애플리케이션으로 전달하는 데 사용됩니다.
Kotlin
class Preview( context: Context, val surfaceView: SurfaceView = SurfaceView(context) ) : ViewGroup(context), SurfaceHolder.Callback { var mHolder: SurfaceHolder = surfaceView.holder.apply { addCallback(this@Preview) setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS) } ... }
자바
class Preview extends ViewGroup implements SurfaceHolder.Callback { SurfaceView surfaceView; SurfaceHolder holder; Preview(Context context) { super(context); surfaceView = new SurfaceView(context); addView(surfaceView); // Install a SurfaceHolder.Callback so we get notified when the // underlying surface is created and destroyed. holder = surfaceView.getHolder(); holder.addCallback(this); holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } ... }
다음 섹션에서와 같이 미리보기 클래스는 라이브 이미지 미리보기가 시작되기 전에 Camera
객체에 전달되어야 합니다.
미리보기 설정 및 시작
카메라 인스턴스 및 그와 관련된 미리보기는 특정한 순서(카메라 개체가 첫 번째)로 생성되어야 합니다. 아래 스니펫에서 카메라의 초기화 절차는 사용자가 카메라를 변경하기 위해 어떤 작업을 할 때마다 Camera.startPreview()
가 setCamera()
메서드에 의해 호출되도록 캡슐화됩니다. 또한 미리보기는 미리보기 클래스의 surfaceChanged()
콜백 메서드에서 다시 시작되어야 합니다.
Kotlin
fun setCamera(camera: Camera?) { if (mCamera == camera) { return } stopPreviewAndFreeCamera() mCamera = camera mCamera?.apply { mSupportedPreviewSizes = parameters.supportedPreviewSizes requestLayout() try { setPreviewDisplay(holder) } catch (e: IOException) { e.printStackTrace() } // Important: Call startPreview() to start updating the preview // surface. Preview must be started before you can take a picture. startPreview() } }
자바
public void setCamera(Camera camera) { if (mCamera == camera) { return; } stopPreviewAndFreeCamera(); mCamera = camera; if (mCamera != null) { List<Size> localSizes = mCamera.getParameters().getSupportedPreviewSizes(); supportedPreviewSizes = localSizes; requestLayout(); try { mCamera.setPreviewDisplay(holder); } catch (IOException e) { e.printStackTrace(); } // Important: Call startPreview() to start updating the preview // surface. Preview must be started before you can take a picture. mCamera.startPreview(); } }
카메라 설정 수정
카메라 설정은 확대/축소 수준에서 노출 보정까지 카메라가 사진 촬영하는 방법을 변경합니다. 이 예는 미리보기 크기만 변경합니다. 더 많은 내용은 카메라 애플리케이션의 소스 코드를 참조하세요.
Kotlin
override fun surfaceChanged(holder: SurfaceHolder, format: Int, w: Int, h: Int) { mCamera?.apply { // Now that the size is known, set up the camera parameters and begin // the preview. parameters?.also { params -> params.setPreviewSize(previewSize.width, previewSize.height) requestLayout() parameters = params } // Important: Call startPreview() to start updating the preview surface. // Preview must be started before you can take a picture. startPreview() } }
자바
@Override public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { // Now that the size is known, set up the camera parameters and begin // the preview. Camera.Parameters parameters = mCamera.getParameters(); parameters.setPreviewSize(previewSize.width, previewSize.height); requestLayout(); mCamera.setParameters(parameters); // Important: Call startPreview() to start updating the preview surface. // Preview must be started before you can take a picture. mCamera.startPreview(); }
미리보기 방향 설정
카메라 센서의 자연스러운 방향은 가로 모드이기 때문에 대부분의 카메라 애플리케이션은 화면을 가로 모드로 잠급니다. 기기의 방향은 EXIF 헤더에 기록되므로 이 설정 때문에 세로 모드 사진을 촬영할 수 없는 것은 아닙니다. setCameraDisplayOrientation()
메서드를 사용하면 이미지가 저장되는 방법에 영향을 주지 않고 미리보기 표시 방법을 변경할 수 있습니다. 그러나, API 레벨 14 이전의 Android에서는 방향을 변경하기 전에 미리보기를 중지하고 그런 다음 미리보기를 다시 시작해야 합니다.
사진 촬영
미리보기가 시작되면 Camera.takePicture()
메서드를 사용하여 사진을 촬영하세요. Camera.PictureCallback
과 Camera.ShutterCallback
개체를 만들고 Camera.takePicture()
로 전달할 수 있습니다.
연속해서 이미지를 가져오려면 onPreviewFrame()
을 구현한 Camera.PreviewCallback
을 만들면 됩니다. 연속한 이미지 사이에서 선택된 미리보기 프레임만 캡처하거나 지연된 작업을 설정하여 takePicture()
를 호출할 수 있습니다.
미리보기 재시작
사진을 촬영한 후에는 사용자가 다른 사진을 촬영하기 전에 미리보기를 재시작해야 합니다. 이 예에서는 셔터 버튼을 오버로드하여 미리보기를 재시작합니다.
Kotlin
fun onClick(v: View) { previewState = if (previewState == K_STATE_FROZEN) { camera?.startPreview() K_STATE_PREVIEW } else { camera?.takePicture(null, rawCallback, null) K_STATE_BUSY } shutterBtnConfig() }
자바
@Override public void onClick(View v) { switch(previewState) { case K_STATE_FROZEN: camera.startPreview(); previewState = K_STATE_PREVIEW; break; default: camera.takePicture( null, rawCallback, null); previewState = K_STATE_BUSY; } // switch shutterBtnConfig(); }
미리보기 중지 및 카메라 해제
애플리케이션에서 카메라 사용을 완료했다면 이제 정리할 시간입니다. 특히, Camera
개체를 해제해야 합니다. 그러지 않으면 다른 애플리케이션 및 자체 애플리케이션의 새 인스턴스가 비정상 종료될 수 있습니다.
언제 미리보기를 중지하고 카메라를 해제해야 할까요? Preview
클래스의 다음 메서드와 같이 미리보기 표면을 없애면 미리보기를 중지하고 카메라를 해제할 때가 되었다는 것을 알릴 수 있습니다.
Kotlin
override fun surfaceDestroyed(holder: SurfaceHolder) { // Surface will be destroyed when we return, so stop the preview. // Call stopPreview() to stop updating the preview surface. mCamera?.stopPreview() } /** * When this function returns, mCamera will be null. */ private fun stopPreviewAndFreeCamera() { mCamera?.apply { // Call stopPreview() to stop updating the preview surface. stopPreview() // Important: Call release() to release the camera for use by other // applications. Applications should release the camera immediately // during onPause() and re-open() it during onResume()). release() mCamera = null } }
자바
@Override public void surfaceDestroyed(SurfaceHolder holder) { // Surface will be destroyed when we return, so stop the preview. if (mCamera != null) { // Call stopPreview() to stop updating the preview surface. mCamera.stopPreview(); } } /** * When this function returns, mCamera will be null. */ private void stopPreviewAndFreeCamera() { if (mCamera != null) { // Call stopPreview() to stop updating the preview surface. mCamera.stopPreview(); // Important: Call release() to release the camera for use by other // applications. Applications should release the camera immediately // during onPause() and re-open() it during onResume()). mCamera.release(); mCamera = null; } }
이 과정의 앞부분에서, 이 절차는 setCamera()
메서드의 일부였으므로 카메라 초기화는 항상 미리보기 중지로 시작합니다.