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

kosdjs·2025년 8월 18일

Android

목록 보기
19/29
  • 반응형/적응형 레이아웃은 화면 크기에 상관없이 최적화된 사용자 경험을 제공함, 반응형/적응형 레이아웃은 뷰 기반 앱이 모든 화면 크기, 방향, 설정(멀티 윈도우 모드와 같은 크기 변경 가능 설정 포함)을 지원할 수 있게 함

반응형 디자인

  • 다양한 기기의 폼 팩터를 지원하는 첫 단계는 앱이 사용 가능한 디스플레이 공간의 크기의 변화에 따라 반응하는 레이아웃을 만드는 것임

ConstraintLayout

  • 반응형 레이아웃을 만드는 가장 좋은 방법은 UI의 기본 레이아웃으로 ConstraintLayout 을 사용하는 것임, ConstraintLayout 은 각 뷰를 레이아웃의 다른 뷰와의 공간적 관계에 따라 위치와 크기를 지정할 수 있게 함, 이에 따라 모든 뷰는 디스플레이 공간의 변화에 따라 이동하고 크기를 변경할 수 있게 됨

  • ConstraintLayout 을 이용해 레이아웃을 만드는 가장 쉬운 받법은 안드로이드 스튜디오의 레이아웃 에디터를 사용하는 것임, 레이아웃 에디터는 XML을 직접 수정하지 않고 레이아웃에 새로운 뷰를 추가하거나 부모, 형제 뷰에 연관된 제약을 걸거나 뷰 속성을 설정할 수 있음

그림 3. ConstraintLayout 을 보여주는 안드로이드 스튜디오의 레이아웃 에디터

반응형 너비와 높이

  • 레이아웃이 디스플레이 크기에 반응되게 만드려면 뷰 컴포넌트의 너비와 높이 속성에 하드 코딩된 값을 대신해 wrap_content, match_parent, 0dp를 사용해야 함
    • wrap_content : 뷰가 포함하는 내용의 크기에 맞게 뷰의 크기를 조절함
    • match_parent : 부모 뷰 안에서 최대한 크게 확장함
    • 0dp (match constraint) : ConstraintLayout 에서 match_parent 와 비슷함, 뷰의 제약 조건안에서 최대한 크게 확장함

반응형 너비와 높이를 사용하는 TextView 의 예시

<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 을 사용한다면 사용 가능한 공간을 비례적으로 채우기 위해 자식 뷰를 layout weight 를 기준으로 확장할 수 있음, 하지만 중첩된 LinearLayout 에서 가중치(weight)을 사용하는 것은 시스템이 각 뷰의 크기를 결정하기 위해 여러 번의 레이아웃 계산 및 배치 단계를 거쳐야 하기 때문에 UI 성능이 저하됨

  • ConstraintLayoutLinearLayout 을 사용해 만들 수 있는 레이아웃 대부분을 성능 영향 없이 만들 수 있으니, 중첩된 LinearLayoutConstraintLayout 으로 변환해야 함, 변환 후에는 제약 조건 체인(constraint chain)을 이용해 가중치 기반 레이아웃을 만들 수 있음

노트: ConstraintLayout 을 사용할 때 match_parent 를 사용하지 마세요. 대신 일반적으로 match_parent 와 동일한 동작을 하고 제약 조건 일치(match constraint) 라고 불리는 특수한 동작을 사용하기 위해서 크기를 0dp로 설정하세요. 더 많은 정보를 확인하려면 ConstraintLayout으로 반응형 UI 빌드뷰 크기 조정부분을 확인하세요.

적응형 디자인

  • 앱의 레이아웃은 항상 다양한 디스플레이 크기에 반응해야 함, 하지만 반응형 레이아웃이더라도 모든 기기나 멀티 윈도우 모드 화면에서 항상 최상의 사용자 경험을 제공할 수는 없음, 예시로 폰을 위해 디자인한 UI의 경우 최적의 사용자 경험을 태블릿에서 제공하지 않을 수 있음, 적응형 디자인은 다양한 디스플레이 크기에 최적화된 대체 레이아웃을 제공함

목록-세부정보 UI를 위한 SlidingPaneLayout

  • 목록-세부정보 UI는 화면 크기마다 다른 사용자 경험을 제공함, 큰 화면에서는 보통 리스트 패널과 세부정보 패널을 나란히 표시함, 리스트의 아이템이 선택될 때 아이템의 정보가 세부정보 창에 UI를 변경하지 않고 표시됨(두 패널이 나열된 상태 그대로), 하지만 작은 화면에서는 두 패널이 각각 전체 디스플레이 영역을 차지하기 때문에 각각 표시됨, 리스트 패널에서 아이템을 선택할 때 선택된 아이템의 정보가 포함된 세부정보 패널이 리스트 패널을 대체함, 뒤로가기를 눌렀을 때 리스트 패널이 세부정보 패널을 대체함

  • SlidingPaneLayout 은 앞서 설명한 두 사용자 경험 중 현재 화면 크기에 따라 어떤 것이 적합한지를 결정함

XML에서 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 값은 두 패널의 크기를 비례적으로 결정하는데 사용됨, 위의 예시에서 리스트 패널은 가중치(weight)를 가지고 있지 않기 때문에 항상 280dp의 너비를 가짐, 하지만 세부정보 패널은 layout_weight 에 따라 580dp를 넘는 가로 공간을 항상 채움

노트: 일반적인 layout_weight의 동작의 한 예외는 폴더블 기기에서 SlidingPaneLayout 1.2.0 버전 이상을 사용할 때이고, 이때는 SlidingPaneLayout 이 접힌 부분이나 힌지의 양쪽에 패널이 위치하도록 자동으로 패널의 크기를 조절합니다.

대체 레이아웃 리소스

  • UI 디자인을 다양한 디스플레이 크기에 맞추려면 리소스 한정자(qualifier)를 사용해 대체 레이아웃을 사용해야 함

그림 5. 디스플레이 크기에 따라 다른 레이아웃을 사용하는 같은 앱

  • 앱 소스 코드에 추가 res/layout/ 디렉토리를 생성해 화면별 레이아웃을 제공할 수 있음, 다른 레이아웃을 필요로 하는 화면 설정마다 디렉토리를 생성하고 디렉토리 이름에 화면 설정 한정자를 추가하면 됨, 예시로 사용 가능한 너비가 600dp인 화면을 위한 디렉토리 이름은 layout-w600dp

  • 이 설정 한정자는 UI를 위해 사용할 수 있는 보이는 디스플레이 공간을 나타냄, 시스템은 앱을 위한 레이아웃을 선택할 때 네비게이션 바와 같은 시스템 장식이나 멀티 윈도우 모드같은 화면 설정 변경을 고려함

최소 너비 한정자

  • 최소 너비 화면 크기 한정자는 밀도 독립적 픽셀(dp)로 측정된 최소 너비를 가진 화면을 위한 대체 레이아웃을 제공하는 것을 가능하게 함

  • 화면 크기를 dp 측정 단위로 나타내는 것은 안드로이드에서 픽셀 밀도와 관계없이 특정 디스플레이 크기를 위한 레이아웃을 만드는 것을 가능하게 함

main_activity 레이아웃을 폰과 태블릿을 위해 최적화된 버전을 다른 디렉토리에 넣는 예시

res/layout/main_activity.xml           # 폰용 (600dp 미만의 최소 너비)
res/layout-sw600dp/main_activity.xml   # 7인치 이상 태블릿용 (최소 너비 600dp 이상)
  • 최소 너비 한정자는 기기의 방향과 관계 없이 디스플레이의 두 면중 짧은 면을 지정하므로, 레이아웃을 위해 사용 가능한 디스플레이 크기를 지정하는 방법임

  • 최소 너비 값은 일반적인 화면 크기에 다음과 같이 대응됨

    • 320dp: 작은 폰 화면 (240x320 ldpi, 320x480 mdpi, 480x800 hdpi, etc.)
    • 480dp: 5인치 까지의 큰 폰 화면 (480x800 mdpi)
    • 600dp: 7인치 태블릿 (600x1024 mdpi)
    • 720dp: 10인치 태블릿 (720x1280 mdpi, 800x1280 mdpi, etc.)
  • 다음 그림은 화면 dp 너비가 화면 크기와 방향에 대응되는지 더 자세하게 보여줌

그림 6. 다양한 화면 크기를 지원하기 위해 권장되는 너비 기준(breakpoint)

  • 최소 너비 한정자의 값은 dp 임, 그 이유는 해상도가 아니라 시스템이 픽셀 밀도를 고려한 후의 사용 가능한 디스플레이 크기가 중요하기 때문임

  • 최소 너비와 같이 리소스 한정자를 사용해 지정하는 크기는 실제 화면 크기가 아님, 오히려 dp 단위로 지정한 너비나 높이의 크기는 앱이 사용 가능한 크기임, 안드로이드 시스템은 화면 아래의 시스템바나 위의 상태 바와 같은 시스템 UI를 위해 화면 일부를 사용하기 때문에 화면의 일부는 앱 레이아웃을 위해 사용하지 못할 수 있음

  • 만약 앱이 멀티 윈도우 모드에서 사용된다면 앱은 앱이 포함된 창의 크기만 사용할 수 있음, 창의 크기가 변경되면 새 창의 크기로 설정 변경이 되고 이는 시스템이 적절한 레이아웃 파일을 선택하게 할 수 있음, 그러므로 선언하는 리소스 한정자의 크기는 앱이 필요로 하는 공간만 지정해야 함, 시스템이 레이아웃을 위한 공간을 제공할 때 시스템 UI에 의해 사용되는 공간을 고려하기 때문임

사용가능한 너비 한정자

  • 화면의 최소 너비 기반으로 레이아웃으로 변경하는 대신 너비나 높이가 얼마나 사용 가능한지에 따라 레이아웃을 변경하고 싶을 수 있음, 예시로 기기가 세로 방향인지 가로 방향인지에 따라 변하는 너비가 600dp 이상이 될 때 양쪽에 두 패널 레이아웃을 사용하고 싶을 수 있음, 이런 경우에는 다음과 같이 사용가능한 너비 한정자를 사용해야 함
res/layout/main_activity.xml         # 폰용 (사용가능한 너비가 600dp 미만일 때)
res/layout-w600dp/main_activity.xml  # 7인치 이상 태블릿용이거나 사용가능한 너비가 600dp 이상인 화면용
                                     # (가로 방향의 폰도 가능함)

방향 한정자

  • 최소 너비 한정자와 사용가능한 너비 한정자의 조합만으로도 모든 크기에 대해 지원할 수 있지만 사용자가 화면의 방향을 변경했을 때의 사용자 경험을 다르게 제공하고 싶을 수 있음

  • 이럴 때 portland 한정자를 레이아웃 디렉토리 이름에 추가할 수 있음, 항상 다음 예시와 같이 방향 한정자는 크기 한정자 이후에 와야함

res/layout/main_activity.xml                # 폰용
res/layout-land/main_activity.xml           # 가로 방향의 폰용
res/layout-sw600dp/main_activity.xml        # 7인치 이상 태블릿용
res/layout-sw600dp-land/main_activity.xml   # 가로 방향의 7인치 이상 태블릿용

창 크기 클래스

  • 창 크기 클래스는 적응형 레이아웃을 만드는데 도움을 주는 뷰포트 기준점(viewport breakpoint)임, 이 기준점은 앱이 사용가능한 디스플레이 영역을 compact, medium, expanded 로 구분함, 너비와 높이가 개별적으로 지정되기 때문에 앱이 항상 너비와 높이에 대한 창 크기 클래스를 따로 가질 수 있음

  • 적응형 레이아웃을 다음과 같이 프로그래밍적으로 적용할 수 있음

    • 창 크기 클래스 기준점을 기준으로 레이아웃 리소스 생성
    • Jetpack WindowManager 라이브러리의 WindowSizeClass#compute() 함수를 사용해 앱의 너비와 높이 창 크기 클래스를 계산함
    • 현재 창 크기 클래스를 위한 레이아웃 리소스를 불러옴

노트: 대부분의 앱은 오직 너비 창 크기 클래스만 고려해서 적응형 레이아웃을 구현할 수 있습니다.

프래그먼트를 사용한 모듈화된 UI 컴포넌트

  • 다양한 디스플레이 크기를 지원하는 앱을 설계할 때 액티비티간에 UI 동작이 불필요하게 중복되지 않도록 프래그먼트를 사용해 UI 로직을 각자의 컴포넌트로 추출해야 함, 그러면 큰 화면에서는 프래그먼트를 결합해 다중 패널 레이아웃을 생성할 수 있고, 작은 화면에서는 별도의 액티비티에 프래그먼트를 배치할 수 있음

  • 예시로 목록-세부사항 패턴(위의 SlidingPaneLayout)은 목록을 포함하는 프래그먼트와 목록 아이템 세부정보를 포함하는 다른 프래그먼트로 구현할 수 있음, 큰 화면에서는 프래그먼트들을 양쪽으로 나열할 수 있고, 작은 화면에서는 각각 화면 전체를 채우게 할 수 있음

액티비티 삽입

  • 만약 앱이 여러 개의 액티비티로 구성되어 있다면, 액티비티 삽입은 적응형 UI를 쉽게 생성할 수 있게 함

  • 액티비티 삽입은 하나의 앱 작업 창에서 여러 액티비티나 동일한 액티비티의 여러 인스턴스를 동시에 표시함, 큰 화면에서 액티비티는 양쪽에 표시될 수 있고, 작은 화면에서는 스택에 쌓일 수 있음

  • XML 설정 파일을 생성해 앱이 액티비티를 어떻게 표시할지를 결정할 수 있음, 시스템은 이 파일을 사용해 디스플레이 크기에 따라 적절한 표시 방법을 결정함, Jetpack WindowManager API를 호출하는 방법도 있음

  • 액티비티 삽입은 폴더블 기기와 기기의 화면 변경을 지원함, 기기가 회전되거나 접었을 때처럼 화면이 작아지면 액티비티를 스택에 쌓고 기기를 다시 피거나 회전시키면 쌓았던 액티비티를 꺼내 한 화면에 표시할 수 있음

화면 크기와 비율

  • 앱을 UI가 정상적으로 확장되는지 확인하려면 다양한 화면 크기와 비율에서 테스트 해야함

  • 안드로이드 10(API 레벨 29) 이상은 넓은 범위의 비율을 지원함, 폴더블 폼팩터는 접었을 때 21:9의 좁고 높은 화면에서 펼쳤을 때 1:1의 정사각형에 가까운 비율 등 다양한 비율로 변할 수 있음

  • 최대한 많은 기기의 호환성을 보장하기 위해 가능하면 아래의 화면 비율과 같은 다양한 화면 비율에서 앱을 테스트 하는 것이 좋음

그림 7. 다양한 화면 비율들

  • 만약 테스트하고 싶은 다양한 화면 크기의 기기를 사용할 수 없다면 안드로이드 에뮬레이터를 사용해 다양한 화면 크기를 테스트할 수 있음

  • 만약 실물 기기에서 테스트해보고 싶을 때 기기가 없다면 Firebase Test Lab 을 사용해 구글 데이터 센터에 있는 기기로 테스트 할 수 있음

원문: https://developer.android.com/develop/ui/views/layout/responsive-adaptive-design-with-views

0개의 댓글