뷰를 사용한 반응형/적응형 디자인

반응형/적응형 레이아웃은 화면 크기와 관계없이 최적화된 사용자 환경을 제공합니다. 반응형/적응형 레이아웃을 구현하여 뷰 기반 앱이 멀티 윈도우 모드와 같이 크기 조절이 가능한 구성을 비롯하여 모든 디스플레이 크기와 방향, 구성을 지원할 수 있도록 합니다.

반응형 디자인

다양한 기기 폼 팩터를 지원하는 첫 번째 단계는 앱에서 사용할 수 있는 디스플레이 공간의 양 변화에 반응하는 레이아웃을 만드는 것입니다.

ConstraintLayout

반응형 레이아웃을 만드는 가장 좋은 방법은 ConstraintLayout을 UI의 기본 레이아웃으로 사용하는 것입니다. ConstraintLayout를 사용하면 레이아웃에 있는 다른 뷰와의 공간적 관계에 따라 각 뷰의 위치와 크기를 지정할 수 있습니다. 그러면 디스플레이 공간이 변경될 때 모든 뷰가 함께 이동하고 크기를 조절할 수 있습니다.

ConstraintLayout을 사용하여 레이아웃을 빌드하는 가장 쉬운 방법은 Android 스튜디오의 Layout Editor를 사용하는 것입니다. Layout Editor를 사용하면 XML을 직접 수정하지 않고도 새 뷰를 레이아웃으로 드래그하고 상위 및 동위 뷰에 상대적인 제약 조건을 적용하며 뷰 속성을 설정할 수 있습니다.

그림 3. ConstraintLayout을 보여주는 Android 스튜디오의 Layout Editor

자세한 내용은 ConstraintLayout으로 반응형 UI 빌드를 참고하세요.

반응형 너비 및 높이

레이아웃이 다양한 디스플레이 크기에 반응하도록 하려면 하드 코딩 값 대신 뷰 구성요소의 너비와 높이에 wrap_content, match_parent 또는 0dp (match constraint)를 사용하세요.

  • wrap_content: 뷰는 뷰에 포함된 콘텐츠에 맞게 크기를 설정합니다.
  • match_parent: 뷰가 상위 뷰 내에서 최대한 크게 확장됩니다.
  • 0dp (match constraint): ConstraintLayout에서 match_parent와 유사합니다. 뷰는 뷰의 제약 조건 내에서 사용 가능한 모든 공간을 차지합니다.

예:

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/lorem_ipsum" />

그림 4에서는 기기 방향에 따라 디스플레이 너비가 변경될 때 TextView의 너비와 높이가 어떻게 조정되는지 보여줍니다.

그림 4. 반응형 TextView

TextView는 사용 가능한 모든 공간 (match_parent)을 채우도록 너비를 설정하고 포함된 텍스트 높이 (wrap_content)에 필요한 공간만큼 높이를 설정합니다. 따라서 뷰를 다양한 디스플레이 크기와 다양한 텍스트 양에 따라 조정할 수 있습니다.

LinearLayout를 사용 중인 경우 뷰가 사용 가능한 공간을 비례적으로 채우도록 레이아웃 가중치에 따라 하위 뷰를 확장할 수도 있습니다. 그러나 중첩 LinearLayout에서 가중치를 사용하려면 시스템이 여러 레이아웃 전달을 실행하여 각 뷰의 크기를 판단해야 하므로 UI 성능이 느려집니다.

ConstraintLayout는 성능에 영향을 미치지 않으면서 LinearLayout로 가능한 거의 모든 레이아웃을 만들 수 있으므로 중첩된 LinearLayoutConstraintLayout로 변환합니다. 그런 다음 제약 조건 체인을 사용하여 가중치 적용 레이아웃을 정의할 수 있습니다.

적응형 디자인

앱 레이아웃은 항상 다양한 디스플레이 크기에 반응해야 합니다. 그러나 반응형 레이아웃이라고 해서 모든 기기나 멀티 윈도우 모드 디스플레이에서 최상의 사용자 환경을 제공할 수는 없습니다. 예를 들어 휴대전화용으로 디자인한 UI는 태블릿에서 최적의 사용자 환경을 제공하지 못할 수 있습니다. 적응형 디자인은 다양한 디스플레이 크기에 최적화된 대체 레이아웃을 제공합니다.

목록 세부정보 UI의 SlidingPaneLayout

목록 세부정보 UI는 일반적으로 다양한 크기의 화면에서 다양한 사용자 환경을 제공합니다. 대형 화면에서는 목록 창과 세부정보 창이 일반적으로 나란히 표시됩니다. 목록의 항목을 선택하면 UI가 변경되지 않고 세부정보 창에 항목 정보가 표시됩니다. 두 창은 나란히 표시됩니다. 그러나 작은 화면에서는 두 창이 별도로 표시되며 각 창은 전체 디스플레이 영역을 차지합니다. 목록 창의 항목을 선택하면 선택된 항목의 정보가 포함된 세부정보 창이 목록 창을 대체합니다. 뒤로 탐색은 세부정보 창을 목록으로 대체합니다.

SlidingPaneLayout는 두 사용자 환경 중에서 현재 창 크기에 적절한 환경을 결정하는 로직을 관리합니다.

<?xml version="1.0" encoding="utf-8"?>
<androidx.slidingpanelayout.widget.SlidingPaneLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="280dp"
        android:layout_height="match_parent"
        android:layout_gravity="start" />

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="300dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        app:defaultNavHost="true"
        app:navGraph="@navigation/item_navigation" />

</androidx.slidingpanelayout.widget.SlidingPaneLayout>

SlidingPaneLayout에 포함된 두 뷰의 layout_widthlayout_weight 속성은 SlidingPaneLayout 동작을 결정합니다. 이 예에서는 창이 두 뷰를 모두 표시할 만큼 충분히 큰 경우 (너비 580dp 이상) 두 창이 나란히 표시됩니다. 하지만 창 너비가 580dp보다 작으면 두 창이 서로 겹쳐서 각각 전체 앱 창을 차지하게 됩니다.

창 너비가 지정된 총 최소 너비 (580dp)보다 크면 layout_weight 값을 사용하여 두 창의 크기를 비례적으로 조정할 수 있습니다. 이 예에서 목록 창은 가중치가 없기 때문에 너비가 항상 280dp입니다. 그러나 세부정보 창은 뷰의 layout_weight 설정으로 인해 항상 580dp를 초과하는 가로 공간을 채웁니다.

대체 레이아웃 리소스

다양한 디스플레이 크기에 맞게 UI 디자인을 조정하려면 리소스 한정자로 식별되는 대체 레이아웃을 사용합니다.

그림 5. 동일한 앱이 다양한 디스플레이 크기에 다른 레이아웃을 사용하는 경우

앱의 소스 코드에 res/layout/ 디렉터리를 추가로 만들어 화면별 적응형 레이아웃을 제공할 수 있습니다. 다른 레이아웃이 필요한 각 화면 구성별로 디렉터리를 만듭니다. 그런 다음 화면 구성 한정자를 layout 디렉터리 이름에 추가합니다 (예: 사용 가능한 너비가 600dp인 화면에는 layout-w600dp).

구성 한정자는 앱 UI에 사용할 수 있는 표시 가능한 디스플레이 공간을 나타냅니다. 시스템은 앱의 레이아웃을 선택할 때 모든 시스템 장식 (예: 탐색 메뉴) 및 창 구성 변경사항 (예: 멀티 윈도우 모드)을 고려합니다.

Android 스튜디오에서 대체 레이아웃을 만들려면 뷰를 사용하여 UI 개발에서 레이아웃 변형을 사용하여 다양한 화면에 맞게 최적화를 참고하세요.

최소 너비 한정자

최소 너비 화면 크기 한정자를 사용하면 밀도 독립형 픽셀(dp) 단위로 측정된 최소 너비가 있는 디스플레이에 대체 레이아웃을 제공할 수 있습니다.

Android에서 화면 크기를 dp 단위의 측정값으로 설명하면 Android에서 다양한 픽셀 밀도를 고려하지 않고 특정 디스플레이 크기로 디자인된 레이아웃을 만들 수 있습니다.

예를 들어 서로 다른 디렉터리에 다른 버전의 파일을 만들어 휴대전화와 태블릿에 최적화된 main_activity라는 레이아웃을 만들 수 있습니다.

res/layout/main_activity.xml           # For phones (smaller than 600dp smallest width)
res/layout-sw600dp/main_activity.xml   # For 7" tablets (600dp wide or wider)

최소 너비 한정자는 기기의 현재 방향과 관계없이 디스플레이의 두 면 중 가장 작은 것을 지정하므로 레이아웃에 사용할 수 있는 전반적인 디스플레이 크기를 지정하는 방법입니다.

다른 최소 너비 값에 해당하는 일반 화면 크기는 다음과 같습니다.

  • 320dp: 소형 휴대전화 화면 (240x320 ldpi, 320x480 mdpi, 480x800 hdpi 등)
  • 480dp: 대형 휴대전화 화면, 최대 5인치까지(480x800 mdpi).
  • 600dp: 7인치 태블릿 (600x1024 mdpi)
  • 720dp: 10인치 태블릿 (720x1280 mdpi, 800x1280 mdpi 등)

다음 그림은 다양한 화면 dp 너비가 다양한 화면 크기 및 방향에 해당하는 방식을 자세히 보여줍니다.

그림 6. 다양한 화면 크기를 지원하는 권장 너비 중단점

최소 너비 한정자의 값은 dp입니다. 중요한 것은 시스템이 픽셀 밀도(원시 픽셀 해상도 아님)를 고려한 후 사용 가능한 디스플레이 공간 크기이기 때문입니다.

최소 너비와 같은 리소스 한정자를 사용하여 지정하는 크기는 실제 화면 크기가 아닙니다. 이 크기는 앱 창에 사용할 수 있는 dp 단위의 너비 또는 높이를 지정합니다. Android 시스템에서는 일부 화면을 시스템 UI (예: 화면 하단의 시스템 표시줄 또는 상단의 상태 표시줄)용으로 사용할 수 있으므로, 이들 화면 중 일부에서는 레이아웃을 사용하지 못할 수 있습니다. 앱을 멀티 윈도우 모드에서 사용하는 경우 앱은 앱이 포함된 창의 크기에만 액세스할 수 있습니다. 창 크기를 조절하면 새 창 크기로 구성 변경을 트리거하므로 시스템에서 적절한 레이아웃 파일을 선택할 수 있습니다. 따라서 선언하는 리소스 한정자 크기는 앱에 필요한 공간만 지정해야 합니다. 시스템에서는 레이아웃용 공간을 제공할 때 시스템 UI가 사용하는 공간을 고려합니다.

사용 가능한 너비 한정자

디스플레이의 최소 너비에 따라 레이아웃을 변경하는 대신 사용할 수 있는 너비나 높이에 따라 레이아웃을 변경할 수 있습니다. 예를 들어 화면의 너비가 최소 600dp가 될 때마다 창 두 개 레이아웃을 사용하고자 할 수 있습니다. 이는 기기가 가로 방향에 있는지 세로 방향에 있는지에 따라 변경될 수 있습니다. 이 경우 다음과 같이 사용 가능한 너비 한정자를 사용해야 합니다.

res/layout/main_activity.xml         # For phones (smaller than 600dp available width)
res/layout-w600dp/main_activity.xml  # For 7" tablets or any screen with 600dp available width
                                     # (possibly landscape phones)

앱에서 사용 가능한 높이가 관건인 경우에는 사용 가능한 높이 한정자를 사용할 수 있습니다. 예를 들어 화면 높이가 600dp 이상인 화면의 경우 layout-h600dp입니다.

방향 한정자

최소 너비사용 가능한 너비 한정자의 조합만 사용하여 모든 크기 변형을 지원할 수도 있지만 사용자가 세로 방향과 가로 방향 간에 전환할 때 사용자 환경을 변경하고자 할 수도 있습니다.

이를 위해 port 또는 land 한정자를 레이아웃 디렉터리 이름에 추가할 수 있습니다. 이러한 방향 한정자는 크기 한정자 뒤에 오도록 해야 합니다. 예:

res/layout/main_activity.xml                # For phones
res/layout-land/main_activity.xml           # For phones in landscape
res/layout-sw600dp/main_activity.xml        # For 7" tablets
res/layout-sw600dp-land/main_activity.xml   # For 7" tablets in landscape

모든 화면 구성 한정자에 관한 자세한 내용은 앱 리소스 개요를 참고하세요.

창 크기 클래스

창 크기 클래스는 적응형 레이아웃을 만드는 데 도움이 되는 표시 영역 중단점입니다. 중단점은 앱에서 사용할 수 있는 디스플레이 영역을 소형, 중간 또는 확장으로 식별합니다. 너비와 높이는 개별적으로 지정되므로 앱에는 항상 너비의 창 크기 클래스와 높이의 창 크기 클래스가 있습니다.

적응형 레이아웃을 프로그래매틱 방식으로 적용하려면 다음 단계를 따르세요.

  • 창 크기 클래스 중단점을 기반으로 레이아웃 리소스 만들기
  • Jetpack WindowManager 라이브러리의 WindowSizeClass#compute() 함수를 사용하여 앱의 너비 및 높이 창 크기 클래스를 계산합니다.
  • 현재 창 크기 클래스의 레이아웃 리소스 확장

창 크기 클래스에 관한 자세한 내용은 다양한 화면 크기 지원을 참고하세요.

프래그먼트로 UI 구성요소 모듈화

여러 디스플레이 크기에 맞게 앱을 디자인할 때는 활동 간에 불필요하게 UI 동작이 중복되지 않도록 프래그먼트를 사용하여 UI 로직을 개별 구성요소로 추출해야 합니다. 그런 다음 프래그먼트를 결합하여 대형 화면에서 다중 창 레이아웃을 만들거나 작은 화면의 별도의 활동에 프래그먼트를 배치할 수 있습니다.

예를 들어 목록 세부정보 패턴 (위의 SlidingPaneLayout 참고)은 목록을 포함하는 프래그먼트 하나와 목록 항목 세부정보를 포함하는 또 다른 프래그먼트로 구현할 수 있습니다. 큰 화면에서는 프래그먼트를 나란히 표시할 수 있고, 작은 화면에서는 개별적으로 화면을 채울 수 있습니다.

자세한 내용은 프래그먼트 개요를 참고하세요.

활동 삽입

앱이 여러 활동으로 구성된 경우 활동 삽입을 사용하면 쉽게 적응형 UI를 만들 수 있습니다.

활동 삽입은 애플리케이션의 작업 창에 동시에 여러 활동을 표시하거나 동일 활동의 여러 인스턴스를 표시합니다. 큰 화면에서는 활동을 나란히 표시하고 작은 화면에서는 활동을 스택으로 표시할 수 있습니다.

앱이 활동을 표시하는 방식은 XML 구성 파일을 만들어서 정합니다. 시스템에서는 XML 구성 파일을 사용하여 디스플레이 크기를 기준으로 적절한 표시를 결정합니다. 또는 Jetpack WindowManager API를 호출할 수 있습니다.

활동 삽입은 기기 방향 변경 및 폴더블 기기를 지원하며 기기가 회전하거나 접히고 펼쳐질 때 활동을 스택하고 스택 해제합니다.

자세한 내용은 활동 삽입을 참고하세요.

화면 크기 및 가로세로 비율

앱을 다양한 화면 크기와 가로세로 비율에서 테스트하여 UI 크기 조정이 올바르게 이루어지는지 확인하세요.

Android 10(API 수준 29) 및 이후 버전에서는 다양한 가로세로 비율을 지원합니다. 폴더블 폼 팩터는 접었을 때 21:9인 길고 좁은 화면부터 펼쳤을 때 1:1인 정사각형 가로세로 비율까지 다양할 수 있습니다.

가능한 한 많은 기기와의 호환성을 보장하려면 가능한 한 많은 화면 가로세로 비율로 앱을 테스트하세요.

그림 7. 다양한 화면 가로세로 비율

테스트하려는 모든 다양한 화면 크기의 기기에 액세스할 수 없다면 Android Emulator를 사용하여 거의 모든 화면 크기를 에뮬레이션할 수 있습니다.

실제 기기에서 테스트하고 싶지만 기기가 없다면 Firebase Test Lab을 사용하여 Google 데이터 센터의 기기에 액세스하면 됩니다.

추가 리소스