이 글의 원문은 여기서 확인하실 수 있습니다.
그동안 Android 기기는 크기, 형태, 디스플레이를 비롯한 여러 특징이 점차 다양해지는 방향으로 발전해 왔지만, 그 와중에도 휴대폰 사진 촬영은 Android 기기 초창기부터 가장 핵심적인 사용 사례에 속했습니다. 카메라의 기능은 지금도 여전히 휴대폰을 구매할 때 가장 중요한 요인으로 고려됩니다.
개발자 여러분은 앱에서 카메라 기능을 활용할 수 있도록 Android Camera Framework를 채택하실 것입니다. 가장 먼저 구현할 사용 사례인 미리보기는 카메라 센서의 출력을 화면에 표시합니다.
개발을 계속 진행하면서 기기 화면과 같은 크기의 영역을 이용하여 CaptureSession을 생성하실 텐데, 화면 비율이 카메라 센서 출력과 동일하고 기기가 처음 그대로 세로 방향을 유지하는 한 모든 것이 정상적으로 작동할 것입니다.
하지만 창 크기를 조정하거나, 기기를 펼치거나, 디스플레이 또는 화면 방향을 바꾸면 어떻게 될까요? 미리보기가 한쪽 방향으로 늘어나거나, 거꾸로 뒤집히거나, 이상하게 회전되어 보이는 경우가 대부분입니다. 멀티 윈도우 환경에서는 앱이 비정상 종료될 수도 있습니다.
왜 이런 문제가 발생하는 걸까요? CaptureSession을 생성할 때 당연히 여기고 넘어가는 점이 있기 때문입니다.
그동안에는 앱을 수명 주기 내내 하나의 동일한 창에서만 보는 편이었지만, 폴더블 기기 같은 새로운 폼 팩터, 멀티 윈도우나 멀티 디스플레이 같은 새로운 디스플레이 모드가 등장한 지금도 그럴 것이라고 생각해서는 안 됩니다.
따라서 다양한 폼 팩터를 대상으로 하는 앱 개발 시 어떤 요소를 가장 우선적으로 고려해야 하는지 살펴볼 필요가 있습니다.
다양한 폼 팩터에서 작동할 앱을 개발할 때 간과하기 쉬운 점을 정리해 보자면 다음과 같습니다.
- 앱을 세로 방향 창에서만 사용한다? Android 13에서도 방향 고정 요청은 가능하지만, 이제 기기 제조사들은 자사의 선호 방향을 고수하고자 앱의 요청을 무시할 수 있습니다.
- 앱의 화면 크기나 비율이 고정되어 있다? resizableActivity = "false"로 설정하더라도 대화면(>=600dp)에서 멀티 윈도우 모드로 앱을 계속 사용할 수 있습니다.
- 화면 방향과 카메라의 관계가 고정되어 있다? Android 호환성 정의 문서에 카메라 이미지 센서는 "카메라의 긴 치수와 화면의 긴 치수가 일치하게 정렬하도록 방향이 조정되어야 한다"라고 명시되어 있습니다. API 레벨 32부터 폴더블 기기에서 방향을 쿼리하는 카메라 클라이언트는 기기/폴드 상태에 따라 동적으로 바뀌는 값을 받을 수 있습니다.
- 인셋의 크기가 변하지 않는다? 새 작업 표시줄은 애플리케이션에 인셋으로 보고되며, 동작 탐색과 함께 사용 시 숨겨진 채 동적으로 표시될 수 있습니다.
- 우리 앱이 카메라 액세스 권한을 독점한다? 여러분의 앱이 멀티 윈도우 상태에 있는 동안 다른 앱이 카메라나 마이크 같은 공유 리소스에 액세스할 수 있습니다.
CameraX가 위 사례를 대부분 해결할 수 있지만, Camera2 API를 사용하여 어느 상황에서든 작동하는 미리보기를 구현하는 것은 어려운 작업이 될 수 있습니다. 이에 대해서는 카메라 앱에서 크기 조절이 가능한 노출 영역 지원 Codelab 문서에서 확인하실 수 있습니다.
어떤 간단한 구성요소가 이런 세부 사항들을 처리해 줘서 개발자 여러분이 특정 앱 로직에만 집중할 수 있다면 좋지 않을까요?
당연히 그렇습니다.
CameraViewfinder 소개
CameraViewfinder는 Jetpack 라이브러리의 새로운 아티팩트로, 카메라 미리보기를 쉽고 빠르게 구현할 수 있도록 도와줍니다. CameraViewfinder는 내부적으로 TextureView 또는 SurfaceView를 사용해 카메라 피드를 표시하며, 뷰파인더를 올바르게 표시하는 데 필요한 변환을 적용합니다. 여기에는 화면 비율, 배율, 회전 등을 수정하는 작업이 포함됩니다. CameraViewfinder는 기존 Camera2 코드베이스와 완벽하게 호환되며 여러 기기에서 계속 테스트를 거칩니다.
이제 사용 방법을 알아보겠습니다.
먼저 앱 수준 build.gradle 파일에 종속성을 추가합니다.
implementation "androidx.camera:camera-viewfinder:1.3.0-alpha01" |
프로젝트를 동기화합니다. 이제 CameraViewfinder를 다른 View로 직접 사용할 수 있을 것입니다. 예를 들어 레이아웃 파일에 이를 추가할 수 있습니다.
<androidx.camera.viewfinder.CameraViewfinder android:id="@+id/view_finder" app:scaleType="fitCenter" app:implementationMode="performance" android:layout_width="match_parent" android:layout_height="match_parent"/> |
보시다시피 CameraViewfinder에는 PreviewView에서 사용할 수 있는 것과 똑같은 컨트롤이 있으므로 다양한 구현 모드와 배율 유형을 선택할 수 있습니다.
이 구성요소가 이제 레이아웃의 일부가 된 상황에서 CameraCaptureSession을 생성할 수도 있지만, TextureView 또는 SurfaceView를 대상 영역으로 제공하는 대신 requestSurfaceAsync()의 결과를 사용할 것을 권장합니다.
fun startCamera(){ val previewResolution = Size(width, height) val viewfinderSurfaceRequest = ViewfinderSurfaceRequest(previewResolution, characteristics) val surfaceListenableFuture = cameraViewfinder.requestSurfaceAsync(viewfinderSurfaceRequest)
Futures.addCallback(surfaceListenableFuture, object : FutureCallback<Surface> { override fun onSuccess(surface: Surface) { //create a CaptureSession using this surface as usual } override fun onFailure(t: Throwable) { /* something went wrong */} }, ContextCompat.getMainExecutor(context)) } |
덧붙임: 폴더블 기기에 최적화된 레이아웃
CameraViewFinder는 영역 크기 조절, 구성 변경, 회전, 멀티 윈도우 모드에 바로 사용할 수 있고, 수많은 폴더블 기기에서 테스트를 거친 상태입니다.
하지만 폴더블 및 이중 화면 기기에 최적화된 레이아웃을 구현하려는 경우에는 CameraViewFinder를 Jetpack WindowManager 라이브러리와 결합하여 사용자에게 꼭 맞는 환경을 제공할 수도 있습니다.
예를 들어 화면 중앙에 힌지가 있거나 기기가 '책' 또는 '탁상' 모드일 때 전체 화면 미리보기를 표시하지 않도록 선택할 수 있습니다. 이때 화면 한쪽에는 뷰파인더를 두고 다른 쪽에는 컨트롤을 두거나, 화면의 일부 영역에 최근 찍은 사진을 띄울 수 있습니다. 상상하는 만큼 마음껏 구현됩니다.
샘플 앱은 이미 폴더블 기기에 최적화되어 있으며, 자세 변화를 처리하는 코드는 여기에서 찾을 수 있습니다. 지금 확인해 보세요!