다양한 화면 크기 지원

다양한 화면 크기를 지원하여 최대한 많은 사람들이 앱에 액세스할 수 있도록 합니다. 사용자 수를 늘리는 것입니다.

가능한 한 많은 화면 크기를 지원하려면 앱 레이아웃을 두 가지 옵션이 있습니다 반응형/적응형 레이아웃은 최적화된 사용자 제공 화면 크기에 구애받지 않고 모든 지원 환경에서 앱을 사용할 수 있도록 지원하여 태블릿, 폴더블, ChromeOS 기기, 세로 및 가로 방향 멀티 윈도우와 같이 크기 조절이 가능한 구성 모드로 설정합니다.

반응형/적응형 레이아웃은 사용 가능한 디스플레이 공간에 따라 변경됩니다. 변경사항 공간을 채우는 작은 레이아웃 조정 (반응형 디자인)부터 한 레이아웃을 다른 레이아웃으로 완전히 교체하여 다양한 디스플레이 크기 (적응형 디자인)를 지원합니다.

선언적 UI 도구 키트인 Jetpack Compose는 개발자가 콘텐츠를 다르게 렌더링하기 위해 동적으로 변경되는 레이아웃 구현 다양한 디스플레이 크기 중에서 선택할 수 있습니다.

화면 수준 컴포저블의 대규모 레이아웃 변경을 명시하기

Compose를 사용하여 전체 애플리케이션을 배치하면 앱 수준 및 화면 수준의 컴포저블은 렌더링을 위해 제공된 앱의 모든 공간을 차지합니다. 설계 시 이 수준에서는 더 큰 화면을 활용하도록 화면의 전체 레이아웃을 변경하는 것이 좋습니다.

레이아웃을 결정할 때 실제 하드웨어 값을 사용하지 않습니다. 하지만 고정된 실질적 가치에 기반하여 결정을 내리고자 합니다 (기기가 태블릿? 실제 화면에는 특정 가로세로 비율이 있는가?) 그러나 정답은 UI가 작동할 수 있는 공간을 결정하는 데 유용하지 않을 수 있습니다. 있습니다.

스마트폰, 폴더블, 태블릿, 노트북 등 다양한 기기 폼 팩터를 보여주는 다이어그램
그림 1. 스마트폰, 폴더블, 태블릿, 노트북 폼 팩터
를 통해 개인정보처리방침을 정의할 수 있습니다.
를 통해 개인정보처리방침을 정의할 수 있습니다.

태블릿에서는 앱이 멀티 윈도우 모드에서 실행될 수 있는데, 이는 앱이 다른 앱과 화면을 분할하고 있을 수 있습니다. ChromeOS에서는 앱이 크기를 조절할 수 있습니다. 폴더블 기기와 같이 실제 화면이 두 개 이상 있을 수도 있습니다. 이러한 모든 경우에 실제 화면 크기는 콘텐츠 표시 방법을 결정하는 데 도움이 됩니다.

대신, Jetpack WindowManager 라이브러리에서 제공하는 현재 창 측정항목과 같이 앱에 할당된 화면의 실제 부분에 따라 결정해야 합니다. Compose 앱에서 WindowManager를 사용하는 방법을 알아보려면 JetNews 샘플을 확인하세요.

이 방법을 따르면 위의 모든 시나리오에서 앱이 제대로 작동하기 때문에 앱이 더 유연해집니다. 화면 공간에 맞게 레이아웃을 적응형으로 만들기 또한 현장에서 사용할 수 있는 작업을 지원하기 위해 필요한 폼 팩터(예: ChromeOS) 그리고 폼 팩터입니다.

앱에서 사용할 수 있는 관련 공간을 관찰하면 Window에서 설명대로 원시 크기를 의미 있는 크기 클래스로 변환합니다. 크기 클래스를 참조하세요. 이 크기를 표준 크기 버킷으로 그룹화합니다. 표준 크기 버킷은 특정 크기의 단순성과 유연성의 균형을 유지하여 고유한 앱에 맞게 앱을 최적화할 수 있습니다. 있습니다. 이러한 크기 클래스는 앱의 전체 창을 참조하므로 클래스는 전체 화면 레이아웃에 영향을 미치는 레이아웃 결정을 위한 클래스입니다. 다음을 수행할 수 있습니다. 이러한 크기 클래스를 상태로 전달하거나 추가 로직을 실행하여 중첩된 컴포저블에 전달할 파생 상태를 만듭니다.

@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun MyApp(
    windowSizeClass: WindowSizeClass = currentWindowAdaptiveInfo().windowSizeClass
) {
    // Perform logic on the size class to decide whether to show the top app bar.
    val showTopAppBar = windowSizeClass.windowHeightSizeClass != WindowHeightSizeClass.COMPACT

    // MyScreen knows nothing about window sizes, and performs logic based on a Boolean flag.
    MyScreen(
        showTopAppBar = showTopAppBar,
        /* ... */
    )
}

이러한 계층화된 접근 방식은 화면 크기 로직을 단일 위치로 제한합니다. 앱 전반에 걸쳐 동기화해야 하는 여러 위치에 분산하는 것입니다. 이 단일 위치는 상태를 생성하며, 상태는 다른 앱 상태와 마찬가지로 다른 컴포저블 함수를 사용합니다. 명시적으로 상태를 전달하면 개별 컴포저블이 간소화됩니다. 이러한 컴포저블은 크기 클래스 또는 다른 데이터와 함께 지정된 구성을 사용하는 일반적인 구성 가능한 함수일 뿐이기 때문입니다.

유연한 중첩 컴포저블은 재사용 가능함

컴포저블은 다양한 위치에 배치할 수 있을 때 더 많이 재사용할 수 있습니다. 컴포저블이 항상 특정 위치에 배치된다고 가정하면 다른 곳에서 재사용하기가 더 어려워집니다. 다른 위치에 있거나 사용 가능한 공간이 다른 경우입니다. 또한 즉, 재사용 가능한 개별 컴포저블은 각 컴포저블이 객체에 따라 암시적으로 '전체' 크기 정보를 참조하세요.

다음 예를 참고하세요. 목록-세부정보 레이아웃, 한 개 또는 두 개의 창을 나란히 표시할 수 있습니다.

두 창을 나란히 표시하는 앱의 스크린샷
그림 2. 일반적인 목록-세부정보 레이아웃을 보여주는 앱의 스크린샷: 1이 목록 영역입니다. 2는 세부정보 영역입니다.

이 결정을 앱의 전체 레이아웃의 일부로 만들고자 하므로 위에서 본 것처럼 화면 수준의 컴포저블에서 결정을 전달합니다.

@Composable
fun AdaptivePane(
    showOnePane: Boolean,
    /* ... */
) {
    if (showOnePane) {
        OnePane(/* ... */)
    } else {
        TwoPane(/* ... */)
    }
}

대신 컴포저블이 다음에 따라 독립적으로 레이아웃을 변경하도록 하려면 어떻게 해야 할까요? 어떻게 해야 할까요? 예를 들어 광고에 추가 세부정보를 표시하고자 하는 경우 할 수 있습니다. 사용 가능한 일부 크기를 기반으로 몇 가지 로직을 수행하고 싶지만 구체적으로 어떤 사이즈를 선택해야 할까요?

서로 다른 두 카드의 예
그림 3. 좁은 카드에는 아이콘과 제목만 표시되고 더 넓은 카드는 아이콘, 제목, 간단한 설명을 표시합니다.

위에서 본 바와 같이 기기의 실제 화면 크기를 화면 여러 화면에서 정확하지 않을 수 있으며 정확하지 않을 수 있습니다. 앱이 전체 화면이 아닌 경우

이 컴포저블은 화면 수준의 컴포저블이 아니므로 재사용성을 최대화하기 위해 현재 창 측정항목을 직접 사용해서도 안 됩니다. 구성요소가 패딩과 함께 배치되거나(예: 인셋) 탐색 레일 또는 앱 바와 같은 구성요소가 있는 경우 컴포저블에 사용할 수 있는 공간의 양은 앱에 사용할 수 있는 전체 공간과 크게 다를 수 있습니다.

따라서 컴포저블이 컴포저블 자체를 렌더링하는 데 실제로 제공되는 너비를 사용해야 합니다. 이 너비를 얻는 방법은 두 가지가 있습니다.

콘텐츠가 표시되는 위치 또는 방법을 변경하려면 수정자 컬렉션 또는 맞춤 레이아웃 레이아웃을 반응형으로 만듭니다 이는 일부 하위 요소가 사용 가능한 공간을 모두 채우도록 하거나, 충분한 공간이 있는 경우 여러 개의 열로 하위 요소를 배치하는 작업만큼 간단한 작업일 수 있습니다.

표시되는 내용을 변경하려면 더 강력한 대안으로 BoxWithConstraints를 사용하면 됩니다. 이 컴포저블은 측정 가능한 제약 조건 배치된 공간에 따라 다양한 컴포저블을 호출하는 데 사용할 수 있는 있습니다. 하지만 BoxWithConstraints가 지연되므로 약간의 비용이 발생합니다. 컴포지션을 업데이트해야 합니다. 더 많은 작업을 실행해야 합니다.

@Composable
fun Card(/* ... */) {
    BoxWithConstraints {
        if (maxWidth < 400.dp) {
            Column {
                Image(/* ... */)
                Title(/* ... */)
            }
        } else {
            Row {
                Column {
                    Title(/* ... */)
                    Description(/* ... */)
                }
                Image(/* ... */)
            }
        }
    }
}

모든 데이터를 다양한 크기로 사용할 수 있는지 확인

추가 화면 공간을 활용하면 대형 화면에서는 작은 화면보다 더 많은 콘텐츠를 사용자에게 보여줄 수 있습니다. 이 동작으로 컴포저블을 구현할 때는 효율적으로 구현하고 싶고 데이터를 현재 크기의 부작용으로 로드하려는 경향이 있을 수 있습니다.

그러나 이는 단일 방향 데이터 흐름의 원칙에 어긋납니다. 데이터를 끌어올려 컴포저블에 제공하여 적절하게 렌더링할 수 있습니다. 충분 컴포저블에 항상 데이터를 제공해야 합니다. 데이터의 일부가 될 수 없는 경우에도 모든 크기로 표시되어야 합니다. 항상 사용됩니다.

@Composable
fun Card(
    imageUrl: String,
    title: String,
    description: String
) {
    BoxWithConstraints {
        if (maxWidth < 400.dp) {
            Column {
                Image(imageUrl)
                Title(title)
            }
        } else {
            Row {
                Column {
                    Title(title)
                    Description(description)
                }
                Image(imageUrl)
            }
        }
    }
}

Card 예를 기반으로, 항상 descriptionCard에 전달합니다. 너비가 설명을 표시하도록 허용하는 경우에만 description을 사용하더라도 사용할 수 있는 너비에 상관없이 Card는 항상 이 값을 요구합니다.

항상 데이터를 전달하면 적응형 레이아웃을 덜 스테이트풀(Stateful)로 만들어 적응형 레이아웃이 더 단순해집니다. 크기 간에 전환할 때 부작용이 발생하지 않도록 할 수 있습니다. 창 크기 조절, 방향 변경 또는 기기 접기 및 펼치기 등으로 인해 발생할 수 있습니다.

또한, 이 원칙을 사용하면 레이아웃을 변경해도 상태를 유지할 수 있습니다. 호이스팅하여 정보를 수집하지 않더라도 사용자의 상태를 크기를 조절하는 것입니다. 예를 들어 showMore 불리언 플래그를 호이스팅할 수 있습니다. 크기를 조절하여 레이아웃이 전환될 때 사용자의 상태가 유지되도록 합니다. 설명 숨기기와 표시 사이의 문제를 살펴보겠습니다

@Composable
fun Card(
    imageUrl: String,
    title: String,
    description: String
) {
    var showMore by remember { mutableStateOf(false) }

    BoxWithConstraints {
        if (maxWidth < 400.dp) {
            Column {
                Image(imageUrl)
                Title(title)
            }
        } else {
            Row {
                Column {
                    Title(title)
                    Description(
                        description = description,
                        showMore = showMore,
                        onShowMoreToggled = { newValue ->
                            showMore = newValue
                        }
                    )
                }
                Image(imageUrl)
            }
        }
    }
}

자세히 알아보기

Compose의 맞춤 레이아웃에 관한 자세한 내용은 다음 추가 리소스를 참고하세요.

샘플 앱

  • 대형 화면 표준 레이아웃 입증된 디자인 패턴의 저장소로서 최적의 사용자 환경을 대형 화면 기기에서의 환경
  • 제트뉴스 사용 가능한 공간을 활용할 수 있도록 UI를 조정하는 앱을 디자인하는 방법을 보여 줍니다.
  • 답글 모바일, 태블릿, 폴더블을 지원하는 적응형 샘플입니다.
  • Now in Android는 적응형 레이아웃을 사용하여 다양한 화면 크기를 지원하는 앱

동영상