lightbulb_outline Help shape the future of the Google Play Console, Android Studio, and Firebase. Start survey

다양한 화면 크기 지원

이 과정에서는 다음과 같은 방법을 사용하여 다양한 화면 크기를 지원하는 방법을 보여줍니다.

  • 레이아웃이 화면에 맞게 적절히 크기를 조절하는지 확인
  • 화면 구성에 따라 적절한 UI 레이아웃 제공
  • 알맞은 화면에 알맞은 레이아웃이 적용되는지 확인
  • 올바르게 크기가 조정되는 비트맵 제공

"wrap_content"와 "match_parent" 사용

레이아웃이 유연하고 다양한 화면 크기에 맞게 적응하는지 확인하려면 일부 뷰 구성 요소의 너비와 높이에 "wrap_content""match_parent"를 사용해야 합니다. "wrap_content"를 사용할 경우 뷰의 너비와 높이는 그 뷰 안의 콘텐츠에 맞는 최소 크기로 설정되고 "match_parent"는 상위 뷰의 크기에 맞게 구성 요소를 확장합니다.

하드코딩된 크기 대신 "wrap_content""match_parent" 크기 값을 사용하면 뷰에 필요한 공간만 사용하거나 이용 가능한 공간에 맞추어 확장됩니다. 예:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <LinearLayout android:layout_width="match_parent" 
                  android:id="@+id/linearLayout1"  
                  android:gravity="center"
                  android:layout_height="50dp">
        <ImageView android:id="@+id/imageView1" 
                   android:layout_height="wrap_content"
                   android:layout_width="wrap_content"
                   android:src="@drawable/logo"
                   android:paddingRight="30dp"
                   android:layout_gravity="left"
                   android:layout_weight="0" />
        <View android:layout_height="wrap_content" 
              android:id="@+id/view1"
              android:layout_width="wrap_content"
              android:layout_weight="1" />
        <Button android:id="@+id/categorybutton"
                android:background="@drawable/button_bg"
                android:layout_height="match_parent"
                android:layout_weight="0"
                android:layout_width="120dp"
                style="@style/CategoryButtonStyle"/>
    </LinearLayout>

    <fragment android:id="@+id/headlines" 
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.HeadlinesFragment"
              android:layout_width="match_parent" />
</LinearLayout>

샘플이 특정 치수가 아닌 구성 요소 크기에 대해 어떤 식으로 "wrap_content""match_parent"를 사용하는지 확인하세요. 그러면 레이아웃이 다양한 화면 크기와 방향에 올바르게 적응할 수 있습니다.

예를 들어 세로와 가로 모드에서 이 레이아웃은 다음과 같이 보입니다. 구성 요소의 크기가 너비와 높이에 맞게 자동으로 조절되는 것을 확인하세요.

그림 1. 세로(왼쪽) 및 가로(오른쪽) 모드의 News Reader 샘플 앱.

RelativeLayout 사용

LinearLayout의 중첩된 인스턴스와 "wrap_content""match_parent" 크기 조합을 사용하여 상당히 복잡한 레이아웃을 구성할 수 있습니다. 그러나 LinearLayout은 하위 뷰의 공간 관계를 정확하게 제어할 수 없습니다. LinearLayout의 뷰는 단순하게 나란히 배치됩니다. 일직선이 아닌 다양한 형태로 하위 뷰의 방향을 배치해야 한다면 종종 RelativeLayout을 사용하는 것이 더 낫습니다. 구성 요소 사이의 공간 관계를 기준으로 레이아웃을 지정할 수 있기 때문입니다. 예를 들어 화면 왼쪽에 하위 뷰를 한 개, 오른쪽에 다른 하나를 정렬할 수 있습니다.

예:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/label"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Type here:"/>
    <EditText
        android:id="@+id/entry"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/label"/>
    <Button
        android:id="@+id/ok"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/entry"
        android:layout_alignParentRight="true"
        android:layout_marginLeft="10dp"
        android:text="OK" />
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toLeftOf="@id/ok"
        android:layout_alignTop="@id/ok"
        android:text="Cancel" />
</RelativeLayout>

그림 2는 QVGA 화면에서 이 레이아웃이 어떻게 나타나는지 보여줍니다.

그림 2. QVGA 화면(작은 화면)의 스크린샷

그림 3은 큰 화면에서 이 레이아웃이 어떻게 나타나는지 보여줍니다.

그림 3. WSVGA 화면(큰 화면)의 스크린샷

구성 요소의 크기가 변경되었지만 공간 관계는 RelativeLayout.LayoutParams에서 지정한 대로 보존된 것을 알 수 있습니다.

크기 한정자 사용

이전 섹션의 유연한 레이아웃이나 상대 레이아웃에는 한계가 있습니다. 이 레이아웃은 구성 요소 내부와 주변의 공간을 늘려서 다양한 화면에 적응하지만, 각 화면 크기에 맞는 최고의 사용자 경험을 제공하지 못할 수 있습니다. 그러므로 애플리케이션은 유연한 레이아웃을 구현할 뿐만 아니라 다양한 화면 구성을 대상으로 하는 여러 가지 대체 레이아웃을 제공해야 합니다. 이 작업은 구성 한정자를 사용하면 가능합니다. 구성 한정자는 런타임이 현재 기기의 구성에 따라 자동으로 적절한 리소스를 선택하게 해줍니다(예: 다양한 화면 크기에 맞는 다양한 레이아웃 디자인).

예를 들어 많은 애플리케이션이 큰 화면에 "창 두 개" 패턴을 구현합니다(앱은 하나의 창에 항목 목록을 표시하고 다른 창에 콘텐츠를 표시할 수 있음). 태블릿과 TV는 화면에 두 개의 창이 동시에 들어갈 만큼 크지만 전화 화면은 창을 따로 표시해야 합니다. 그러므로 이런 레이아웃을 구현하려면 다음 파일이 있어야 합니다.

  • res/layout/main.xml, 단일 창(기본) 레이아웃:
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <fragment android:id="@+id/headlines"
                  android:layout_height="fill_parent"
                  android:name="com.example.android.newsreader.HeadlinesFragment"
                  android:layout_width="match_parent" />
    </LinearLayout>
  • res/layout-large/main.xml, 창 두 개 레이아웃:
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="horizontal">
        <fragment android:id="@+id/headlines"
                  android:layout_height="fill_parent"
                  android:name="com.example.android.newsreader.HeadlinesFragment"
                  android:layout_width="400dp"
                  android:layout_marginRight="10dp"/>
        <fragment android:id="@+id/article"
                  android:layout_height="fill_parent"
                  android:name="com.example.android.newsreader.ArticleFragment"
                  android:layout_width="fill_parent" />
    </LinearLayout>

두 번째 레이아웃의 디렉터리 이름에 있는 large 한정자를 확인하세요. 이 레이아웃은 큰 화면으로 분류되는 기기에서 선택됩니다(예: 7인치 태블릿 이상). 다른 레이아웃(한정자 없음)은 화면이 더 작은 기기에서 선택됩니다.

최소 너비 한정자 사용

3.2 이전 Android 기기에서 개발자가 겪는 어려움은 "큰" 화면 크기 빈입니다. 여기에는 Dell Streak과 오리지널 Galaxy Tab, 일반 7인치 태블릿이 포함됩니다. 그러나 이 기기들이 모두 "큰" 화면으로 분류되기는 하지만 이 카테고리에 있는 다양한 기기(예: 5인치, 7인치 기기)에 다양한 레이아웃을 보여주고자 하는 애플리케이션이 많습니다. 그래서 Android는 Android 3.2에 특히 "최소 너비 한정자"를 도입했습니다.

최소 너비 한정자는 dp 기준으로 일정한 최소 너비가 지정된 화면을 표적으로 삼을 수 있습니다. 예를 들어 일반 7인치 태블릿은 최소 너비가 600dp입니다. 이 화면에서 UI를 창 두 개로 표시하고 싶다면(단, 작은 화면에는 단일 목록으로 표시), 이전 섹션처럼 단일 창과 창 두 개 레이아웃을 사용할 수 있지만 large 크기 한정자 대신 sw600dp를 사용하여 최소 너비가 600dp인 화면에 창 두 개 레이아웃을 사용한다고 나타냅니다.

  • res/layout/main.xml, 단일 창(기본) 레이아웃:
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <fragment android:id="@+id/headlines"
                  android:layout_height="fill_parent"
                  android:name="com.example.android.newsreader.HeadlinesFragment"
                  android:layout_width="match_parent" />
    </LinearLayout>
  • res/layout-sw600dp/main.xml, 창 두 개 레이아웃:
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="horizontal">
        <fragment android:id="@+id/headlines"
                  android:layout_height="fill_parent"
                  android:name="com.example.android.newsreader.HeadlinesFragment"
                  android:layout_width="400dp"
                  android:layout_marginRight="10dp"/>
        <fragment android:id="@+id/article"
                  android:layout_height="fill_parent"
                  android:name="com.example.android.newsreader.ArticleFragment"
                  android:layout_width="fill_parent" />
    </LinearLayout>

최소 너비가 600dp 이상인 기기는 layout-sw600dp/main.xml(창 두 개) 레이아웃을 선택하고 그보다 작은 화면은 layout/main.xml(단일 창) 레이아웃을 선택합니다.

그러나 3.2 이전 버전 기기는 sw600dp를 크기 한정자로 인식하지 않으므로 이 방법을 사용할 수 없습니다. 이 기기에서는 large 한정자도 사용해야 합니다. res/layout-large/main.xml이라는 이름의 파일이 있어야 하며, 이 파일은 res/layout-sw600dp/main.xml과 동일합니다. 다음 섹션에서는 이 방식으로 레이아웃 중복을 피하는 기술을 보여줍니다.

레이아웃 별칭 사용

최소 너비 한정자는 Android 3.2 이상에서만 사용할 수 있습니다. 그러므로 추상 크기 빈(small, normal, large 및 xlarge)을 사용하여 이전 버전과 호환시켜야 합니다. 예를 들어 전화에서는 단일 창 UI를 표시하고 7인치 태블릿, TV, 다른 기기에서는 다중 창 UI를 표시하도록 UI를 디자인하고 싶다면 다음 파일을 제공해야 합니다.

  • res/layout/main.xml: 단일 창 레이아웃
  • res/layout-large: 다중 창 레이아웃
  • res/layout-sw600dp: 다중 창 레이아웃

마지막 두 개의 파일은 하나는 Android 3.2 기기와 일치하고 다른 하나는 이전 버전의 Android가 탑재된 태블릿 및 TV를 위한 것으로 서로 동일한 파일입니다.

이 경우 별칭 파일을 사용하면 태블릿 및 TV용으로 동일한 파일이 중복되지 않도록 하고 이를 관리해야 하는 번거로움을 없앨 수 있습니다 예를 들어 다음 레이아웃을 정의할 수 있습니다.

  • res/layout/main.xml, 단일 창 레이아웃
  • res/layout/main_twopanes.xml, 창 두 개 레이아웃

다음 파일 두 개를 추가합니다.

  • res/values-large/layout.xml:
    <resources>
        <item name="main" type="layout">@layout/main_twopanes</item>
    </resources>
    
  • res/values-sw600dp/layout.xml:
    <resources>
        <item name="main" type="layout">@layout/main_twopanes</item>
    </resources>
    

마지막 파일 두 개는 콘텐츠가 같지만 실제로는 레이아웃을 정의하지 않습니다. mainmain_twopanes의 별칭이 되도록 설정할 뿐입니다. 이 파일들에 largesw600dp 선택기가 있으므로 Android 버전과 관계없이 태블릿과 TV에 적용됩니다(3.2 이전 태블릿과 TV는 large, 3.2 이후 버전은 sw600dp와 일치).

방향 한정자 사용

가로와 세로 방향에서 모두 잘 작동하는 레이아웃이 있지만 대부분은 조정이 필요합니다. 다음은 News Reader 샘플 앱으로 각 화면 크기와 방향에서 레이아웃이 동작하는 방식을 보여줍니다.

  • 작은 화면, 세로: 단일 창, 로고 포함
  • 작은 화면, 가로: 단일 창, 로고 포함
  • 7인치 태블릿, 세로: 단일 창, 작업 모음 포함
  • 7인치 태블릿, 가로: 이중 창, 넓게, 작업 모음 포함
  • 10인치 태블릿, 세로: 이중 창, 좁게, 작업 모음 포함
  • 10인치 태블릿, 가로: 이중 창, 넓게, 작업 모음 포함
  • TV, 가로: 이중 창, 넓게, 작업 모음 포함

이 레이아웃 각각은 res/layout/ 디렉터리의 XML 파일에서 정의됩니다. 각 레이아웃을 다양한 화면 구성에 할당하기 위해 앱이 레이아웃 별칭을 사용하여 각 레이아웃을 구성에 일치시킵니다.

res/layout/onepane.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment android:id="@+id/headlines"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.HeadlinesFragment"
              android:layout_width="match_parent" />
</LinearLayout>

res/layout/onepane_with_bar.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <LinearLayout android:layout_width="match_parent" 
                  android:id="@+id/linearLayout1"  
                  android:gravity="center"
                  android:layout_height="50dp">
        <ImageView android:id="@+id/imageView1" 
                   android:layout_height="wrap_content"
                   android:layout_width="wrap_content"
                   android:src="@drawable/logo"
                   android:paddingRight="30dp"
                   android:layout_gravity="left"
                   android:layout_weight="0" />
        <View android:layout_height="wrap_content" 
              android:id="@+id/view1"
              android:layout_width="wrap_content"
              android:layout_weight="1" />
        <Button android:id="@+id/categorybutton"
                android:background="@drawable/button_bg"
                android:layout_height="match_parent"
                android:layout_weight="0"
                android:layout_width="120dp"
                style="@style/CategoryButtonStyle"/>
    </LinearLayout>

    <fragment android:id="@+id/headlines" 
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.HeadlinesFragment"
              android:layout_width="match_parent" />
</LinearLayout>

res/layout/twopanes.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal">
    <fragment android:id="@+id/headlines"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.HeadlinesFragment"
              android:layout_width="400dp"
              android:layout_marginRight="10dp"/>
    <fragment android:id="@+id/article"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.ArticleFragment"
              android:layout_width="fill_parent" />
</LinearLayout>

res/layout/twopanes_narrow.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal">
    <fragment android:id="@+id/headlines"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.HeadlinesFragment"
              android:layout_width="200dp"
              android:layout_marginRight="10dp"/>
    <fragment android:id="@+id/article"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.ArticleFragment"
              android:layout_width="fill_parent" />
</LinearLayout>

모든 가능한 레이아웃이 정의되었으므로 구성 한정자를 사용하여 각 구성에 올바른 레이아웃을 매핑하는 작업만 남았습니다. 이제 레이아웃 별칭 기술을 사용하여 레이아웃을 매핑할 수 있습니다.

res/values/layouts.xml:

<resources>
    <item name="main_layout" type="layout">@layout/onepane_with_bar</item>
    <bool name="has_two_panes">false</bool>
</resources>

res/values-sw600dp-land/layouts.xml:

<resources>
    <item name="main_layout" type="layout">@layout/twopanes</item>
    <bool name="has_two_panes">true</bool>
</resources>

res/values-sw600dp-port/layouts.xml:

<resources>
    <item name="main_layout" type="layout">@layout/onepane</item>
    <bool name="has_two_panes">false</bool>
</resources>

res/values-large-land/layouts.xml:

<resources>
    <item name="main_layout" type="layout">@layout/twopanes</item>
    <bool name="has_two_panes">true</bool>
</resources>

res/values-large-port/layouts.xml:

<resources>
    <item name="main_layout" type="layout">@layout/twopanes_narrow</item>
    <bool name="has_two_panes">true</bool>
</resources>

나인 패치 비트맵 사용

일반적으로 다양한 화면 크기를 지원하면 이미지 리소스도 다양한 크기에 따라 조절되어야 합니다. 예를 들어 버튼 배경은 적용되는 버튼 형태와 일치해야 합니다.

크기를 바꿀 수 있는 구성 요소에 단순한 이미지를 사용하면 런타임이 이미지를 균일하게 늘리거나 줄여서 다소 보기 좋지 않은 결과가 나온다는 것을 금세 알 수 있습니다. 해결책은 나인 패치 비트맵을 사용하는 것입니다. 나인 패치 비트맵은 늘릴 수 있는 영역과 늘릴 수 없는 영역을 나타내는 특수한 형식의 PNG 파일입니다.

그러므로 다양한 크기가 포함된 구성 요소에 사용할 비트맵을 디자인할 때는 항상 나인 패치를 사용하세요. 비트맵을 나인 패치로 변환하려면 일반 이미지(그림 4, 명확히 보이도록 4배 확대)부터 시작합니다.

그림 4. button.png

그리고 SDK의 draw9patch 유틸리티에서 실행합니다(tools/ 디렉터리에 위치). 여기서 왼쪽과 위쪽 경계선을 따라 픽셀을 그려서 늘려야 하는 영역을 표시할 수 있습니다. 왼쪽과 아래쪽 경계선을 따라 픽셀을 그려서 콘텐츠를 배치해야 하는 영역을 표시할 수도 있습니다. 그 결과는 그림 5와 같습니다.

그림 5.button.9.png

경계선을 따라 표시된 검은색 픽셀을 확인하세요. 위와 왼쪽 경계선에 있는 픽셀은 이미지를 늘릴 수 있는 곳을 나타내고 오른쪽과 아래쪽 경계선에 있는 픽셀은 콘텐츠를 배치해야 할 곳을 나타냅니다.

.9.png 확장자도 확인하세요. 이 확장자를 통해 프레임워크가 일반 PNG 이미지가 아니라 나인 패치 이미지라는 것을 감지할 수 있으므로 이 확장자를 사용해야 합니다.

이 백그라운드를 구성 요소에 적용하면( android:background="@drawable/button" 설정) 프레임워크가 그림 6의 다양한 크기에서 보이는 바와 같이 버튼 크기에 맞게 이미지를 올바르게 늘립니다.

그림 6. 다양한 크기에서 button.9.png 나인 패치를 사용하는 버튼