View on GitHub
mixi-inc/AndroidTraining
アプリのレイアウト作成
この章では、Android アプリ画面のレイアウトの作り方を学びます。

参考:Layouts | Android Developers
参考:Supporting Different Densities | Android Developers
参考:Supporting Multiple Screens | Android Developers

目次

画面を構成する要素

画面を構成する要素となる View には以下のようなものがあります。

TextView、EditText、ImageView、Button、CheckBox、RadioButton といったウィジェット(Widget)と、
LinearLayout、RelativeLayout、FrameLayout といったウィジェットを取りまとめるレイアウト(Layout)です。

これらウィジェットとレイアウトをまとめて、View と呼びます。

View を配置する

View を配置する際に使用する各種パラメータについて説明します。

サイズ

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:text="hello_world" />

</RelativeLayout>
要素 意味
android:layout_height Viewの高さ
android:layout_width Viewの横幅

android:layout_heightandroid:layout_widthで指定できる値は、数値 dp,sp,pxなどとmatch_parent(fill_parent)とwrap_contentとなります。

match_parentは、親のViewと同じサイズに調整されます。
wrap_contentは、コンテンツを表示するのに十分なサイズに調整されます。上記サンプルの場合、TextViewのサイズは”hello_world”を表示するのに必要なサイズへと調整されます。

fill_parentmatch_parentと同じ機能ですが、API Level 8から非推奨となっています。
API Level 7以下をターゲットとしないのであればmatch_parentを使用しましょう。

パディング、マージン

padding(パディング)、margin(マージン)を使用することでViewの余白を設定することができます。
パディングでViewの内側の余白をマージンで外側の余白を調整することができます。

padding_margin

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#f2f2f2"
        android:padding="12dp"
        android:text="Padding"
        android:textSize="30sp" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="12dp"
        android:background="#f2f2f2"
        android:text="Margin"
        android:textSize="30sp" />
要素 意味
android:padding 上下左右のパディング
android:paddingTop 上のパディング
android:paddingBottom 下のパディング
android:paddingLeft 左のパディング
android:paddingRight 右のパディング
android:layout_margin 上下左右のマージン
android:layout_marginTop 上のマージン
android:layout_marginBottom 上下のマージン
android:layout_marginLeft 左のマージン
android:layout_marginRight 右のマージン

単位

Androidには様々な画面サイズ、画面密度(解像度)の端末が存在します。
そのためpx(pixel)でサイズ指定をすると端末によって画像が小さくなったり大きくなったりしてしまいます。
Androidではこのような複数解像度の端末に対応するため以下の単位が用意されています。

dp(dip)

dp(dip) は density-independent pixels (密度非依存のピクセル) の略です。

dipは160dpi(dots per inch) の画面を基準とされており、1dipは160dpiの画面では1ピクセルと同等と定義されています。
この関係を式にすると以下のようになり、解像度に対する1dipが何pxに相当するかがわかります。

px = dp * (dpi / 160)

320dpiの画面の場合だと1dipは2pxに自動的に換算され、画面上に反映されることになります。
よって、サイズにdpを使用することで特に意識することなく複数のが解像度端末に対応することができます。

dipとdpは両方使えますがdpの記述の方がspと統一感があるのでわかりやすいです。

sp

spはscale-independent pixels(スケール非依存のピクセル)の略です。

指定したサイズは、解像度とユーザーが設定したフォントサイズにあわせて自動的にスケールされます。

位置

Gravityを使用することでViewを指定した位置に配置することができます。
Gravityは重力という意味です。Viewに重力をかけることによって、かけた方向へViewが配置されます。
Gravityにはandroid:gravityandroid:layout_gravityが存在し、用途によって使い分けます。

要素 意味
android:gravity 内部の要素の位置を決めます
android:layout_gravity 自分の位置を決めます

android:gravity

内部にTextViewを持ったLinearLayoutandroid:gravity="right"を指定すると以下のようになります。
このことより、TextViewが右に整列されるのがわかります。
gravity_to_parent

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/black"
    android:gravity="right"
    android:orientation="vertical" >

    <TextView
        android:layout_width="180dp"
        android:layout_height="100dp"
        android:background="@android:color/white"
        android:text="Gravity Right1"
        android:textSize="20sp" />

    <TextView
        android:layout_width="180dp"
        android:layout_height="100dp"
        android:background="@android:color/darker_gray"
        android:text="Gravity Right2"
        android:textSize="20sp" />

</LinearLayout>

次は、LinearLayout内部のTextView自体にandroid:gravity="right"を指定すると以下のようになります。
このことより、TextView内の文字が右に整列されます。
gravity_to_textview

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

    <TextView
        android:layout_width="180dp"
        android:layout_height="100dp"
        android:background="@android:color/white"
        android:gravity="right"
        android:text="Gravity Right1"
        android:textSize="20sp" />

    <TextView
        android:layout_width="180dp"
        android:layout_height="100dp"
        android:background="@android:color/darker_gray"
        android:gravity="right"
        android:text="Gravity Right2"
        android:textSize="20sp" />

</LinearLayout>

android:layout_gravity

内部にTextViewを持ったLinearLayoutandroid:layout_gravity="right"を指定すると以下のようになります。
LinearLayout自体の位置が右に移動しTextViewLinearLayout内での位置は変わっていません。
layout_gravity_to_parent

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

    <LinearLayout
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        android:background="#d1992f"
        android:orientation="vertical" >

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@android:color/white"
            android:text="Gravity Right1"
            android:textSize="20sp" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@android:color/darker_gray"
            android:text="Gravity Right2"
            android:textSize="20sp" />
    </LinearLayout>

</LinearLayout>

次に、LinearLayout内部のTextView自体にandroid:layout_gravity="right"を指定すると以下のようになります。
このことより、TextViewが右に整列されます。
layout_gravity_to_textview

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

    <LinearLayout
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:background="#d1992f"
        android:orientation="vertical" >

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="right"
            android:background="@android:color/white"
            android:text="Gravity Right1"
            android:textSize="20sp" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="right"
            android:background="@android:color/darker_gray"
            android:text="Gravity Right2"
            android:textSize="20sp" />
    </LinearLayout>

</LinearLayout>

RTL support

RTLとはright-to-left の略でアラビア語やヘブライ語などの右から左に記述する言語のことをRTL言語と呼ぶことがあります。
Androidでは古くからRTL言語の表示をすることは可能でしたが、4.2から機能が強化されました。ドキュメントではfull native support for RTLと記載されています。(実際には4.4で画像反転等の機能追加が行われるなどしています)
これにより、レイアウト上でもRTLを意識したデザインをすることが可能になりました。
レイアウトファイルをRTL対応した場合、どのように表示されるかは、下図のように左右が反転したデザインとなります。

ltr setting

rtl setting

日本国内だけをターゲットにする場合は、対応が必須とは言えませんができるだけ対応したほうがいいといえるでしょう。

対応方法

  1. AndroidManifest.xmlの<application>内にadd android:supportsRtl="true"と記述する
  2. targetSdkVersionに応じてレイアウトファイル内の記述を変更する
    • minSdkVersion17未満
      left,rightが含まれる値にstart,endの指定を追加し、両方の記述をする
    • minSdkVersion17以上
      left,rightが含まれる値をstart,endの指定に変更する

で、対応が完了します。

レイアウトファイル内の記述の変更について具体的に記述すると

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        android:paddingLeft="2dp"
        android:background="@android:color/darker_gray"
        android:text="Gravity Right2"
        android:textSize="20sp" />

と記述されていたものを、minSdkVersion17未満の場合は

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="left|start"
        android:paddingLeft="2dp"
        android:paddingStart="2dp"
        android:background="@android:color/darker_gray"
        android:text="Gravity Right2"
        android:textSize="20sp" />

とします。この時にpaddingLeftに対してpaddingStartが追加されていることに気をつけてください。
minSdkVersion17以上の場合は、

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="start"
        android:paddingStart="2dp"
        android:background="@android:color/darker_gray"
        android:text="Gravity Right2"
        android:textSize="20sp" />

とします。

レイアウトを作成する

レイアウトはUIの構造を定義します。
LinearLayout、RelativeLayout、FrameLayout、GridLayout、ListView、ScrollViewなどがあり、用途によって使い分けます。

LinearLayout

Viewを縦横に並べることができます。
android:orientationで並べる方向を決めます。”horizontal” か “vertical”を指定します。
未設定の場合はhorizontalが適用されます。

横に並べる

layout_linear_horizontal

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

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Text1" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Text2" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Text3" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Text4" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Text5" />

</LinearLayout>

android:orientation="horizontal"を指定することによりTextViewが横に並んでいます。
(android:orientation="horizontal"を記述していなくても同様の画面レイアウトになります)

縦に並べる

layout_linear_vertical

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

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Text1" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Text2" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Text3" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Text4" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Text5" />

</LinearLayout>

android:orientation="vertical"を指定することによりTextViewが縦に並んでいます。

Layout Weight

LinearLayoutは個々の内部View(子View)に対してandroid:layout_weightを割り当てることができます。
android:layout_weightはViewを配置したときに残っているスペースを埋めることができるとても重要な機能です。

例えば、2つボタンが横並びに配置されているとします。片方のボタンにのみandroid:layout_weight="1"を指定します。
指定していないボタンのandroid:layout_weight0として扱われます。
android:layout_weightは指定された値を元にスペースを割り当てます。この例では割り当てられる比率は1:0となります。
よって、指定されたボタンは残っているスペースを全て使うことになり目一杯横方向に伸びます。

layout_linear_weight

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

    <Button
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="Button1" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button2" />

</LinearLayout>

RelativeLayout

相対的にVeiwを配置することができます。
layout_relative

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <Button
        android:id="@+id/a"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:text="A"
        android:textSize="16sp" />

    <Button
        android:id="@+id/b"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignTop="@+id/a"
        android:layout_toRightOf="@+id/a"
        android:text="B" />

    <Button
        android:id="@+id/c"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/b"
        android:layout_below="@+id/b"
        android:text="C" />

    <Button
        android:id="@+id/d"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@+id/c"
        android:layout_alignTop="@+id/b"
        android:layout_toRightOf="@+id/b"
        android:text="D" />

</RelativeLayout>
  1. ボタンAをandroid:layout_centerHorizontal="true"で横方向の中心に、さらにandroid:layout_centerVertical="true"で縦方向の中心に配置しています。またandroid:id="@+id/a"と宣言することでボタンAの識別子を付与しています。
  2. ボタンBはandroid:layout_toRightOf="@+id/a"を指定することでボタンAの右に配置しており、android:layout_alignTop="@+id/a"で上端をボタンAの上端に合わせています。
  3. ボタンCはandroid:layout_below="@+id/b"を指定することでボタンBの下に配置しており、android:layout_alignLeft="@+id/b"で左端をボタンBの左端に合わせるようにしています。
  4. ボタンDはandroid:layout_toRightOf="@+id/b"を指定することでボタンBの右に配置しており、android:layout_alignTop="@+id/b"で上端をボタンBの上端、android:layout_alignBottom="@+id/c"で上端をボタンCの下端に配置するようにしています。
要素 意味
android:layout_above 指定したidのViewの上に配置します
android:layout_alignBaseline 指定したidのViewのベースラインにあわせて配置します
android:layout_alignBottom 指定したidのViewの下端にあわせて配置します
android:layout_alignLeft 指定したidのViewの左端にあわせて配置します
android:layout_alignParentBottom 親となるViewの下端にあわせて配置します
android:layout_alignParentLeft 親となるViewの左端にあわせて配置します
android:layout_alignParentRight 親となるViewの右端にあわせて配置します
android:layout_alignParentTop 親となるViewの上端にあわせて配置します
android:layout_alignRight 指定したidのViewの右端にあわせて配置します
android:layout_alignTop 指定したidのViewの上端にあわせて配置します
android:layout_below 指定したidのViewの下に配置します
android:layout_centerHorizontal 親となるViewの横方向の中央に配置します
android:layout_centerInParent 親となるViewの中央に配置します
android:layout_centerVertical 親となるViewの縦方向の中央に配置します
android:layout_toLeftOf 指定したidのViewの左に配置します
android:layout_toRightOf 指定したidのViewの右に配置します

FrameLayout

FrameLayoutはViewを重ねて配置するのに使用します。
layout_frame

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/FrameLayout1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <!-- 赤 -->
    <ImageView
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:background="#FF0000" />

    <!-- 緑 -->
    <ImageView
        android:layout_width="190dp"
        android:layout_height="190dp"
        android:background="#00FF00" />

    <!-- 青 -->
    <ImageView
        android:layout_width="180dp"
        android:layout_height="180dp"
        android:background="#0000FF" />

</FrameLayout>

FrameLayout内の上から順にViewが配置されます。
上記の場合は、赤のImageViewの上に青のImageViewが配置され、その上に緑のImageViewが配置されています。

ScrollView

ScrollViewは画面にレイアウトが収まらない場合、収まらない分をスクロールして表示するための使用します。
layout_scroll

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/ScrollView1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" >

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="hello_world"
            android:textSize="30dp" />

        ・・・

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="hello_world"
            android:textSize="30dp" />

    </LinearLayout>

</ScrollView>

ScrollViewが持つルートとなるViewは1つでなければなりません。
上記サンプルの場合、複数のTextViewLinearLayoutでまとめ、LinearLayoutをルートとしています。
ScrollViewの直下にTextViewを複数配置するということはできません。

実習

実習プロジェクトを開き、以下の項目に取り組んでください。
レイアウトの参考として、スクリーンショットと同じようなレイアウトになるようにします。
詳細は、各レイアウトファイルの中に記述されています。

実習プロジェクトの開き方

  1. 実習プロジェクトを開くにはまず、Android Training資料を手元にダウンロードしてください。
  2. 次にAndroid Studioを起動し、”Quick Start”メニューの”Open an existing Android Studio project”を選択します。
  3. プロジェクト選択画面で、1の手順でダウンロードしたディレクトリ内のAndroidStudio/practice/fundamentals/1stの中にある、該当のbuild.gradleを選択します。LinearLayoutPractice/Practice1であれば、 AndroidStudio/practice/fundamentals/1st/LinearLayoutPractice/Practice1/build.gradleを選択してください。
  4. “Import Project from Gradle”という確認ダイアログが出ますので、”Use default gradle wrapper(recommended)”を選択してください。
  5. 以上の手順でプロジェクトが開いたら、ウィンドウ左側のツリーから app > res > layout > activity_main.xml を選択し、実習に取り組んでください。

実習一覧

  1. LinearLayoutPractice
  2. RelativeLayoutPractice
  3. FrameLayoutPractice
  4. ScrollViewPractice

課題

以下の項目に取り組んでください。
レイアウトの参考として、スクリーンショットと同じようなレイアウトになるようにします。
実習プロジェクトを開くには 詳細は、各レイアウトファイルの中に記述されています。

課題プロジェクトの開き方

  1. 実習プロジェクトの開き方2までは同じです
  2. プロジェクト選択画面で、1の手順でダウンロードしたディレクトリ内のAndroidStudio/assignments/fundamentals/1stの中にある、該当のbuild.gradleを選択します。LayoutAssignment1であれば、 AndroidStudio/assignments/fundamentals/1st/LayoutAssignment1/build.gradleを選択してください。
  3. “Import Project from Gradle”という確認ダイアログが出ますので、”Use default gradle wrapper(recommended)”を選択してください。
  4. 以上の手順でプロジェクトが開いたら、ウィンドウ左側のツリーから app > res > layout > activity_main.xml を選択し、課題に取り組んでください。

課題一覧

  1. LayoutAssignment1で、下記の画像のようなレイアウトが作成されています。このレイアウトを元に、自由にレイアウトを編集してください。
  2. LayoutAssignment2で、layout_weightがネストしているので、LinearLayoutRelativeLayoutに置き換かえてネストを解消してください。
  3. LayoutAssignment2で下記の画像のようなレイアウトを作成してください(課題 2 と同じプロジェクトで実行する)
  4. LayoutAssignment3、下記の画像のようなレイアウトを作成してください。レイアウトファイル中に、指示と条件、ヒントがあります。
    • スクリーンショット
    • 3 つ並べるボタンの画像の指定は、それぞれ android:src=”@android:drawable/ic_menu_call”, android:src=”@android:drawable/ic_menu_send”, android:src=”@android:drawable/ic_menu_share” とします