Skip to content

⚙ [트러블 슈팅] WindowInset 다루기

HahyunKang edited this page Dec 3, 2024 · 1 revision

앱의 이미지 집중 모드 기능 구현을 위해 WIndowInset을 다뤘는데 이를 정리해보고자 한다

🔍 WindowInsets

windowInsets은 앱이 올바른 영역에 그려지고 UI가 시스템 UI로 가려지지 않도록 시스템 ui에 관한 정보를 제공한다. 버튼이 시스템바나 네비게이션 바에 가려지면, 사용자는 버튼을 클릭할 수 없기 때문에, windowInset을 이해하고 활용하는 것은 굉장히 중요하다.

시스템 UI의 각 부분에는 크기와 배치 위치를 설명하는 상응하는 인셋 유형이 있다. 예를 들어 statusBar Inset은 상태 표시줄의 크기와 위치를 제공하는 반면 navigationBar Inset은 탐색 메뉴의 크기와 위치를 제공한다.

공식문서를 보면 WindowInset에는 다양한 유형의 Insets이 있다.

대표적으로 3가지 유형만 살펴보자면,

  • WindowInsets.statusBars : 상태 표시줄을 나타내는 인셋이다. 알림 아이콘, 기타 표시기가 포함된 상단의 시스템 UI 표시줄이다.
  • WindowInsets.navigationBars : 내비게이션 바에 대한 정보를 제공하는 인셋. 내비게이션 바의 크기,위치 등에 대한 정보를 포함한다.
  • WindowInsets.ime : 소프트웨어 키보드에 대한 정보를 제공한다. IME가 나타날 때의 크기, 위치 등에 대한 정보를 포함한다. UI가 소프트웨어 키보드에 의해 가려지지 않도록 조정할 수 있다.

이외에도 여러 inset 유형을 아래 링크에서 확인할 수 있다. https://developer.android.com/develop/ui/compose/layouts/insets?hl=ko#inset-fundamentals

compose에서는 기본적으로 Activity에서 enableEdgeToEdge를 사용하면, Android에서 앱의 UI가 화면의 가장자리까지 확장될 수 있도록 허용하고, 상태 표시줄이나 내비게이션 바와 같은 시스템 ui와 겹치지 않도록 조정할 수 있다.

image

앱의 집중모드 구현을 위해서 systemBar와 navigationBar를 숨겨야하는데, WIndowInset은 해당 시스템 UI의 정보만 포함하는 interface이기 때문에, System Bar를 핸들링할 수 있는 무언가가 필요했다. 다행히 WindowInsetsControllerCompat이 이 기능을 제공해주고 있었다.

🔍 WindowInsetsControllerCompat

WindowInsetsControllerCompat은 androidx.core library에 속해있는 클래스이며, 상태 표시줄이나 내비게이션 바를 숨기거나 표시할 수 있다. WindowInsetsControllerCompat을 사용하면 모든 SDK 버전에 대응할 수 있게 된다. 해당 클래스 내부에 모든 SDK 버전에 대응할 수 있도록 분기처리가 이루어지고 있기 때문이다.

WindowInsetsControllerCompat을 가져오려면 WindowCompat의 getInsetsController 메서드를 사용해야 한다. 이를 위해 파라미터로 window와 view를 넘겨줘야 하는데 compose에서 구현하고 있기 때문에, LocalView.current 로 view를 받아온 후, getActivity를 통해 window에 접근했다. 참고로 window란 화면의 가장 상위 요소이고, 화면 구성에 존재하는 각각의 Frame안에 존재하는 영역, 또는 그 전체로 볼 수 있다. 하나의 화면에 여러개의 Window가 존재할 수 있으며, Window들은 WindowManager가 관리한다.

image

이후, WindowInsetsControllerCompat의 hide 함수를 통해 stautsBar와 navigationBar를 숨길 수 있었다.

image

이를 통해 systembar와 navigation bar가 사라지는 것을 볼 수 있다. 하지만, 의도하는 동작은 아니었다. 안드로이드 갤러리 full screen 모드처럼, swipe했을 때 systembar와 navigation bar가 3초 정도 나오고 다시 사라지는 동작을 원했다.

디행히 이를 지원해주는 옵션이 있었다. systemBarsBehavior 옵션을 통해 application에 의해 시스템바가 사라졌을 경우 동작을 지정할 수 있었다. BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE 옵션을 사용해서 사용자가 화면 내 system bar 근처에서 스와이프 할 때 투명 시스템 바를 띄우고, 3초 정도 이후에 사라지도록 구현할 수 있었다.

image

aaa.mp4

✔️ WindowInset 이용해서 겹치는 현상 해결하기

위의 영상처럼 full screen mode가 잘 구현된 줄 알았으나,,, 한 가지 문제가 더 있었다. systembar가 나타날 때, Scaffold의 icon과 겹치는 문제가 있었다.

image

그렇다면, 이것은 아까 알아봤던, WindowInset으로 해결할 수 있지 않을까? 라는 생각에 일단 Scaffold의 TopAppBar의 속성부터 찾아봤다. TopAppBar의 속성 중 WindowInset이 있었다.

이 속성의 주요 기능은 상태 표시줄과 같은 시스템 UI 요소가 차지하는 공간을 파악하여, TopAppBar가 이 영역과 겹치지 않도록 조정할 수 있다고 한다. 이 속성을 사용하면 뭔가 해결될 수 있을 것 같았다!

그래서 아까 그 공식 문서에 가서 이 문제를 해결할 수 있는 Inset 유형이 어떤 것이 있을지 찾아봤다. 여러 유형 중 statusBarsIgnoringVisibility가 눈에 띄었다.

image

이 옵션은 상태 표시줄의 가시성과 관계없이 항상 상태 표시줄이 차지하는 공간을 반환한다. 즉, 상태 표시줄이 숨겨져 있을 때도 그 크기 정보를 제공햔다. 이것을 사용하면 상태 표시줄이 보일 때 겹쳐지지 않으면서도, 보일 때와 보이지 않을 때 UI의 일관성을 보장할 수 있다!

TopAppBar의 windowInsets 속성을 WindowInsets.statusBarsIgnoringVisibility 로 지정하고 테스트 해본 결과 원하는 대로 동작하는 것을 확인할 수 있었다.

image

Clone this wiki locally