본문 바로가기
프로젝트/협업 메신저 앱

핀치 줌 4배를 100배로 올리며 손본 안드로이드 렌더러 크기와 툴바 여백

by iwbap 2026. 10. 17.
728x90
반응형

협업 메신저 앱의 원격 화면 보기에서 핀치 줌 상한을 4배에서 100배로 올렸다. 그 과정에서 확대된 크기대로 렌더러 뷰를 배치하는 구조와 툴바 높이가 빠진 여백 계산도 손봤다. 숫자 하나를 바꾸며 함께 고친 배치 계산을 정리했다.

휴대폰 화면에 원격 PC 화면이 100배로 확대되어 표시되고 아래에 툴바가 떠 있는 모습과 핀치 줌 4배를 100배로 올리며 렌더러 크기를 묶었다는 문구를 담은 대표 이미지
핀치 줌 상한 숫자 하나를 바꾸며 함께 손본 렌더러 크기와 툴바 여백

4배 제한을 100배로 바꾸면 끝일 줄 알았다

안드로이드 앱에서 원격 PC 화면은 두 손가락으로 확대할 수 있었다. 기존에는 맞춤 배율의 4배에서 멈추도록 coerceIn(1f, 4f)로 제한했다.

이번에는 상한을 100배로 높이고 상수로 분리했다. 다른 원격 데스크톱 앱도 100배를 쓴다는 설명을 코드 주석에 적었지만, 그 수치를 별도로 검증하지는 않았다.

아래 코드는 커밋에서 필요한 줄만 뽑아 간략화한 예시다.

// 수정 전: 맞춤 배율의 4배까지
val nextZoom = (gestureZoom * scale).coerceIn(1f, 4f)

// 수정 후: 줌 범위를 상수로 관리
const val MAX_ZOOM = 100f
fun clampZoom(zoom: Float) = zoom.coerceIn(1f, MAX_ZOOM)

확대한 만큼 렌더러 뷰가 커지는 구조였다

기존 배치 함수는 확대 후 계산한 크기를 그대로 렌더러 뷰의 layout()에 적용했다. 줌 상한만 올리면 렌더러 뷰도 그 배율만큼 커지는 구조였다.

이대로라면 가로 1,000px에 맞춘 화면을 100배 확대할 때 가로 10만 px짜리 뷰가 되는 셈이었다. 실제 장애가 난 것은 아니지만 SurfaceViewRenderer의 배치 크기를 제한할 필요가 있다고 판단했다.

그래서 렌더러 한 변의 상한을 8,192px로 정했다. 코드 주석에는 흔한 안드로이드 서피스 한계 안에 드는 값이라고만 적었다. 모든 기기에서 안전하다고 확인한 수치는 아니고, 실제 한계는 기기마다 다를 수 있다.

수정 전에는 핀치 줌 상한이 4배이고 렌더러 뷰의 배치 크기가 확대된 그림 크기 그대로 커져서 상한만 100배로 올리면 1,000px 무대의 뷰가 가로 100,000px가 되는 구조였고, 수정 후에는 최대 줌 100배와 렌더러 한 변 상한 8,192px를 두고 상한을 넘은 비율만큼 뷰를 줄인 뒤 scale로 키우는 구조를 비교한 도식

뷰 크기를 제한하고 나머지는 scale로 늘렸다

그려야 할 크기가 상한을 넘으면 초과 비율 over만큼 뷰를 줄여 배치했다. 화면에 보이는 크기는 scaleX와 scaleY에 over를 적용해 유지했다.

배치 계산 부분도 줄바꿈과 주석을 덧붙여 간략화한 예시로 옮겼다.

// 한 변이 8,192px을 넘는 비율
val over = max(
    drawnWidth / MAX_RENDERER_EDGE,
    drawnHeight / MAX_RENDERER_EDGE
)

// 상한 안쪽이면 기존 배치 유지
if (over <= 1f) return RenderPlacement(
    at.x, at.y, drawnWidth, drawnHeight, 1f, 0f, 0f
)

// 상한을 넘으면 뷰 배치 크기를 줄임
return RenderPlacement(
    0f, 0f, drawnWidth / over, drawnHeight / over,
    over, at.x, at.y
)

위치는 layout() 대신 translationX, translationY로 옮겼다. 확대 기준점(pivot)도 왼쪽 위 (0, 0)으로 고정했다. 기준점이 가운데로 남아 있으면 scale을 줄 때 그림이 엉뚱한 방향으로 밀리기 때문이다.

휴대폰 화면 무대의 왼쪽 위 (0, 0)에 8,192×4,096px로 줄인 렌더러 뷰를 놓고 기준점을 (0, 0)으로 고정한 뒤 scale을 over 값으로, translation을 원래 왼쪽 위 좌표로 주어 화면에서는 100배 크기로 보이면서 같은 영상 좌표가 같은 화면 좌표에 찍히는 배치 순서를 나타낸 도식

예를 들어 1,000×500px 화면을 100배 확대하면 계산상 크기는 100,000×50,000px이다. 이때 over는 약 12.2로, 뷰를 8,192×4,096px로 배치한다. 2배처럼 상한을 넘지 않으면 scale 1, translation 0으로 예전처럼 layout()만으로 배치한다.

아래 툴바 높이도 가림 여백에 포함했다

기존 bottomInset에는 키보드 높이만 반영했다. 키보드 위에 떠 있는 툴바 행의 높이는 빠져 있어 맞춤 배율에서는 화면을 그만큼 위로 옮길 수 없었다.

이를 고치려고 툴바의 실제 높이를 측정해 키보드 높이와 합산했다. 가림 여백이 바뀔 때는 레이아웃을 다시 요청하도록 수정했다.

  • Modifier.onSizeChanged로 툴바 높이(px)를 받는다.
  • bottomCover()로 키보드와 툴바 높이를 더하되 음수는 0으로 처리한다.
  • bottomInset 변경 시 requestLayout()을 호출한다.

이렇게 하면 확대하지 않은 상태에서도 화면을 툴바 높이만큼 위로 올릴 수 있다. 테스트로 보면 툴바 180px이면 최대 180px, 키보드 400px까지 올라오면 580px까지 올라간다. 툴바가 없으면 예전처럼 움직이지 않는다.

수정 전에는 아래 가림 여백에 키보드 높이만 들어가 맞춤 배율에서 툴바 뒤에 가린 원격 화면 아래쪽을 볼 수 없었고, 수정 후에는 툴바 높이를 재서 키보드 높이와 더해 툴바 180px이면 180px, 키보드 400px까지 더하면 580px만큼 화면을 위로 올릴 수 있게 된 휴대폰 화면 비교 도식

다섯 개의 단위 테스트로 계산 조건을 남겼다

배치 계산을 별도로 분리한 덕분에 화면 없이 검사할 단위 테스트를 작성했다. 이번 커밋에는 다섯 개가 포함됐지만 실행 결과는 기록돼 있지 않았다.

테스트검사하는 조건
툴바 높이 합산400+180=580px, 음수는 0
맞춤 배율 이동툴바 180px일 때 위로 180px 이동, 0이면 이동 불가
줌 범위clampZoom(300)=100, 최솟값 1
100배 배치뷰 각 변 8,192px 이하, 좌표 매핑 유지
2배 배치scale 1, 기존 레이아웃 유지

특히 100배에서 뷰 크기를 줄여도 같은 영상 좌표가 같은 화면 좌표에 놓이는지 검사하도록 했다. 다만 이는 계산을 확인하는 테스트이며 실기기 화질이나 터치 정확도까지 검증한 결과는 아니다.

이번에 배운 것

  • 줌 상한을 바꿀 때는 그 값이 쓰이는 배치 계산도 확인해야 했다.
  • 뷰의 레이아웃 크기와 화면상의 확대 배율을 분리할 수 있었다.
  • 떠 있는 툴바도 가림 영역에 포함해 이동 범위를 계산해야 했다.

100배에서의 실제 화질, 성능, 터치 위치는 아직 기기에서 따로 확인하지 않았다. 다음에는 그 부분을 실기기로 확인할 생각이다. 관련 작업은 멀티 모니터 원격 뷰어 창 구조를 정리한 글에 남겼다. 확대 기준점과 배율 속성은 Android View 공식 문서를 참고했다.

728x90
반응형