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 縦表示(左)と横表示(右)でのニュース リーダーのサンプルアプリ

RelativeLayout を使用する

ネストされた LinearLayout インスタンスを、"wrap_content" および "match_parent" のサイズと組み合わせることによって、かなり複雑なレイアウトを構築できます。ただし、LinearLayout では子ビューの空間的な位置関係を正確に制御できません。LinearLayout でのビューは単に横並びになります。 子ビューを直線以外の方向に配置する必要がある場合は、RelativeLayout の方が適しています。これにより、コンポーネント間の空間的な関係に重点をおいてレイアウトを指定できます。 たとえば 1 つの子ビューを画面の左側に揃え、別のビューを画面の右側に揃えることができます。

次に例を示します。

<?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 で指定したとおりに保持されています。

サイズ修飾子を使用する

前のセクションで説明したような柔軟なレイアウトまたは相対的なレイアウトで得られる利用度は限定的なものです。 これらのレイアウトは、コンポーネント内またはコンポーネントの周囲のスペースを広げることによってさまざまな画面に対応できるとはいえ、それぞれの画面サイズで最適なユーザー エクスペリエンスを得られるとは限りません。 そのため、アプリケーションでは、柔軟なレイアウトを実装するだけでなく、さまざまな画面構成を対象とした複数の代替レイアウトを提供する必要もあります。 このような場合、設定修飾子を使用します。設定修飾子により、現在の端末の設定に基づいた適切なリソースをランタイムが自動的に選択できるようになります(さまざまな画面サイズ用の別のレイアウト デザインなど)。

たとえば、多くのアプリケーションでは、大きい画面に対して「2 ペイン」パターンが実装されます(アプリの 1 つのペインにアイテムリストが表示され、もう 1 つのペインにコンテンツが表示されるような場合です)。 タブレットや TV は十分に大きいので 2 つのペインを同時に画面上に表示できますが、携帯端末の画面ではこれらを別々に表示する必要があります。 これらのレイアウトを実装するために、次のファイルが必要になります。

  • 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、2 ペイン レイアウト:
    <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>

2 つ目のレイアウトのディレクトリ名に含まれている large 修飾子に注目してください。このレイアウトは、画面が large として分類されている端末(7 インチ タブレット以上)で選択されます。 修飾子のないもう一方のレイアウトは小さい端末で選択されます。

最小幅の修飾子を使用する

開発者にとって、3.2 よりも前の Android 端末で難しかったことの 1 つは、「大きい」画面サイズ カテゴリで、これには Dell Streak、最初の Galaxy Tab、7 インチ タブレット全般が含まれます。 このカテゴリのさまざまな端末(5 インチや 7 インチの端末など)はすべて「大きい」画面と見なされていますが、多くのアプリケーションでは、端末によって異なるレイアウトを表示することが必要な場合があります。 そのため、Android では、特に Android 3.2 で「最小幅」の修飾子を導入しました。

最小幅の修飾子によって、特定の最小幅(dp 単位)を持つ画面を対象とすることができます。 たとえば、標準的な 7 インチ タブレットの最小幅は 600 dp であるため、画面上の 2 つのペインで UI を表示する場合(ただし、それよりも小さい画面では 1 つのリスト)、単一ペイン レイアウトと 2 ペイン レイアウトについては前のセクションの同じ 2 つのレイアウトを使用できますが、large サイズ修飾子ではなく sw600dp を使用して、2 ペイン レイアウトが最小幅 600 dp の画面用であることを指定します。

  • 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、2 ペイン レイアウト:
    <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>

つまり、最小幅が 600 dp 以上の端末では layout-sw600dp/main.xml(2 ペイン)のレイアウトが選択され、小さい画面では layout/main.xml(単一ペイン)のレイアウトが選択されます。

ただし、3.2 よりも前の端末では、sw600dp がサイズ修飾子として認識されないため、うまく機能しません。そのため large 修飾子も使用する必要があります。 したがって、res/layout-sw600dp/main.xml と内容が同一の、res/layout-large/main.xml という名前のファイルが必要になります。 次のセクションでは、このようなレイアウト ファイルの重複を避ける手法について説明します。

レイアウト エイリアスを使用する

最小幅の修飾子は Android 3.2 以降でのみ使用できます。そのため、以前のバージョンとの互換性を維持するために抽象サイズのカテゴリ(small(小)、normal(通常)、large(大)、xlarge(特大))も使用する必要があります。 たとえば携帯端末では単一ペインの UI を表示し、7 インチ タブレットや TV などの大きい端末ではマルチペインの UI を表示するように UI を設計する場合は、次のファイルを提供する必要があります。

  • res/layout/main.xml: 単一ペイン レイアウト
  • res/layout-large: マルチペイン レイアウト
  • res/layout-sw600dp: マルチペイン レイアウト

後の 2 つのファイルは、1 つは Android 3.2 端末用であり、もう 1 つは以前のバージョンの Android のタブレットと TV 用であるため、同一です。

このタブレットと TV 用の同じファイルの重複(およびそのために生じるメンテナンスの問題)を避けるために、エイリアス ファイルを使用できます。 たとえば次のレイアウトを定義できます。

  • res/layout/main.xml、単一ペイン レイアウト
  • res/layout/main_twopanes.xml、2 ペイン レイアウト

これら 2 つのファイルを追加します。

  • 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>
    

後者の 2 つのファイルは内容は同じですが、実際にはレイアウトを定義していません。 単に、mainmain_twopanes のエイリアスになるように設定しています。これらのファイルには large および sw600dp セレクターがあるため、Android のバージョンに関係なくタブレットと TV に適用されます(3.2 よりも前のタブレットと TV には large が、3.2 以降には sw600dp が対応します)。

画面の向きの修飾子を使用する

一部のレイアウトは横向きと縦向きの両方で適切に表示されますが、そのほとんどは調整を行うと効果があります。 ここではニュース リーダーのサンプルアプリで、レイアウトがそれぞれの画面サイズと画面の向きでどのように表示されるかを示します。

  • 小さい画面、縦: 単一ペイン、ロゴあり
  • 小さい画面、横: 単一ペイン、ロゴあり
  • 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>

9-patch ビットマップを使用する

さまざまな画面サイズをサポートするということは、通常は画像リソースもさまざまなサイズに対応可能である必要があることを意味します。 たとえば、ボタンの背景は適用対象のボタンのあらゆる形状に対応する必要があります。

サイズが変わるコンポーネントで単純な画像を使用する場合、ランタイムによって画像が均一に引き延ばされたり縮められたりするため、結果があまりよくないことがすぐにわかります。 これに対処するためには、9-patch ビットマップを使用します。これは、引き延ばし可能および不可能な領域を指定する特別な形式の PNG ファイルです。

したがって、サイズが変わるコンポーネントで使用されるビットマップを設計する場合は、常に 9-patch を使用します。 ビットマップを 9-patch に変換するには、通常の画像から開始できます(図 4 - 見やすいように 4 倍にズームして表示)。

図 4. button.png

次に、 tools/ ディレクトリにある)SDK の draw9patch ユーティリティを使用してこれを実行します。ここでは、左と上の境界に沿ってピクセルを描画することにより、引き延ばす領域をマークできます。 また、右と下の境界に沿ってピクセルを描画することにより、コンテンツを保持する領域をマークできます。この結果は図 5 になります。

図 5. button.9.png

境界に沿った黒いピクセルに注意してください。上と左の境界にあるものは画像を引き延ばすことができる場所を示し、右と下の境界にあるものはコンテンツを配置する場所を示します。

また、.9.png という拡張子に注意してください。この拡張子を使用する必要があります。この拡張子により、フレームワークではこれが通常の PNG 画像ではなく 9-patch 画像であることが検出されるためです。

android:background="@drawable/button" を設定して)この背景をコンポーネントに適用すると、図 6 にさまざまなサイズで示すように、フレームワークによって画像はボタンのサイズに対応して正しく引き延ばされます。

図 6 さまざまなサイズの button.9.png 9-patch を使用したボタン