창 인셋 설정

앱이 콘텐츠를 그리는 위치를 완전히 제어하도록 허용하려면 다음 설정 단계를 따르세요. 이 단계를 따르지 않으면 앱이 시스템 UI 뒤에 검은색이나 단색을 그리거나 소프트웨어 키보드와 동기화되지 않을 수 있습니다.

  1. Android 15 이상에서 더 넓은 화면을 적용하려면 Android 15 (API 수준 35) 이상을 타겟팅하세요. 앱이 시스템 UI 뒤에 표시됩니다. 인셋을 처리하여 앱의 UI를 조정할 수 있습니다.
  2. 원하는 경우 Activity.onCreate()에서 enableEdgeToEdge()를 호출하여 이전 Android 버전에서 앱이 더 넓은 화면을 사용할 수 있도록 합니다.
  3. 활동의 AndroidManifest.xml 항목에서 android:windowSoftInputMode="adjustResize"을 설정합니다. 이 설정을 사용하면 앱이 소프트웨어 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와 겹치지 않지만, 앱이 시스템 UI 뒤에 그려져 더 넓은 화면 효과를 내지는 않습니다. 전체 창을 최대한 활용하려면 화면별 또는 구성요소별로 인셋이 적용되는 위치를 미세 조정해야 합니다.

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

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

패딩 수정자

Modifier.windowInsetsPadding(windowInsets: WindowInsets)는 지정된 창 인셋을 패딩으로 적용하여 Modifier.padding과 똑같이 작동합니다. 예를 들어 Modifier.windowInsetsPadding(WindowInsets.safeDrawing)는 안전한 그리기 인셋을 4면 모두에 패딩으로 적용합니다.

가장 일반적인 인셋 유형을 위한 여러 내장 유틸리티 메서드도 있습니다. 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
            )
        )
    }
}

인셋 소비

인셋 패딩 수정자 (windowInsetsPaddingsafeDrawingPadding과 같은 도우미)는 패딩으로 적용된 인셋 부분을 자동으로 사용합니다. 컴포지션 트리를 더 깊이 살펴보면 중첩된 인셋 패딩 수정자와 인셋 크기 수정자는 인셋의 일부가 이미 외부 인셋 패딩 수정자에 의해 사용되었음을 알고 있으며, 인셋의 동일한 부분을 두 번 이상 사용하여 너무 많은 여백이 생기는 것을 방지합니다.

인셋 크기 수정자는 인셋이 이미 사용된 경우 인셋의 동일한 부분을 두 번 이상 사용하지 않습니다. 하지만 크기를 직접 변경하므로 인셋 자체는 사용하지 않습니다.

따라서 패딩 수정자를 중첩하면 각 컴포저블에 적용되는 패딩 양이 자동으로 변경됩니다.

이전과 동일한 LazyColumn 예시를 살펴보면 LazyColumn의 크기가 imePadding 수정자에 의해 조정되고 있습니다. LazyColumn 내에서 마지막 항목은 시스템 표시줄 하단의 높이로 크기가 조정됩니다.

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

IME가 닫히면 IME에 높이가 없으므로 imePadding() 수정자는 패딩을 적용하지 않습니다. imePadding() 수정자는 패딩을 적용하지 않으므로 인셋이 사용되지 않으며 Spacer의 높이는 시스템 표시줄의 하단 크기가 됩니다.

IME가 열리면 IME 인셋이 IME 크기에 맞게 애니메이션으로 표시되고 imePadding() 수정자가 IME가 열릴 때 LazyColumn의 크기를 조절하기 위해 하단 패딩을 적용하기 시작합니다. imePadding() 수정자가 하단 패딩을 적용하기 시작하면 해당 양의 인셋도 사용하기 시작합니다. 따라서 시스템 표시줄의 간격 중 일부가 이미 imePadding() 수정자에 의해 적용되었으므로 Spacer의 높이가 감소하기 시작합니다. imePadding() 수정자가 시스템 표시줄보다 큰 하단 패딩을 적용하면 Spacer의 높이는 0입니다.

IME가 닫히면 변경사항이 반대로 적용됩니다. imePadding()이 시스템 표시줄의 하단보다 적게 적용되면 Spacer이 높이 0에서 확장되기 시작하여 IME가 완전히 애니메이션으로 표시되지 않을 때까지 Spacer이 시스템 표시줄의 하단 높이와 일치합니다.

그림 2. TextField를 사용하는 더 넓은 지연 로드 열

이 동작은 모든 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 또는 고정 높이 스페이서)에 의해 패딩이나 간격이 제공되는 경우 자녀에게 알리는 데 유용합니다.

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는 인셋을 관리하는 기본 플랫폼 API를 사용합니다. 이 플랫폼 동작으로 인해 인셋은 Jetpack Compose 단계와 특별한 관계를 갖습니다.

인셋 값은 컴포지션 단계 , 레이아웃 단계 에 업데이트됩니다. 즉, 컴포지션에서 인셋 값을 읽으면 일반적으로 인셋 값이 한 프레임 늦게 사용됩니다. 이 페이지에 설명된 내장 수정자는 레이아웃 단계까지 인셋 값의 사용을 지연하도록 빌드되어 인셋 값이 업데이트되는 것과 동일한 프레임에서 사용되도록 합니다.