시스템 상태 표시줄과 UI 조정

Wear OS 7 (API 37)까지는 플랫폼이 애플리케이션 창 위에 시스템 상태 표시줄을 렌더링하지 않았습니다. 앱이 전체 화면을 소유하고 상단 (12시) 위치에 콘텐츠 (예: 인앱 TimeText 시계, 맞춤 곡선 헤더, 상단 컨트롤)를 그릴 수 있었습니다.

Wear OS 8부터(현재 Wear OS 7.1 베타 프리뷰 에뮬레이터(API 37.2-beta1)에서 초기 프리뷰 테스트 가능) 시스템은 애플리케이션 창 위 12시 방향에 상태 표시줄을 렌더링하여 시간과 상태 표시기를 표시합니다. 시스템 상태 표시줄과 겹치지 않도록 앱은 자체 시계 (TimeText) 렌더링을 중지하고 상단 상태 표시줄 창 인셋을 준수해야 합니다 (Wear Compose 1.8부터 AppScaffold 및 ScreenScaffold에서 자동으로 처리됨). 또는 전체 화면 몰입형 환경이 필요한 경우 시스템 상태 표시줄을 숨겨야 합니다.

Wear OS 8의 시스템 상태 표시줄
그림 1. Wear OS 8의 시스템 상태 표시줄

주요 이점은 다음과 같습니다.

  • 시각적 일관성: 시스템과 모든 앱에서 일관된 시계 서체, 배치, 백플레이트
  • 시스템 상태 표시기: 플랫폼에서 여러 표시 경로에 상태 아이콘(예: 활성 마이크 및 위치 표시기, 연결 상태, 배터리 정보)과 시간을 표시할 수 있습니다. 우선순위에 따라 한 번에 하나의 아이콘만 표시됩니다.
  • 자동 스크롤 및 전환 조정: Wear OS 1.8용 Jetpack Compose와 페어링하면 사용자가 콘텐츠를 스크롤할 때 시스템 상태 표시줄이 자동으로 숨겨지고 잠시 (2초) 유휴 지연 후 다시 표시됩니다.

앱 설정

시작하려면 다음 기본 요건 단계를 완료하세요.

  1. Android 스튜디오 프리뷰 (Canary)를 설치합니다.
  2. SDK Manager에서 Wear OS 7.1 프리뷰 에뮬레이터 시스템 이미지(API 37.2-beta1)를 다운로드합니다. 이 버전에서는 Wear OS 8 SDK에 앞서 Wear OS 8 시스템 상태 표시줄을 사전 체험할 수 있습니다.
  3. 기기 관리도구에서 API 37.2-beta1 이미지를 사용하여 Wear OS 가상 기기를 만들고 실행합니다.

앱에서 시스템 상태 표시줄을 지원하려면 앱의 targetSdk 버전을 35 이상으로 업데이트하세요. 플랫폼은 기존 앱의 하위 호환성을 유지하기 위해 API 35 이상을 타겟팅하는 앱에만 시스템 상태 표시줄을 활성화합니다. 또한 다음 코드 스니펫과 같이 Wear Compose 종속 항목을 최신 알파 버전(현재 1.8.0-alpha02)으로 업데이트합니다. 앱에서 이전 버전의 Wear Compose를 사용하는 경우 Wear Compose Material 3 이전 기술을 사용하여 이전을 용이하게 할 수 있습니다.

android {
    defaultConfig {
        targetSdk = 35
    }
}

dependencies {
    implementation("androidx.wear.compose:compose-foundation:1.8.0-alpha02")
    implementation("androidx.wear.compose:compose-material3:1.8.0-alpha02")
}

시스템 상태 표시줄 사용

Wear Compose Material 3 1.8.0의 최신 알파 버전(현재 1.8.0-alpha02)에서는 라이브러리가 com.google.wear.ENABLE_GLOBAL_STATUS_BAR <meta-data> 속성을 AndroidManifest.xml에 병합하여 앱을 자동으로 선택합니다. 런타임 시 AppScaffold 및 ScreenScaffold는 기기에서 시스템 상태 표시줄이 사용 설정되어 있는지, 이 매니페스트 선택 속성이 true로 설정되어 있는지 확인합니다.

  • 미리보기 에뮬레이터/Wear OS 8 이상 (선택한 경우): AppScaffold는 내부 TimeText를 억제하여 시스템 상태 표시줄이 중복 없이 12시 위치에 깔끔하게 렌더링됩니다. ScreenScaffold는 상태 표시줄 인셋 경계를 고려하여 contentPadding를 다시 계산하고 목록이 스크롤될 때 상태 표시줄을 숨기거나 표시하도록 조정합니다.
  • Wear OS 7 이하 기기 (또는 선택하지 않은 경우): AppScaffold는 자동으로 표준 인앱 TimeText를 렌더링하도록 대체되어 단일 코드베이스에서 완전한 하위 호환성을 유지합니다.
@Composable
fun GsbSampleApp() {
    // AppScaffold coordinates the system status bar across the app
    // and falls back to rendering TimeText on Wear OS 7 and earlier.
    AppScaffold {
        val columnState = rememberTransformingLazyColumnState()

        // ScreenScaffold includes status bar top inset clearance in
        // contentPadding and hides/shows the status bar during list scrolling.
        ScreenScaffold(scrollState = columnState) { contentPadding ->
            TransformingLazyColumn(
                state = columnState,
                contentPadding = contentPadding,
            ) {
                item {
                    ListHeader { Text("Hello Status Bar") }
                }
                items(count = 15) { index ->
                    Button(onClick = { /* Handle click */ }) {
                        Text("Item #${index + 1}")
                    }
                }
            }
        }
    }
}

샘플을 실행하면 다음과 같은 결과가 나타납니다.

  • 정지 상태 (목록 상단): 시스템 상태 표시줄이 12시 위치에 표시됩니다. 'Hello Status Bar' 헤더는 잘리거나 겹치지 않고 상태 표시줄 인셋 영역 아래에 있습니다.
  • 아래로 스크롤: 목록 항목을 스크롤하면 상태 표시줄이 숨겨지고 목록 항목이 상단 베젤 뒤에서 화면 전체로 스크롤됩니다.
  • 유휴: 스크롤을 중지하고 화면에서 손을 떼면 약 2초 후에 상태 표시줄이 자동으로 다시 표시됩니다.

일반적인 시나리오 및 맞춤설정 처리

다음 섹션에서는 시스템 상태 표시줄을 사용할 때 발생할 수 있는 일반적인 시나리오를 처리하는 방법을 설명합니다.

스크롤되지 않는 (정적) 화면 처리

스크롤 목록이 아닌 정적 레이아웃 (예: Column 또는 Box)을 사용하는 화면에서는 ScreenScaffold에서 제공하는 contentPadding를 루트 레이아웃에 적용하여 상단 UI 요소가 시스템 상태 표시줄과 겹치지 않도록 합니다.

AppScaffold {
    ScreenScaffold { contentPadding ->
        Column(modifier = Modifier.padding(contentPadding)) {
            Text("Static Screen Content")
        }
    }
}

전체 화면 대화형 구성요소 작업

Wear Compose Material 3의 전체 화면 대화형 구성요소(AlertDialog, Dialog, Stepper, TimePicker, DatePicker 포함)는 화면에 표시되는 동안 시스템 상태 표시줄을 자동으로 숨기므로 컨트롤이 원형 디스플레이 상단에 완전히 액세스할 수 있습니다. 닫으면 창 인셋 컨트롤러 API를 사용하여 시스템 상태 표시줄이 자동으로 복원됩니다.

몰입형 모드로 전환

시스템 상태 표시줄과 로컬 TimeText가 모두 숨겨지는 몰입형 모드로 전환하려면 앱이나 화면에 StatusBarSuppression 컴포저블을 추가합니다. 사용자가 잠깐 보는 화면 (확인 오버레이, 대화상자)이나 전체 디스플레이를 사용하는 화면 (전체 화면 진행률 표시기)에 몰입형 모드를 사용하세요.

화면 수준 몰입형 모드를 사용하는 경우 고려사항

앱의 특정 화면만 전체 화면/몰입형이어야 하는 경우 ScreenScaffold의 콘텐츠 내에서 StatusBarSuppression()를 호출합니다. ScreenScaffold는 해당 화면이 활성 상태인 동안 시스템 상태 표시줄을 자동으로 숨기고 (상단 상태 표시줄 인셋 패딩 생략) 다른 화면으로 다시 이동할 때 시스템 상태 표시줄을 복원합니다.

AppScaffold {
    ScreenScaffold() {
        StatusBarSuppression()
        // Full-screen immersive content
    }
}

앱 수준 몰입형 모드를 사용할 시기 고려

전체 애플리케이션 (또는 Activity)이 모든 화면에서 몰입형 전체 화면 환경인 경우 AppScaffold 수준에서 StatusBarSuppression를 사용하고 콘텐츠를 래핑하여 모든 화면에서 시스템 상태 표시줄을 억제합니다.

// Use StatusBarSuppression at AppScaffold level and wrap your content
// to suppress status bar in the entire scaffold.
AppScaffold {
    StatusBarSuppression {
        SwipeDismissableNavHost(...) {
            // Nested screens inherit suppression and 0.dp padding
        }
    }
}

맞춤 시간 텍스트 재정의 표시

Wear OS 8로 이전하는 동안 특정 화면이나 앱 전체에서 맞춤 곡선 헤더를 표시하거나 시스템 상태 표시줄을 일시적으로 선택 해제해야 하는 경우 맞춤 timeText 컴포저블을 전달하면 시스템 상태 표시줄이 표시되지 않습니다.

// Screen-level custom TimeText override:
ScreenScaffold(
    timeText = { TimeText { timeTextCurvedText("Custom Status") } },
) {
    // Content
}

// App-level opt-out using in-app TimeText:
AppScaffold(
    timeText = { TimeText() },
) {
    // Screens
}

뷰를 사용하여 구현 (권장되지 않음)

앱에서 Wear OS용 Jetpack Compose가 아닌 Android 뷰를 사용하는 경우 AndroidManifest.xml를 사용하여 선택합니다. 시스템 상태 표시줄을 자동으로 사용 설정하는 Wear Compose Material 3의 최신 알파 버전과 달리 뷰 기반 앱은 <application> 내에 ENABLE_GLOBAL_STATUS_BAR <meta-data> 태그를 추가하여 명시적으로 선택해야 합니다.

<application ...>
    <meta-data
        android:name="com.google.wear.ENABLE_GLOBAL_STATUS_BAR"
        android:value="true" />
</application>

상단 창 인셋 모니터링

ViewCompat.setOnApplyWindowInsetsListener를 사용하고 windowInsets.getInsets(WindowInsetsCompat.Type.statusBars()) 값을 확인합니다. statusBarInsets.top > 0인 경우 기존 12시 UI를 삭제하고 상단 패딩을 조정합니다. 그렇지 않으면 기존 UI로 대체합니다.

ViewCompat.setOnApplyWindowInsetsListener(rootView) { view, windowInsets ->
    val statusBarInsets = windowInsets.getInsets(
        WindowInsetsCompat.Type.statusBars()
    )

    if (statusBarInsets.top > 0) {
        // status bar is visible: remove legacy 12 o'clock UI and apply top inset padding
        view.updatePadding(top = statusBarInsets.top)
    } else {
        // status bar is not visible: fall back to legacy UI and padding
    }

    WindowInsetsCompat.CONSUMED
}

프로그래매틱 방식으로 상태 표시줄 숨기기 및 표시

Compose의 ScreenScaffold와 달리 뷰 기반 앱은 WindowInsetsControllerCompat를 사용하여 시스템 상태 표시줄을 명시적으로 숨기거나 표시해야 합니다 (예: 전체 화면 몰입형 뷰로 전환하거나 스크롤 동작을 조정할 때).

val insetsController = WindowCompat.getInsetsController(window, window.decorView)

// Hide the status bar
insetsController.hide(WindowInsetsCompat.Type.statusBars())

// Show the status bar
insetsController.show(WindowInsetsCompat.Type.statusBars())

앱 테스트

Wear OS 7.1 베타 프리뷰 에뮬레이터 (API 37.2-beta1)에서 앱을 테스트할 때 다음을 확인하세요.

  • 시계가 한 번만 표시됨: 시스템 상태 표시줄 시간만 12시 위치에 표시되고 기존 인앱 TimeText 및 앱 콘텐츠가 겹치지 않는지 확인합니다.
  • 상단 패딩: 화면 제목, 헤더, 상단 버튼이 상태 표시줄 아래에 표시되는지 확인합니다.
  • 전체 화면 오버레이 및 대화상자: 대화상자, 선택 도구, 스테퍼, 맞춤 전체 화면 활동이 열릴 때 상태 표시줄을 숨기고 닫힐 때 복원하는지 확인합니다.