UI가 창 인셋과 함께 작동하는지 확인

활동에서 모든 인셋 처리를 제어하면 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 예시를 보면 LazyColumnimePadding 수정자를 사용하여 크기가 조절됩니다. 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가 사용됩니다. 이러한 플랫폼 동작으로 인해 인셋은 Jetpack Compose의 단계와 특별한 관계가 있습니다.

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