レイアウト   Android Jetpack の一部

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

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

View オブジェクトは、通常「ウィジェット」と呼ばれ、ButtonTextView など、多くのサブクラスのいずれかになります。ViewGroup オブジェクトは、通常「レイアウト」と呼ばれ、LinearLayoutConstraintLayout など、さまざまなレイアウト構造を提供する多くのタイプのいずれかになります。

レイアウトを宣言する方法は 2 通りあります。

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

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

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

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

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

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

XML の記述

Android の XML ボキャブラリを使用して、HTML でウェブページを作成する場合と同じ方法で、一連の要素をネストして UI レイアウトとその画面要素をすばやく設計できます。

各レイアウト ファイルには、ルート要素が 1 つだけ必要です(View または ViewGroup オブジェクトである必要があります)。ルート要素を定義したら、追加のレイアウト オブジェクトまたはウィジェットを子要素として追加し、レイアウトを定義するビュー階層を段階的に作成できます。縦方向の LinearLayout を使用して TextViewButton を保持する 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 でレイアウトを宣言したら、ファイルに .xml 拡張子を付けて Android プロジェクトの res/layout/ ディレクトリに保存して、適切にコンパイルされるようにします。

レイアウト 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 属性など)。他の属性は「レイアウト パラメータ」と見なされます。これは View オブジェクトの特定のレイアウト方向を記述する属性であり、オブジェクトの親である ViewGroup オブジェクトによって定義されます。

ID

ツリー内でビューを一意に識別するために、View オブジェクトに整数の ID を関連付けることができます。アプリがコンパイルされると、この ID が整数として参照されますが、この ID は通常、レイアウト XML ファイルの id 属性で、文字列として割り当てられます。これは、すべての View オブジェクト(View クラスによって定義)に共通の XML 属性であり、頻繁に使用します。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. 次に、ビュー オブジェクトのインスタンスを作成し、レイアウトからキャプチャします(通常は onCreate() メソッド)。

    Kotlin

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

    Java

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

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

ツリー全体で ID が一意である必要はありませんが、検索するツリーの一部では一意である必要があります(ツリー全体となることが多いため、可能であれば完全に一意にすることをおすすめします)

注: Android Studio 3.6 以降では、ビュー バインディング機能を使用して findViewById() 呼び出しを置き換え、ビューと連携するコードをコンパイル時においてタイプセーフであるようなコードにすることができます。findViewById() の代わりにビュー バインディングを使用することを検討してください。

レイアウト パラメータ

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

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

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

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

すべてのビューグループには幅と高さ(layout_widthlayout_height)が含まれており、各ビューでそれらを定義する必要があります。多くの LayoutParams には、オプションのマージンと境界線も含まれています。

必要になる頻度は多くないはずですが、幅と高さは正確な値で指定できます。多くの場合、次の定数のいずれかを使用して幅または高さを設定します。

  • wrap_content は、ビューのサイズをそのコンテンツに必要なサイズにします。
  • match_parent は、ビューのサイズを親ビューグループで許容される最大サイズにします。

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

レイアウトの位置

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

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

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

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

ビューのサイズは、幅と高さで表します。実際のビューには幅と高さの値のペアが 2 つあります。

1 つ目のペアは「測定幅」と「測定高さ」といいます。これらの寸法で、親の中でのビューの大きさが定義されます。測定寸法を取得するには、getMeasuredWidth()getMeasuredHeight() を呼び出します。

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

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

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

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

一般的なレイアウト

ViewGroup クラスの各サブクラスで、中にネストするビューを表示する方法はそれぞれ異なります。Android プラットフォームに組み込まれている一般的なレイアウト タイプの例は次のとおりです。

注: 1 つまたは複数のレイアウトを別のレイアウト内にネストして UI を設計することもできますが、レイアウト階層が可能な限り浅くなるようにしてください。ネストするレイアウトが少なければ、レイアウトの描画が速くなります(深いビュー階層より広いビュー階層の方が望ましい)。

線形レイアウト

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

相対レイアウト

子オブジェクトの位置を、他の子に相対的に指定したり(子 B の左側に子 A など)、親に相対的に指定したり(親の上部に揃えるなど)できます。

ウェブビュー

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

アダプターを使用したレイアウトの作成

レイアウトのコンテンツが動的であるか、事前設定されていない場合、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() メソッドをオーバーライドします。または、TextView 以外のアイテムごとにビューを作成するには(配列アイテムごとに ImageView が必要な場合など)、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 デモアプリで使用されています。