レイアウト   Part of Android Jetpack.

レイアウトは、アクティビティでの場合と同様、アプリでのユーザー インターフェースの構造を定義します。レイアウトのすべての要素は、ViewViewGroup のオブジェクトの階層を使用して作成されます。View は、通常、ユーザーが見て操作できるものを描画します。一方、ViewGroup は非表示のコンテナで、図 1 に示すように、View と他の ViewGroup のオブジェクトのレイアウト構造を定義します。

図 1. UI レイアウトを定義するビュー階層を示す図

View オブジェクトは、通常は「ウィジェット」と呼ばれ、ButtonTextView などの多数のサブクラスの 1 つです。ViewGroup オブジェクトは、通常は「レイアウト」と呼ばれ、LinearLayoutConstraintLayout などの異なるレイアウト構造を提供する多数のタイプの 1 つです。

レイアウトは、次の 2 つの方法で宣言できます。

  • XML で UI 要素を宣言する。Android では、ウィジェットやレイアウトの単純な XML ボキャブラリなど、View クラスとサブクラスに対応する単純な XML ボキャブラリが提供されます。

    Android Studio のレイアウト エディタを使用して、ドラッグ&ドロップ インターフェースで XML レイアウトをビルドすることもできます。

  • 実行時にレイアウト要素をインスタンス化する。アプリで View オブジェクトと ViewGroup オブジェクトをプログラムによって作成したり、プロパティを操作したりできます。

XML で UI を宣言すると、アプリの動作を制御するコードから、アプリの表示を切り離すことができます。XML ファイルを使用することで、異なる画面のサイズや向きのさまざまなレイアウトを簡単に提供できます(詳細については、さまざまな画面サイズのサポートをご覧ください)。

Android フレームワークでは、これらの方法のいずれか、または両方を柔軟に使用して、アプリの UI をビルドできます。たとえば、XML でアプリのデフォルト レイアウトを宣言し、そのレイアウトを実行時に変更できます。

ヒント: 実行時にレイアウトをデバッグするには、Layout Inspector ツールを使用します。

XML の記述

Android の XML ボキャブラリを使って、HTML でウェブページを作成するのと同じ方法で(ネストした一連の要素を使って)、UI レイアウトとそれに含まれる画面要素を素早く設計できます。

各レイアウト ファイルには、必ず 1 つのルート要素が含まれていて、そのルート要素は View または ViewGroup オブジェクトでなくてはなりません。ルート要素を定義したら、追加のレイアウト オブジェクトやウィジェットを子の要素として追加し、レイアウトを定義するビュー階層を徐々にビルドできます。TextViewButton を保持するために、縦方向の LinearLayout を使用する XML レイアウトの例を次に示します。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical" >
    <TextView android:id="@+id/text"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="Hello, I am a TextView" />
    <Button android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello, I am a Button" />
</LinearLayout>

XML でレイアウトを宣言した後、そのファイルを Android プロジェクトの res/layout/ ディレクトリ内で .xml 拡張子を付けて保存して、正しくコンパイルされるようにします。

レイアウト XML ファイルの構文の詳細については、レイアウト リソースのドキュメントをご覧ください。

XML リソースの読み込み

アプリをコンパイルすると、各 XML レイアウト ファイルは View リソースにコンパイルされます。Activity.onCreate() コールバックの実装で、アプリコードからレイアウト リソースを読み込む必要があります。これを行うには、setContentView() を呼び出し、R.layout.layout_file_name の形式でレイアウト リソースへの参照を渡します。たとえば、XML レイアウトが main_layout.xml として保存されている場合、次のようにしてアクティビティに読み込みます。

Kotlin

fun onCreate(savedInstanceState: Bundle) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.main_layout)
}

Java

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main_layout);
}

アクティビティの onCreate() コールバック メソッドは、アクティビティが起動されるときに、Android フレームワークによって呼び出されます(ライフサイクルに関する説明については、アクティビティのドキュメントをご覧ください)。

属性

すべての View オブジェクトと ViewGroup オブジェクトでは、独自のさまざまな XML 属性がサポートされています。一部の属性は、View オブジェクト特有のものですが(たとえば TextView では textSize 属性がサポートされています)、これらの属性はこのクラスを拡張可能な View オブジェクトによっても継承されます。一部は、ルート View クラスから継承されるため、すべての View オブジェクトに共通します(id 属性など)。その他の属性は「レイアウト パラメータ」として考慮され、オブジェクトの親である ViewGroup オブジェクトとして定義された、View オブジェクトの特定のレイアウト方向を記述する属性となります。

ID

ツリー内で View を一意に識別するために、すべての View オブジェクトには、整数の ID が関連付けられることがあります。アプリがコンパイルされると、この ID は整数として参照されますが、一般的にその ID は id 属性で文字列としてレイアウト XML ファイルで割り当てられます。これは、すべての View オブジェクトに共通の XML 属性で(View クラスで定義)、非常に頻繁に使用されます。XML タグ内の ID の構文を次に示します。

android:id="@+id/my_button"

文字列の先頭にあるアットマーク(@)は、その XML パーサーが ID の残りの文字列をパースして展開し、それを ID リソースとして識別する必要があることを示します。プラス記号(+)は、それが新しいリソース名で、作成してリソースに追加する(R.java ファイル内で)必要があることを意味しています。Android フレームワークによって提供されるその他のさまざまな ID リソースがあります。Android リソース ID を参照するときは、プラス記号を使う必要はありませんが、次のように android パッケージ名前空間を追加する必要があります。

android:id="@android:id/empty"

android パッケージ名前空間付きの場合、ローカルのリソースクラスからではなく android.R リソースクラスから ID を参照するようになります。

ビューを作成してアプリからそれを参照するには、次のような共通のパターンがあります。

  1. レイアウト ファイルでビューとウィジェットを定義して、一意の ID を割り当てる。
    <Button android:id="@+id/my_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/my_button_text"/>
    
  2. 次に、View オブジェクトのインスタンスを作成し、それを次のようにレイアウトから取得する(通常は、onCreate() メソッド内で)。

    Kotlin

    val myButton: Button = findViewById(R.id.my_button)
    

    Java

    Button myButton = (Button) findViewById(R.id.my_button);
    

View オブジェクトの ID を定義することは、RelativeLayout を作成するときに重要になります。相対レイアウトでは、兄弟ビューは一意の ID で参照される別の兄弟ビューに相対するレイアウトを定義できます。

ツリー全体で ID が一意である必要はありませんが、探しているツリーの一部では、一意である必要があります。ほとんどの場合、それはツリー全体になりますが、可能であれば完全に一意になるようにすることが最善です。

レイアウト パラメータ

layout_something という名前の XML レイアウト属性では、含まれている ViewGroup に適した View に対するレイアウト パラメータが定義されます。

すべての ViewGroup クラスでは、ViewGroup.LayoutParams を拡張するネストされたクラスが実装されます。このサブクラスには、各子ビューのサイズと位置を ViewGroup に合うように定義するプロパティ タイプが含まれています。図 2 にあるように、親 ViewGroup によって、子 ViewGroup を含む、各子ビューのレイアウト パラメータが定義されます。

図 2. 各ビューに関連付けられたレイアウトのパラメータを含むビュー階層の視覚化

すべての LayoutParams サブクラスには、設定値に独自の構文があります。それぞれの子の要素では、その親に適した LayoutParams が定義される必要がありますが、自分の子に対して異なる LayoutParams を定義することもできます。

すべての ViewGroup には、幅と高さ(layout_widthlayout_height)が含まれていて、各ビューでそれが定義されている必要があります。多くの LayoutParams には、省略可能なマージンと境界線も含まれています。

必要になる頻度は少ない可能性はありますが、幅と高さを正確なサイズで指定することもできます。ほとんどの場合は、次の定数のいずれかを使って幅や高さを設定します。

  • wrap_content では、ビューがそのコンテンツに必要な寸法に合わせられます。
  • match_parent では、ビューが親 ViewGroup で許容される最大サイズで表示されます。

通常、ピクセルなどの絶対単位でレイアウトの幅と高さを指定することは推奨されません。密度非依存ピクセル単位(dp)、wrap_contentmatch_parent などで相対的なサイズ指定をする方が賢明です。そうすることで、さまざまな端末の画面サイズでアプリが正しく表示されるようになります。使用可能な測定タイプは、使用可能なリソースのドキュメントで定義されています。

レイアウトの位置

ビューの形状は、矩形です。ビューには、左と上の座標ペアで表される位置と、幅と高さで表される 2 次元があります。位置と寸法の単位はピクセルです。

getLeft()getTop() メソッドを呼び出してビューの位置を取得できます。前者では、左またはビューを表す矩形の X 座標が返されます。後者では、上またはビューを表す矩形の Y 座標が返されます。これらのメソッドでは、両方とも親に相対するビューの位置が返されます。たとえば、getLeft() で 20 が返される場合、そのビューはその直属の親の左端から右に 20 ピクセルの位置にあることを意味します。

他にも、getRight()getBottom() といった不要な計算を回避するさまざまな便利なメソッドが提供されています。これらのメソッドでは、ビューを表す矩形の右端と下端の座標が返されます。たとえば、getRight() を呼び出すことは、getLeft() + getWidth() の計算と同様です。

サイズ、パディング、マージン

ビューのサイズは、幅と高さで示します。実際に、ビューには幅と高さの値の 2 つのペアが保持されています。

最初のペアは、測定された幅と測定された高さと呼ばれます。これらの寸法で、その親内でのビューの大きさが定義されます。測定された寸法は、getMeasuredWidth()getMeasuredHeight() を呼び出して取得できます。

2 番目のペアは、単に高さと呼ばれたり、描画する幅描画する高さと呼ばれたりします。これらの寸法は、描画時とレイアウト後に、画面上のビューの実サイズを定義するものです。これらの値は、測定された幅や高さと異なる値にすることもできますが、必須ではありません。幅と高さは、getWidth()getHeight() を呼び出して取得できます。

その寸法を測るために、ビューではパディングも考慮されます。パディングは、ビューの上下左右に対し、ピクセルで記述されます。パディングを使って、特定のピクセル値でビュー コンテンツのオフセットを指定できます。たとえば、左側のパディングが 2 の場合は、左端から 2 ピクセル右にビューのコンテンツが寄せられます。パディングは setPadding(int, int, int, int) メソッドを使って設定でき、getPaddingLeft()getPaddingTop()getPaddingRight()getPaddingBottom() を呼び出してクエリできます。

ビューではパディングを定義できますが、マージンについてはサポートされていません。ただし、ViewGroup ではそのようなサポートが提供されています。詳細については、ViewGroupViewGroup.MarginLayoutParams をご覧ください。

寸法の詳細については、寸法の値をご覧ください。

共通レイアウト

ViewGroup クラスの各サブクラスでは、その中にネストするビューを表示する一意の方法が提供されます。Android プラットフォームにビルドされる共通のレイアウト タイプの一部を以下に示します。

注:別のレイアウト内で 1 つ以上のレイアウトをネストして UI を設計できますが、レイアウトの階層はできる限り浅くしておくようにしてください。ネストが浅いレイアウトの場合、レイアウトの描画がより速くなります。深いビュー階層より、ワイドなビュー階層の方がより良いと言えます。

線形レイアウト

子を横方向または縦方向の 1 行にまとめるレイアウト。ウィンドウの長さが画面の長さを超える場合は、スクロールバーが作成されます。

相対レイアウト

たとえば子 A を子 B の左になど、それぞれの子オブジェクトの位置を相対して指定したり、親の上に揃えてなど、親に相対して指定したりできます。

ウェブビュー

ウェブページを表示します。

アダプタを使ったレイアウトをビルドする

レイアウトのコンテンツが動的または事前設定されていないとき、AdapterView のサブクラスのレイアウトを使って、実行時にビューでレイアウトを設定できます。AdapterView クラスのサブクラスでは、Adapter を使ってそのレイアウトにデータがバインドされます。Adapter はデータソースと AdapterView レイアウト間の仲介として動作します。Adapter によって、配列やデータベース クエリのようなソースからデータが取得され、各エントリが AdapterView レイアウトに追加できるビューに変換されます。

アダプタでサポートされている共通レイアウトには次が含まれます。

リストビュー

スクロール可能な 1 列のリストが表示されます。

グリッドビュー

スクロール可能な列と行のグリッドが表示されます。

データを使ったアダプタビューを書き込む

AdapterView インスタンスを Adapter にバインドして、ListViewGridView などの AdapterView を入力できます。外部ソースのデータが取得され、各データエントリを表す View が作成されます。

Android ではさまざまな種類のデータの取得と AdapterView のビューのビルドに役立つ Adapter のサブクラスがいくつか提供されます。最も一般的なアダプタは次の 2 つです。

ArrayAdapter
データソースが配列の場合に、このアダプタを使います。デフォルトでは、ArrayAdapter によって各アイテムで toString() が呼び出され、そのコンテンツが TextView に配置されることで、各配列アイテムのビューが作成されます。

たとえば、ListView に表示する文字列の配列がある場合、コンストラクタを使って新しい ArrayAdapter を初期化して、各文字列と文字列配列のレイアウトを指定します。

Kotlin

val adapter = ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, myStringArray)

Java

ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
        android.R.layout.simple_list_item_1, myStringArray);

このコンストラクタの引数を次に示します。

  • アプリの Context
  • 配列の各文字列に対して TextView を含むレイアウト
  • 文字列配列

次に、ListViewsetAdapter() を呼び出します。

Kotlin

val listView: ListView = findViewById(R.id.listview)
listView.adapter = adapter

Java

ListView listView = (ListView) findViewById(R.id.listview);
listView.setAdapter(adapter);

各アイテムの外観をカスタマイズするには、自分の配列内のオブジェクト用に toString() メソッドをオーバーライドします。または、たとえば各配列アイテムに ImageView が必要な場合など、TextView 以外の各アイテムのビューを作成するには、ArrayAdapter クラスを拡張し、getView() をオーバーライドして、各アイテムに必要なビュータイプが返されるようにします。

SimpleCursorAdapter
Cursor から取得されたデータの場合は、このアダプタを使います。SimpleCursorAdapter を使う場合は、Cursor で各行に使うレイアウトを指定して、Cursor のどの列をレイアウトのビューに挿入するのかを指定してください。たとえば、人の名前と電話番号のリストを作成する場合は、それぞれの人の行と、その名前と番号の列を含む Cursor を返すクエリを実行します。次に、それぞれの結果に対して Cursor のどの列をレイアウトに含めるかを指定する文字列配列と、各列が配置されるべき対応するビューを指定する整数配列を作成します。

Kotlin

val fromColumns = arrayOf(ContactsContract.Data.DISPLAY_NAME,
                          ContactsContract.CommonDataKinds.Phone.NUMBER)
val toViews = intArrayOf(R.id.display_name, R.id.phone_number)

Java

String[] fromColumns = {ContactsContract.Data.DISPLAY_NAME,
                        ContactsContract.CommonDataKinds.Phone.NUMBER};
int[] toViews = {R.id.display_name, R.id.phone_number};

SimpleCursorAdapter をインスタンス化するとき、各結果に使うレイアウト、結果を含む Cursor、次の 2 つの配列を渡します。

Kotlin

val adapter = SimpleCursorAdapter(this,
        R.layout.person_name_and_number, cursor, fromColumns, toViews, 0)
val listView = getListView()
listView.adapter = adapter

Java

SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
        R.layout.person_name_and_number, cursor, fromColumns, toViews, 0);
ListView listView = getListView();
listView.setAdapter(adapter);

次に、SimpleCursorAdapter によって、対応する toViews ビューに各 fromColumns アイテムを挿入して、提供されたレイアウトを使って Cursor で各行のビューが作成されます。

アプリのライフサイクル中に、アダプタによって読み取られる基礎となるデータを変更する場合は、notifyDataSetChanged() を呼び出してください。これによって、データが変更されたアタッチされたビューが通知され、それ自体を更新する必要があることが通知されます。

クリック イベントを処理する

AdapterView.OnItemClickListener インターフェースを実装して AdapterView の各アイテムでのクリック イベントに応答できます。次に例を示します。

Kotlin

listView.onItemClickListener = AdapterView.OnItemClickListener { parent, view, position, id ->
    // Do something in response to the click
}

Java

// Create a message handling object as an anonymous class.
private OnItemClickListener messageClickedHandler = new OnItemClickListener() {
    public void onItemClick(AdapterView parent, View v, int position, long id) {
        // Do something in response to the click
    }
};

listView.setOnItemClickListener(messageClickedHandler);

参考資料

レイアウトは Sunflower デモアプリで使用されています。