Compose의 창 인셋

Android 플랫폼은 다음과 같은 시스템 UI 그리기를 담당합니다. 탐색 메뉴가 있습니다. 이 시스템 UI는 앱에 액세스할 수 있습니다.

WindowInsets는 시스템에 관한 정보를 제공합니다. 앱이 올바른 영역에 그리고 UI가 가려지지 않도록 하는 UI 시스템 UI에 의해 결정됩니다.

더 넓은 화면으로 시스템 표시줄 뒤에 그리기
그림 1. 더 넓은 화면으로 시스템 표시줄 뒤에 그려짐

Android 14 (API 수준 34) 이하에서는 앱의 UI가 아래에 그려지지 않습니다. 기본적으로 시스템 표시줄 및 디스플레이 컷아웃에 있습니다.

Android 15 (API 수준 35) 이상에서는 앱이 시스템 아래에 그려집니다. 디스플레이 컷아웃에 적용할 수 있습니다. 그 결과 원활한 사용자 환경을 구현하고, 앱에서 제공하는 사용할 수 있는 창 공간을 의미합니다.

시스템 UI 뒤에 콘텐츠를 표시하는 것을 더 넓은 화면 표시라고 합니다. 이 알림의 페이지에서 다양한 유형의 인셋, 더 넓은 화면으로 전환하는 방법, 인셋 API를 사용하여 UI에 애니메이션을 적용하고 앱 콘텐츠가 제대로 표시되는지 확인하는 방법 시스템 UI 요소로 가리지 않아야 합니다.

인셋 기초

앱을 더 넓은 화면으로 전환할 때 중요한 콘텐츠와 시스템 UI에 의해 가려지지 않습니다. 예를 들어 버튼이 배치되어 있으면 사용자가 클릭하지 못할 수 있습니다.

시스템 UI의 크기와 배치 위치에 관한 정보가 명시됨 인셋을 통해 전달됩니다.

시스템 UI의 각 부분에는 시스템 UI를 설명하는 크기 및 배치 위치를 결정합니다. 예를 들어, 상태 표시줄 삽입은 상태 표시줄의 정확한 위치를 나타내는 반면, 탐색 메뉴 인셋은 크기와 위치를 지정해야 합니다. 각 인셋 유형은 네 가지 픽셀 크기: 상단, 왼쪽, 오른쪽 및 하단 이 측정기준은 시스템 UI가 앱 창의 상응하는 측면에서 확장되어야 합니다. 회피하다 따라서 앱 UI는 해당 유형의 시스템 UI와 겹쳐서 삽입해야 합니다. 있습니다.

다음과 같은 내장 Android 인셋 유형은 WindowInsets를 통해 사용할 수 있습니다.

WindowInsets.statusBars

상태 표시줄을 설명하는 삽입 다음은 알림 아이콘과 기타 표시기가 포함된 상단의 시스템 UI 표시줄입니다.

WindowInsets.statusBarsIgnoringVisibility

표시될 때의 상태 표시줄 인셋 몰입형 전체 화면 모드로 전환되어 상태 표시줄이 현재 숨겨져 있는 경우 기본 상태 표시줄 삽입은 비어 있지만 이러한 삽입은 비어 있지 않습니다.

WindowInsets.navigationBars

탐색 메뉴를 설명하는 인셋입니다. 이는 기기의 왼쪽, 오른쪽 또는 하단에 있는 시스템 UI 표시줄로, 작업 표시줄이나 탐색 아이콘을 설명합니다. 이는 사용자가 선호하는 탐색 방법 및 작업 표시줄과의 상호작용에 따라 런타임 시 변경될 수 있습니다.

WindowInsets.navigationBarsIgnoringVisibility

탐색 메뉴가 표시될 때의 탐색 메뉴 인셋 몰입형 전체 화면 모드로 전환되어 탐색 메뉴가 현재 숨겨져 있는 경우 기본 탐색 메뉴 인셋은 비어 있지만 이러한 인셋은 비어 있지 않습니다.

WindowInsets.captionBar

자유 형식 창(상단 제목 표시줄 등)에 있는 경우 시스템 UI 창 장식을 설명하는 인셋입니다.

WindowInsets.captionBarIgnoringVisibility

자막 표시줄이 표시될 때의 인셋입니다. 현재 자막 막대가 숨겨져 있으면 기본 자막 표시줄 삽입은 비어 있지만 이러한 삽입은 비어 있지 않습니다.

WindowInsets.systemBars

상태 표시줄, 탐색 메뉴, 자막 표시줄이 포함된 시스템 표시줄 인셋의 합집합

WindowInsets.systemBarsIgnoringVisibility

표시되는 시스템 표시줄 인셋 몰입형 전체 화면 모드로 전환되어 시스템 표시줄이 현재 숨겨져 있는 경우 기본 시스템 표시줄 삽입은 비어 있지만 이러한 삽입은 비어 있지 않습니다.

WindowInsets.ime

인셋은 소프트웨어 키보드가 차지하는 바닥 부분의 공간을 설명합니다.

WindowInsets.imeAnimationSource

인셋은 현재 키보드 애니메이션 전에 소프트웨어 키보드가 차지한 공간의 양을 설명합니다.

WindowInsets.imeAnimationTarget

인셋은 현재 키보드 애니메이션 이후 소프트웨어 키보드가 차지하게 될 공간의 양을 설명합니다.

WindowInsets.tappableElement

탐색 UI에 관한 자세한 정보를 설명하는 인셋 유형으로, '탭'하는 공간의 양을 제공합니다. 앱이 아니라 시스템에서 처리합니다. 동작 탐색이 있는 투명한 탐색 메뉴의 경우 시스템 탐색 UI를 통해 일부 앱 요소를 탭할 수 있습니다.

WindowInsets.tappableElementIgnoringVisibility

표시되는 시점에 관한 탭 가능 요소 인셋 몰입형 전체 화면 모드로 전환되어 탭 가능 요소가 현재 숨겨져 있는 경우 탭 가능 기본 요소 인셋은 비어 있지만 이러한 인셋은 비어 있지 않습니다.

WindowInsets.systemGestures

시스템이 탐색 동작을 가로챌 인셋의 양을 나타내는 인셋입니다. 앱에서는 Modifier.systemGestureExclusion를 통해 제한된 수의 동작을 처리하도록 수동으로 지정할 수 있습니다.

WindowInsets.mandatorySystemGestures

시스템에서 항상 처리하고 Modifier.systemGestureExclusion를 통해 선택 해제할 수 없는 시스템 동작의 하위 집합입니다.

WindowInsets.displayCutout

디스플레이 컷아웃 (노치 또는 핀홀)과 겹치지 않도록 하기 위해 필요한 간격의 양을 나타내는 인셋

WindowInsets.waterfall

폭포 디스플레이의 곡선 영역을 나타내는 인셋입니다. 워터폴 디스플레이에는 화면 가장자리를 따라 화면이 기기 측면을 따라 래핑되기 시작하는 곡선 영역이 있습니다.

이러한 유형은 세 가지 '안전한' 인셋 유형을 사용하면 콘텐츠가 가려짐:

이러한 '안전' 상태의 인셋 유형은 기본 플랫폼 인셋:

  • WindowInsets.safeDrawing를 사용하여 그려서는 안 되는 콘텐츠를 보호합니다. 시스템 UI 아래에 있습니다. 이것은 인셋의 가장 일반적인 용도입니다. 시스템 UI에 의해 가려지는 콘텐츠 그리기 합니다.
  • WindowInsets.safeGestures을 사용하여 동작으로 콘텐츠를 보호합니다. 이 시스템 동작이 앱 동작 (예: 하단 동작)과 충돌하는 것을 방지합니다. 시트, 캐러셀, 게임에서 사용할 수 있음).
  • 다음 조합으로 WindowInsets.safeContent 사용 WindowInsets.safeDrawingWindowInsets.safeGestures이 콘텐츠에 시각적으로 겹치지 않으며 동작이 겹치지 않아야 합니다.

인셋 설정

앱이 콘텐츠를 그리는 위치를 완전히 제어할 수 있도록 하려면 다음 설정을 따르세요. 학습합니다. 이 단계를 수행하지 않으면 앱에서 배경색의 색상이나 단색을 소프트웨어 키보드와 동기식으로 애니메이션 처리되지 않을 수 있습니다.

  1. SDK 35 이상을 타겟팅하여 Android 15 이상에서 더 넓은 화면에 광고를 적용합니다. 내 앱 시스템 UI 뒤에 표시됩니다. 다음을 처리하여 앱의 UI를 조정할 수 있습니다. 삽입.
  2. 원하는 경우 다음에서 enableEdgeToEdge()를 호출합니다. Activity.onCreate(): 앱이 이전 버전에서 더 넓은 화면에 표시될 수 있습니다. Android 버전
  3. 활동의 android:windowSoftInputMode="adjustResize" 설정: AndroidManifest.xml 항목. 이 설정을 사용하면 앱이 소프트웨어 IME의 인셋으로, 콘텐츠를 패딩하고 배치하는 데 사용할 수 있습니다. IME가 앱에 표시되고 사라질 때 적절하게 적절하게 처리되어야 합니다.

    <!-- in your AndroidManifest.xml file: -->
    <activity
      android:name=".ui.MainActivity"
      android:label="@string/app_name"
      android:windowSoftInputMode="adjustResize"
      android:theme="@style/Theme.MyApplication"
      android:exported="true">
    

Compose API

활동에서 모든 인셋 처리를 제어하면 Compose를 사용할 수 있습니다. 콘텐츠를 가리거나 상호작용 가능한 요소가 가려지지 않도록 하는 API 시스템 UI와 겹치지 않습니다. 또한 이러한 API는 앱의 레이아웃을 인셋 변경사항을 적용할 수 있습니다.

예를 들어, 이것은 콘텐츠에 인셋을 적용하는 가장 기본적인 방법입니다. 있습니다.

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    enableEdgeToEdge()

    setContent {
        Box(Modifier.safeDrawingPadding()) {
            // the rest of the app
        }
    }
}

이 스니펫은 safeDrawing 창 인셋을 앱 콘텐츠 전체를 표시할 수 있습니다. 이렇게 하면 상호작용 가능한 요소가 시스템 UI와 겹치기 때문에 어떤 앱도 뒤에 그려지지 않습니다. 더 넓은 화면 효과를 얻을 수 있습니다. 전체 인셋이 적용되는 위치를 화면별로 세밀하게 조정해야 합니다. 또는 컴포넌트별로 생성할 수 있습니다.

이러한 모든 인셋 유형은 IME 애니메이션으로 자동 애니메이션됩니다. API 21로 백포팅됩니다 더 나아가 이러한 인셋을 사용하는 모든 레이아웃은 인셋 값이 변경되면 자동으로 애니메이션 처리됩니다.

이러한 인셋 유형을 사용하여 컴포저블을 조정하는 기본적인 방법은 두 가지입니다. 레이아웃: 패딩 수정자와 인셋 크기 수정자.

패딩 수정자

Modifier.windowInsetsPadding(windowInsets: WindowInsets)는 창 인셋을 패딩으로 제공하여 Modifier.padding와 같이 작동합니다. 예를 들어 Modifier.windowInsetsPadding(WindowInsets.safeDrawing)는 네 면 모두의 패딩으로.

가장 일반적인 인셋 유형을 위한 여러 개의 내장 유틸리티 메서드도 있습니다. Modifier.safeDrawingPadding()는 다음과 같은 메서드 중 하나입니다. Modifier.windowInsetsPadding(WindowInsets.safeDrawing)입니다. 이와 유사한 다른 인셋 유형의 수정자입니다.

인셋 크기 수정자

다음 수정자는 창 인셋의 크기를 구성요소를 인셋의 크기가 됩니다.

Modifier.windowInsetsStartWidth(windowInsets: WindowInsets)

windowInsets의 시작 측을 너비로 적용합니다 (예: Modifier.width).

Modifier.windowInsetsEndWidth(windowInsets: WindowInsets)

windowInsets의 끝부분을 너비로 적용합니다 (예: Modifier.width).

Modifier.windowInsetsTopHeight(windowInsets: WindowInsets)

windowInsets의 상단을 높이로 적용합니다 (예: Modifier.height).

Modifier.windowInsetsBottomHeight(windowInsets: WindowInsets)

windowInsets의 하단을 높이로 적용합니다 (예: Modifier.height).

이러한 수정자는 Spacer 삽입 공간:

LazyColumn(
    Modifier.imePadding()
) {
    // Other content
    item {
        Spacer(
            Modifier.windowInsetsBottomHeight(
                WindowInsets.systemBars
            )
        )
    }
}

인셋 사용

인셋 패딩 수정자 (windowInsetsPadding 및 다음과 같은 도우미는 safeDrawingPadding)는 인셋의 일부분을 자동으로 소비하고 패딩으로 적용됩니다. 컴포지션 트리를 자세히 살펴보면서 중첩된 인셋 패딩 수정자와 인셋 크기 수정자는 인셋은 이미 외부 인셋 패딩 수정자에 의해 소비되고 있으므로 인셋의 동일한 부분을 두 번 이상 사용하면 여유 공간이 확보됩니다.

또한 인셋 크기 수정자는 인셋의 동일한 부분을 두 번 이상 사용하지 않습니다. 인셋이 이미 소비된 경우. 하지만 Kubernetes가 인셋 자체를 소비하지 않습니다.

따라서 중첩 패딩 수정자는 패딩을 포함해야 합니다.

이전과 동일한 LazyColumn 예를 보면 LazyColumn는 다음과 같습니다. imePadding 수정자로 크기가 조절됩니다. LazyColumn 내에서 마지막 항목은 다음과 같습니다. 시스템 표시줄 하단 높이가 되도록 설정합니다.

LazyColumn(
    Modifier.imePadding()
) {
    // Other content
    item {
        Spacer(
            Modifier.windowInsetsBottomHeight(
                WindowInsets.systemBars
            )
        )
    }
}

IME가 닫히면 imePadding() 수정자는 패딩을 적용하지 않습니다. IME에 높이가 없습니다. imePadding() 수정자는 패딩을 적용하지 않으므로 인셋이 소비되지 않으며 Spacer의 높이는 확인할 수 있습니다

IME가 열리면 IME 인셋이 IME 크기에 맞게 애니메이션 처리되며 imePadding() 수정자가 하단 패딩을 적용하여 LazyColumn를 엽니다. imePadding() 수정자가 적용되기 시작할 때 또한 그만큼의 인셋을 소비하기 시작합니다. 따라서 시스템 간격의 일부로 Spacer의 높이가 감소하기 시작함 막대가 이미 imePadding() 수정자에 의해 적용되었습니다. 일단 imePadding() 수정자가 더 큰 하단 패딩을 적용합니다. Spacer의 높이가 0입니다.

IME가 닫히면 변경사항은 역순으로 발생합니다. SpacerimePadding()가 시스템 표시줄 하단까지, 최종적으로 Spacer가 높이와 일치할 때까지 IME가 완전히 애니메이션되면 시스템 표시줄 하단을 표시합니다.

그림 2. TextField를 사용하는 에지 투 에지 지연 열
를 통해 개인정보처리방침을 정의할 수 있습니다.

이 동작은 모든 Google 계정 간의 windowInsetsPadding 수정자를 포함하며, 몇 가지 다른 수정자의 영향을 받을 수 있음 있습니다.

Modifier.consumeWindowInsets(insets: WindowInsets)는 인셋도 소비함 Modifier.windowInsetsPadding와 같은 방식으로 작동하지만 적용되지 않습니다. 패딩으로 사용됩니다. 인셋과 함께 사용하면 유용합니다. 크기 수정자: 형제에 특정 양의 인셋이 있다는 것을 나타냅니다. 다음과 같습니다.

Column(Modifier.verticalScroll(rememberScrollState())) {
    Spacer(Modifier.windowInsetsTopHeight(WindowInsets.systemBars))

    Column(
        Modifier.consumeWindowInsets(
            WindowInsets.systemBars.only(WindowInsetsSides.Vertical)
        )
    ) {
        // content
        Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.ime))
    }

    Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.systemBars))
}

Modifier.consumeWindowInsets(paddingValues: PaddingValues)WindowInsets 인수가 있는 버전과 비슷하지만 사용할 임의의 PaddingValues입니다. 이는 광고 소재를 패딩이나 간격이 인셋 패딩 수정자(예: 일반 Modifier.padding 또는 고정 높이) 스페이서:

@OptIn(ExperimentalLayoutApi::class)
Column(Modifier.padding(16.dp).consumeWindowInsets(PaddingValues(16.dp))) {
    // content
    Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.ime))
}

사용하지 않고 원시 창 인셋이 필요한 경우에는 WindowInsets 값을 직접 사용하거나 WindowInsets.asPaddingValues()를 사용하여 소비의 영향을 받지 않는 인셋의 PaddingValues을 반환합니다. 하지만 아래 주의사항으로 인해 창 인셋 패딩을 사용하는 것이 좋습니다. 가능한 경우 수정자 및 창 인셋 크기 수정자를 따릅니다.

인셋 및 Jetpack Compose 단계

Compose는 기본 AndroidX 핵심 API를 사용하여 인셋을 업데이트하고 애니메이션 처리합니다. 인셋을 관리하는 기본 플랫폼 API를 사용합니다. 플랫폼 덕분에 삽입은 Jetpack의 구현 단계와 특별한 관계가 있습니다. 작성하기를 참고하세요.

인셋의 값은 컴포지션 단계 이후이지만 레이아웃 단계입니다. 이것은 컴포지션에서 인셋 값을 읽는 것이 는 일반적으로 한 프레임 지연된 인셋 값을 사용합니다. 내장된 이 페이지에서 설명하는 수정자는 인셋 값이 레이아웃 단계까지 사용되도록 하여 동일한 프레임에 광고가 게재됩니다.

WindowInsets를 사용하는 키보드 IME 애니메이션

스크롤 컨테이너에 Modifier.imeNestedScroll()를 적용하여 열고 컨테이너 하단으로 스크롤하면 IME가 자동으로 닫힙니다.

class WindowInsetsExampleActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        WindowCompat.setDecorFitsSystemWindows(window, false)

        setContent {
            MaterialTheme {
                MyScreen()
            }
        }
    }
}

@OptIn(ExperimentalLayoutApi::class)
@Composable
fun MyScreen() {
    Box {
        LazyColumn(
            modifier = Modifier
                .fillMaxSize() // fill the entire window
                .imePadding() // padding for the bottom for the IME
                .imeNestedScroll(), // scroll IME at the bottom
            content = { }
        )
        FloatingActionButton(
            modifier = Modifier
                .align(Alignment.BottomEnd)
                .padding(16.dp) // normal 16dp of padding for FABs
                .navigationBarsPadding() // padding for navigation bar
                .imePadding(), // padding for when IME appears
            onClick = { }
        ) {
            Icon(imageVector = Icons.Filled.Add, contentDescription = "Add")
        }
    }
}

키보드 표시 공간을 위해 UI 요소를 위아래로 스크롤하는 모습을 보여주는 애니메이션
그림 3. IME 애니메이션

Material 3 구성요소의 인셋 지원

사용 편의성을 위해 내장된 Material 3 컴포저블 대부분 (androidx.compose.material3) 앱에 컴포저블이 배치되는 방식에 따라 인셋 자체 처리 를 사용합니다.

인셋 처리 컴포저블

다음은 Android에서 사용할 수 있는 Google에서 제공하는 인셋을 자동으로 처리합니다.

앱 바

콘텐츠 컨테이너

Scaffold

기본적으로 Scaffold 드림 개발자가 사용하고 사용할 수 있도록 인셋을 paddingValues 매개변수로 제공합니다. Scaffold가 콘텐츠에 인셋을 적용하지 않습니다. 이에 대한 책임은 사용자에게 있습니다 예를 들어 Scaffold 내에서 LazyColumn가 있는 이러한 인셋을 사용하는 방법은 다음과 같습니다.

Scaffold { innerPadding ->
    // innerPadding contains inset information for you to use and apply
    LazyColumn(
        // consume insets as scaffold doesn't do it by default
        modifier = Modifier.consumeWindowInsets(innerPadding),
        contentPadding = innerPadding
    ) {
        items(count = 100) {
            Box(
                Modifier
                    .fillMaxWidth()
                    .height(50.dp)
                    .background(colors[it % colors.size])
            )
        }
    }
}

기본 인셋 재정의

컴포저블에 전달된 windowInsets 매개변수를 다음과 같이 변경할 수 있습니다. 컴포저블의 동작을 구성합니다. 이 매개변수는 창 인셋을 대신 적용하거나 빈 인스턴스를 전달하여 사용 중지합니다. WindowInsets(0, 0, 0, 0)

예를 들어 LargeTopAppBar님, windowInsets 매개변수를 빈 인스턴스로 설정합니다.

LargeTopAppBar(
    windowInsets = WindowInsets(0, 0, 0, 0),
    title = {
        Text("Hi")
    }
)

뷰 시스템 인셋과의 상호 운용성

화면에 뷰와 뷰가 모두 있으면 기본 인셋을 재정의해야 할 수 있습니다. 동일한 계층 구조로 Compose 코드 작성 이 경우 어느 것이 인셋을 사용해야 하고, 어떤 것은 인셋을 무시해야 합니다.

예를 들어 가장 바깥쪽 레이아웃이 Android 뷰 레이아웃인 경우 뷰 시스템의 인셋을 소비하고 Compose의 경우 이를 무시합니다. 또는 가장 바깥쪽 레이아웃이 컴포저블인 경우 인셋을 사용하고 그에 따라 AndroidView 컴포저블을 패딩합니다.

기본적으로 각 ComposeViewWindowInsetsCompat 수준 소비 이 기본 동작을 변경하려면 ComposeView.consumeWindowInsets 드림 false에게.

리소스