Android 用の XML レイアウトを作成する

1. 始める前に

この Codelab では、簡単なチップ計算アプリのレイアウトを作成します。Codelab の最後にはアプリの UI が機能するようになりますが、チップの計算はまだ行えません。後続の Codelab で実際にアプリを動作させ、デザインをより洗練されたものにしていきます。

前提条件

  • Android Studio でテンプレートから Android アプリを作成して実行できること

学習内容

  • Android での XML レイアウトの読み取りおよび書き込み方法
  • ユーザーのテキスト入力と選択を受け取る簡単なフォーム用のレイアウトを作成する方法

作成するアプリの概要

  • チップ計算用 Android アプリの UI

必要なもの

2. プロジェクトを開始する

Google でチップ計算ツール(https://www.google.com/search?q=tip+calculator)をご覧ください。

18da3c120daa0759.png

ここでは、チップ計算ツールの簡易版を Android アプリとして作成します。

デベロッパーは多くの場合、このようにアプリの簡易版を作成し、一部の機能が動作することを確認したうえで、完全な機能を実装してデザインを洗練されたものにします。

この Codelab を終了する時点では、チップ計算アプリは次のようになります。

bcc5260318477c14.png

Android で用意されている次の UI 要素を使用します。

  • EditText - テキストの入力と編集
  • TextView - サービスに関する質問やチップ金額などのテキストの表示
  • RadioButton - チップ オプション用の選択可能なラジオボタン
  • RadioGroup - ラジオボタン オプションのグループ化
  • Switch - チップを切り上げるかどうかのオン / オフの切り替え

Empty Activity プロジェクトを作成する

  1. まず、Android Studio で Empty Activity テンプレートを使用して新しい Kotlin プロジェクトを作成します。
  2. アプリの名前を「Tip Time」とし、最小 API レベルに 19(KitKat)を選びます。パッケージ名は com.example.tiptime です。

4f7619e9faff20e9.png

  1. [Finish] をクリックして、アプリを作成します。

3.XML を読んで理解する

使い慣れている Layout Editor を使用する代わりに、UI が記述されている XML を変更することによって、アプリのレイアウトを作成します。XML を使用して UI レイアウトを理解し、修正する方法を学ぶことは、Android デベロッパーにとって重要です。

このアプリの UI レイアウトが定義されている XML ファイルを確認して編集します。XML は「拡張マークアップ言語(eXtensible Markup Language)」の略であり、テキストベースのドキュメントを使用してデータを記述する方法のひとつです。XML は拡張可能で柔軟性が高いため、Android アプリの UI レイアウトの定義など、さまざまな用途に使用されます。前の Codelab で説明したとおり、アプリの文字列などの他のリソースも、strings.xml という XML ファイルで定義されています。

Android アプリの UI は、コンポーネント(ウィジェット)の包含階層と、それらのコンポーネントの画面レイアウトとして作成されます。これらのレイアウトは UI コンポーネントそのものであることに注意してください。

画面上の UI 要素のビュー階層を記述します。たとえば、ConstraintLayout(親)には、ButtonsTextViewsImageViews などのビュー(子)を含めることができます。ConstraintLayoutViewGroup のサブクラスであり、これを使用して子ビューの配置やサイズ調整を柔軟に行うことができます。

74c7c563d18fffd4.png

Android アプリの包含階層

32df120272b2331d.png

各 UI 要素は、XML ファイルの XML 要素で表されます。各要素の開始と終了はタグで示され、各タグは < で始まり > で終わります。Layout Editor(Design)を使用して UI 要素の属性を設定するのと同じように、XML 要素にも属性を設定できます。上記の UI 要素の XML は、簡略化すると次のようになります。

<ConstraintLayout>
    <TextView
        text="Hello World!">
    </TextView>
</ConstraintLayout>

8dea708333aebabe.png

具体的な例を見てみましょう。

  1. activity_main.xml を開きます([res] > [layout] > [activity_main.xml])。
  2. このテンプレートから作成した以前のプロジェクトと同様に、アプリの ConstraintLayout 内に「Hello World!」という TextView が表示されます。

4fbdb64c02d62e73.png

  1. Layout Editor の右上で、[Code]、[Split]、[Design] の各ビューを選択できるようになっています。
  2. [Code] ビューを選択します。

6203bec920791bcc.png

activity_main.xml の XML は次のようになります。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    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:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

簡略化された例に比べると分量は多くなっていますが、Kotlin コードの場合と同様に、XML を読みやすくするための処理が行われています。

  1. インデント。Android Studio は、要素の階層を示すためにインデントを自動的に行います。TextViewConstraintLayout に含まれているため、インデントされています。ConstraintLayout が親で、TextView は子です。各要素の属性がインデントされて、その要素の一部であることが示されます。
  2. 色分け(青や緑など)。ファイルの類似部分は同じ色で描画されるため、マッチングしやすくなります。特に、Android Studio では要素のタグの開始と終了が同じ色で描画されます(注: Codelab で使用されている色は、Android Studio に表示される色と一致しない場合があります)。

XML タグ、要素、属性

ここでは TextView 要素を簡略化し、重要な部分を確認できるようにしています。

<TextView
    android:text="Hello World!"
/>

<TextView の行がタグの先頭、/> の行がタグの末尾です。android:text="Hello World!" の行はタグの属性で、TextView によって表示されるテキストを表します。この 3 行は空要素タグと呼ばれる短縮形で、よく使用されます。次のように開始タグと終了タグを別々に記述した場合も、同じ意味になります。

<TextView
    android:text="Hello World!"
></TextView>

また、空要素タグはできるだけ少ない行数で記述し、タグの末尾を前の行と結合するのが一般的です。そのため、空要素タグが 2 行(属性がない場合は 1 行)で記述されていることもあります。

<!-- with attributes, two lines -->
<TextView
    android:text="Hello World!" />

ConstraintLayout 要素は、中に他の要素を入れる必要があるため、開始タグと終了タグを別々に記述します。ConstraintLayout 要素とその中の TextView 要素を簡略化した例を次に示します。

<androidx.constraintlayout.widget.ConstraintLayout>
    <TextView
        android:text="Hello World!" />
</androidx.constraintlayout.widget.ConstraintLayout>

別の ViewConstraintLayout の子として追加する場合(TextView の下に Button を追加する場合など)、次のように、TextView タグの末尾 />ConstraintLayout の終了タグの間に配置します。

<androidx.constraintlayout.widget.ConstraintLayout>
    <TextView
        android:text="Hello World!" />
    <Button
        android:text="Calculate" />
</androidx.constraintlayout.widget.ConstraintLayout>

レイアウト用の XML の詳細

  1. ConstraintLayout のタグを見ると、TextView のように単に「ConstraintLayout」ではなく、「androidx.constraintlayout.widget.ConstraintLayout」と記述されています。これは、ConstraintLayout が Android Jetpack の一部であるためです。Jetpack には、基盤となる Android プラットフォーム上で追加の機能を提供するコードのライブラリが含まれています。Jetpack には、アプリを簡単に作成できる便利な機能が用意されています。この UI コンポーネントは「androidx」で始まっているので、Jetpack の一部だとわかります。
  2. 次のように xmlns: で始まり、その後に androidapptools が続く行があります。
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"

xmlns は XML 名前空間を表し、各行はスキーマ(これらの単語に関連する属性の語彙)を定義しています。たとえば、android: 名前空間は、Android システムによって定義されている属性を示します。レイアウト XML のすべての属性が、それらの名前空間のいずれかで始まります。

  1. XML 要素間に空白文字があることで、コンピュータにとっての意味は変わりませんが、人間にとって XML が読みやすくなります。

Android Studio では、読みやすくなるように自動的に空白文字とインデントが追加されます。Android Studio で XML がコーディング スタイル規則に準拠していることを確認する方法については、後ほど説明します。

  1. Kotlin コードの場合と同様に、XML にもコメントを追加できます。コメントは <!-- で開始し、--> で終了します。
<!-- this is a comment in XML -->

<!-- this is a
multi-line
Comment.
And another
Multi-line comment -->
  1. ファイルの 1 行目に注目してください。
<?xml version="1.0" encoding="utf-8"?>

これは、ファイルが XML ファイルであることを示していますが、すべての XML ファイルにこれが含まれているわけではありません。

4. XML でレイアウトを作成する

  1. activity_main.xml で [Split] 画面ビューに切り替えると、Design Editor の横に XML が表示されます。Design Editor を使用すると、UI レイアウトをプレビューできます。

a03bcf5beacb4b45.png

  1. 好みに応じてどのビューを使用しても構いませんが、この Codelab では、XML を編集しながらその変更結果を Design Editor で確認できるように、[Split] ビューを使用します。
  2. ConstraintLayout の下の行をクリックし、その後 TextView の下の行をクリックして、Design Editor で対応するビューが選択されることを確認します。逆も同様で、たとえば、Design EditorTextView をクリックすると、対応する XML がハイライト表示されます。

1abc54a646c39f66.png

TextView を削除する

  1. ここでは TextView は不要なため、削除します。<TextView から終了タグの /> まで、すべての内容を削除してください。
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Hello World!"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

ファイルに残っているのは ConstraintLayout だけになります。

<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

</androidx.constraintlayout.widget.ConstraintLayout>
  1. UI と画面の端の間に余裕を持たせるために、ConstraintLayout に 16 dp のパディングを追加します。

パディングは余白に似ていますが、ConstraintLayout の外側ではなく内側にスペースを追加します。

<androidx.constraintlayout.widget.ConstraintLayout
    ...
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    tools:context=".MainActivity">

サービス料金のテキスト フィールドを追加する

このステップでは、ユーザーがアプリにサービス料金を入力できる UI 要素を追加します。EditText 要素を使用して、ユーザーがアプリでテキストを入力または変更できるようにします。

7746dedb0d79923f.png

  1. EditText のドキュメントを参照し、サンプル XML を確認します。
  2. ConstraintLayout の開始タグと終了タグの間にある空白を見つけます。
  3. ドキュメントから XML をコピーして、Android Studio のレイアウトの該当領域に貼り付けます。

レイアウト ファイルは次のようになります。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    tools:context=".MainActivity">

    <EditText
        android:id="@+id/plain_text_input"
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        android:inputType="text"/>

</androidx.constraintlayout.widget.ConstraintLayout>

まだ理解できない部分があるかもしれませんが、それについては後のステップで説明します。

  1. EditText に赤い下線が表示されます。
  2. 下線部分にカーソルを合わせると、「ビューに制約が設定されていない」というエラーが表示されます。前の Codelab で説明したように、ConstraintLayout の子には制約を設定する必要があります。これにより、レイアウトが子の配置方法を理解できるようになります。

40c17058bd6786f.png

  1. 次の制約を EditText に追加して、親の左上に固定します。
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"

英語など、左から右に記述する(LTR)言語の場合は、始端が左になります。しかし、アラビア語などの一部の言語は右から左に記述する(RTL)ので、始端は右になります。そのため、制約では「start」を使用し、LTR 言語と RTL 言語の両方で機能するようにしています。同様に、制約では right ではなく「end」を使用します。

新しい制約を追加すると、EditText 要素は次のようになります。

<EditText
    android:id="@+id/plain_text_input"
    android:layout_height="wrap_content"
    android:layout_width="match_parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    android:inputType="text"/>

EditText の属性を確認する

貼り付けた EditText のすべての属性をもう一度確認して、アプリで適切に機能するようにします。

  1. @+id/plain_text_input に設定されている id 属性を探します。
  2. id 属性をより適切な名前(@+id/cost_of_service)に変更します。
  1. layout_height 属性は wrap_content に設定されています。これは、高さが内部のコンテンツと同じになることを意味します。ここでは 1 行のテキストしか表示されないため、この設定で問題ありません。
  2. layout_width 属性は match_parent に設定されていますが、ConstraintLayout の子に match_parent を設定することはできません。また、テキスト フィールドの幅はそれほど必要がないため、160dp の固定幅に設定します。これで、ユーザーがサービス料金を入力するのに十分なスペースを確保できます。

1f82a5e86ae94fd2.png

  1. ここで新たに inputType 属性に注目します。この属性の値は "text" であるため、ユーザーは画面上のフィールドに任意のテキスト(アルファベット、記号など)を入力できます。
android:inputType="text"

しかし、このフィールドは金額を表すため、EditText には数値のみを入力してもらう必要があります。

  1. text」という単語を削除し、引用符はそのままにします。
  2. 代わりに「number」と入力します。「n」と入力すると、「n」を含む候補のリストが表示されます。

99b04cbd21e74693.gif

  1. numberDecimal を選択します。これにより、入力できる値は小数点を含む数値のみに制限されます。
android:inputType="numberDecimal"

入力タイプのその他のオプションについては、デベロッパー向けドキュメントの入力方法のタイプの指定をご覧ください。

さらに変更を加えて、このフィールドに何を入力すべきかを示すヒントが表示されるようにしましょう。

  1. EditText に、このフィールドに入力する内容を説明するための hint 属性を追加します。
android:hint="Cost of Service"

Design Editor も更新されます。

824454d2a316efb1.png

  1. エミュレータでアプリを実行すると、次のようになります。

c9d413de53b0853d.png

これで、アプリはまだ完全には動作しませんが、XML の編集については良いスタートを切ることができました。レイアウトの XML は次のようになります。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    tools:context=".MainActivity">

    <EditText
        android:id="@+id/cost_of_service"
        android:layout_width="160dp"
        android:layout_height="wrap_content"
        android:hint="Cost of Service"
        android:inputType="numberDecimal"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

サービスに関する質問を追加する

このステップでは、「How was the service?」という質問のプロンプトの TextView を追加します。これをコピー&ペーストせずに直接入力すると、Android Studio に候補が表示されます。

  1. EditText タグの末尾の /> の後に新しい行を追加して、<TextView の入力を始めます。
  2. 候補から TextView を選択すると、TextViewlayout_width 属性と layout_height 属性が Android Studio によって自動的に追加されます。
  3. いずれの属性についても wrap_content を選択します。TextView は内部のテキスト コンテンツと同じ大きさであれば十分であるためです。fee18cc43df85294.png
  4. text 属性を追加し、"How was the service?" と指定します。
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="How was the service?"
  1. /> でタグを閉じます。
  2. Design Editor で確認すると、TextViewEditText に重なっています。

ac09d5cae6ae2455.png

次に、これが適切に表示されるよう TextView に制約を追加しましょう。この場合、どのような制約が必要になるか考えてみてください。TextView は、水平方向と垂直方向で、それぞれどこに配置すべきでしょうか?次に示すアプリのスクリーンショットを参考にしてください。

bcc5260318477c14.png

垂直方向については、TextView をサービス料金のテキスト フィールドの下に配置します。水平方向については、TextView を親の始端に揃えます。

  1. TextView に水平方向の制約を追加して、その始端と親の始端の間に制約を設定します。
app:layout_constraintStart_toStartOf="parent"
  1. TextView に垂直方向の制約を追加して、TextView の上端とサービス料金 View の下端の間に制約を設定します。
app:layout_constraintTop_toBottomOf="@id/cost_of_service"

ID はすでに定義されているため、@id/cost_of_service には何も追加しません。

3822136f7ed815f2.png

見た目は洗練されていませんが、現時点では問題ありません。ここでは必要なすべての要素が画面に表示され、機能が正しく動作することだけを確認してください。デザインについては次の Codelab で修正します。

  1. TextView にリソース ID を追加します。後でビューを追加し、ビュー同士で制約を適用する際に、このビューを参照する必要があります。
android:id="@+id/service_question"

この時点で、XML は次のようになります。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    tools:context=".MainActivity">

    <EditText
        android:id="@+id/cost_of_service"
        android:hint="Cost of Service"
        android:layout_height="wrap_content"
        android:layout_width="160dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:inputType="numberDecimal"/>

    <TextView
        android:id="@+id/service_question"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="How was the service?"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/cost_of_service"/>

</androidx.constraintlayout.widget.ConstraintLayout>

5. チップ オプションを追加する

次に、ユーザーが選択できる各チップ オプションに対応するラジオボタンを追加します。

次の 3 つのオプションを設定します。

  • Amazing(20%)
  • Good(18%)
  • Okay(15%)

設定方法がわからない場合は、Google で検索してください。Google 検索は、デベロッパーが行き詰ったときに使用できる優れたツールです。

  1. Google で「radio button android」を検索します。検索結果の最上位に、Android デベロッパー サイトのラジオボタンの使用に関するガイドが表示されます。

f5f1c6778ae7a5d.png

  1. ラジオボタンに関するガイドに目を通します。

説明を読むと、必要なラジオボタンのレイアウトごとに RadioButton UI 要素を使用できることがわかります。また、一度に選択できるオプションは 1 つのみであるため、RadioGroup 内のラジオボタンもグループ化する必要があります。

このニーズに合うと思われる XML があります。説明を最後まで読むと、RadioGroup が親ビューで、RadioButtons がその子ビューであることがわかります。

  1. Android Studio のレイアウトに戻り、RadioGroupRadioButton をアプリに追加します。
  2. ConstraintLayout 内の TextView 要素の後で、<RadioGroup の入力を開始します。XML を完成させるのに便利な候補が Android Studio に表示されます。aee75ba409dc51aa.png
  3. RadioGrouplayout_widthlayout_heightwrap_content に設定します。
  4. リソース ID を追加し、@+id/tip_options に設定します。
  5. > で開始タグを閉じます。
  6. Android Studio が </RadioGroup> を追加します。ConstraintLayout と同様に、RadioGroup 要素内には他の要素も配置されるため、この終了タグを別個の行に移動することをおすすめします。layout_width と layout_height を wrap_content に設定する
  7. RadioGroup と、サービスに関する質問の下端の間(垂直方向)、および親の始端との間(水平方向)に制約を設定します。
  8. android:orientation 属性を vertical に設定します。RadioButtons を横 1 列に並べるには、向きを horizontal に設定します。

RadioGroup の XML は次のようになります。

<RadioGroup
    android:id="@+id/tip_options"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@id/service_question">

</RadioGroup>

RadioButton を追加する

  1. RadioGroup の最後の属性と </RadioGroup> 終了タグの間に、RadioButton を追加します。
<RadioGroup
    android:id="@+id/tip_options"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@id/service_question">

    <!-- add RadioButtons here -->

</RadioGroup>
  1. layout_widthlayout_heightwrap_content に設定します。
  2. @+id/option_twenty_percent のリソース ID を RadioButton に割り当てます。
  3. テキストを Amazing (20%) に設定します。
  4. /> でタグを閉じます。
<RadioGroup
   android:id="@+id/tip_options"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   app:layout_constraintTop_toBottomOf="@id/service_question"
   app:layout_constraintStart_toStartOf="parent"
   android:orientation="vertical">

   <RadioButton
       android:id="@+id/option_twenty_percent"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="Amazing (20%)" />

</RadioGroup>

53cb416b368e9612.png

これで RadioButton を 1 つ追加できたので、この XML を編集し、Good (18%)Okay (15%) 用としてラジオボタンをさらに 2 つ追加してみましょう。

RadioGroupRadioButtons の XML は次のようになります。

<RadioGroup
   android:id="@+id/tip_options"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   app:layout_constraintTop_toBottomOf="@id/service_question"
   app:layout_constraintStart_toStartOf="parent"
   android:orientation="vertical">

   <RadioButton
       android:id="@+id/option_twenty_percent"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="Amazing (20%)" />

   <RadioButton
       android:id="@+id/option_eighteen_percent"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="Good (18%)" />

   <RadioButton
       android:id="@+id/option_fifteen_percent"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="Okay (15%)" />

</RadioGroup>

bab25b6a35d4ce52.png

デフォルトの選択を追加する

チップ オプションは現在選択されていませんが、デフォルトでラジオボタンを 1 つ選択することをおすすめします。

RadioGroup に、最初にチェックするボタンを指定する属性があります。これは checkedButton と呼ばれ、選択するラジオボタンのリソース ID にこの属性を設定します。

  1. RadioGroup で、android:checkedButton 属性を @id/option_twenty_percent に設定します。
<RadioGroup
   android:id="@+id/tip_options"
   android:checkedButton="@id/option_twenty_percent"
   ...

Design Editor でレイアウトが更新され、デフォルトで 20% のチップが選択されています。だんだんチップ計算ツールらしくなってきました。

c412e7f16590cd33.png

ここまでの XML は次のようになります。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    tools:context=".MainActivity">

    <EditText
        android:id="@+id/cost_of_service"
        android:hint="Cost of Service"
        android:layout_height="wrap_content"
        android:layout_width="160dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:inputType="numberDecimal"/>

    <TextView
        android:id="@+id/service_question"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="How was the service?"
        app:layout_constraintTop_toBottomOf="@id/cost_of_service"
        app:layout_constraintStart_toStartOf="parent" />

    <RadioGroup
        android:id="@+id/tip_options"
        android:checkedButton="@id/option_twenty_percent"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@id/service_question"
        app:layout_constraintStart_toStartOf="parent"
        android:orientation="vertical">

        <RadioButton
            android:id="@+id/option_twenty_percent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Amazing (20%)" />

        <RadioButton
            android:id="@+id/option_eighteen_percent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Good (18%)" />

        <RadioButton
            android:id="@+id/option_fifteen_percent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Okay (15%)" />
    </RadioGroup>
</androidx.constraintlayout.widget.ConstraintLayout>

6. レイアウトの残りを完成させる

次はいよいよレイアウトの最後の部分です。SwitchButtonTextView を追加して、チップの金額を表示します。

bcc5260318477c14.png

チップの端数を切り上げる Switch を追加する

次に、Switch ウィジェットを使用して、ユーザーがチップの端数の切り上げについて「はい」か「いいえ」を選択できるようにします。

Switch の幅を親と同じにするには、幅を match_parent に設定すればよいと考えるかもしれません。前述のとおり、ConstraintLayout の UI 要素に match_parent を設定することはできません。代わりに、ビューの始端と終端に制約を設定し、幅を 0dp に設定する必要があります。幅を 0dp に設定すると、システムは幅を計算せずに、ビューに設定されている制約に合致させようとします。

  1. RadioGroup の XML の後に Switch 要素を追加します。
  2. 上記のとおり、layout_width0dp に設定します。
  3. layout_heightwrap_content に設定します。これにより、Switch ビューの高さを内部のコンテンツに合わせます。
  4. id 属性を @+id/round_up_switch に設定します。
  5. text 属性を Round up tip? に設定します。これは Switch のラベルとして使用されます。
  6. Switch の始端と tip_options の始端の間、Switch の終端と親の終端の間に制約を設定します。
  7. Switch の上端と tip_options の下端の間に制約を設定します。
  8. /> でタグを閉じます。

Switch がデフォルトで有効になっていると便利ですが、そのために android:checked という属性が用意されています。可能な値は true(オン)または false(オフ)です。

  1. android:checked 属性を true に設定します。

まとめると、Switch 要素の XML は次のようになります。

<Switch
    android:id="@+id/round_up_switch"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:checked="true"
    android:text="Round up tip?"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="@id/tip_options"
    app:layout_constraintTop_toBottomOf="@id/tip_options" />

d374fab984650296.png

[Calculate] ボタンを追加する

次に、Button を追加して、ユーザーがチップの計算をリクエストできるようにします。ボタンの幅は、親の幅と同じにする必要があります。そのため、水平方向の制約と幅は Switch の場合と同じように設定します。

  1. Switch の後に Button を追加します。
  2. Switch の場合と同様に、幅を 0dp に設定します。
  3. 高さを wrap_content に設定します。
  4. リソース ID を @+id/calculate_button とし、テキストは "Calculate" とします。
  5. Button の上端と [Round up tip?] Switch の下端の間に制約を設定します。
  6. Button の始端と親の始端の間、Button の終端と親の終端の間に制約を設定します。
  7. /> でタグを閉じます。

[Calculate] Button の XML は次のようになります。

<Button
   android:id="@+id/calculate_button"
   android:layout_width="0dp"
   android:layout_height="wrap_content"
   android:text="Calculate"
   app:layout_constraintTop_toBottomOf="@id/round_up_switch"
   app:layout_constraintStart_toStartOf="parent"
   app:layout_constraintEnd_toEndOf="parent" />

5338cf87c61d15c9.png

チップの計算結果を追加する

レイアウトの完成まであと少しです。このステップでは、チップの計算結果の TextView を追加します。これを [Calculate] ボタンの下に配置しますが、他の UI 要素のように開始位置ではなく、終了位置に合わせて配置します。

  1. TextView を追加して、リソース ID に tip_result、テキストに Tip Amount を指定します。
  2. TextView の終端と親の終端の間に制約を設定します。
  3. 上端と [Calculate] ボタンの下端の間に制約を設定します。
<TextView
    android:id="@+id/tip_result"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintTop_toBottomOf="@id/calculate_button"
    android:text="Tip Amount" />

9644bcdabbd8d7d1.png

  1. アプリを実行します。次に示すスクリーンショットのようになります。

e4ed552fa9fbe4ce.png

お疲れさまでした。今回初めて XML を使用したかもしれませんが、問題なくレイアウトを作成することができました。

なお、Android Studio の新しいバージョンではテンプレートが変更されている可能性があるため、実際のアプリはスクリーンショットと一部異なる場合があります。[Calculate] ボタンはまだ機能しませんが、料金を入力し、チップの割合を選択して、チップの端数を切り上げるかどうかのオプションを切り替えることができます。[Calculate] ボタンは次の Codelab で動作するようにしますので、それまでお待ちください。

7. 適切なコーディング慣習を採用する

文字列を抽出する

ハードコードされた文字列に関する警告が表示されていることに気づいていたでしょうか。前の Codelab で、文字列をリソース ファイルに抽出すると、アプリを他の言語に翻訳したり、文字列を再利用したりしやすくなることを説明しました。activity_main.xml を確認して、すべての文字列リソースを抽出します。

  1. 文字列をクリックして、表示される黄色の電球アイコンの上にカーソルを合わせ、その横にある三角形をクリックします。表示されるメニューの中から、[Extract String Resource] を選択します。文字列リソースの名前はデフォルトのままでも構いませんが、チップ オプションに amazing_servicegood_serviceok_service を使用して、よりわかりやすい名前を付けることもできます。

追加した文字列リソースを確認します。

  1. [Project] ウィンドウが表示されない場合は、ウィンドウの左側にある [Project] タブをクリックします。
  2. [app] > [res] > [values] > [strings.xml] を開いて、UI 文字列リソースをすべて表示します。
<resources>
    <string name="app_name">Tip Time</string>
    <string name="cost_of_service">Cost of Service</string>
    <string name="how_was_the_service">How was the service?</string>
    <string name="amazing_service">Amazing (20%)</string>
    <string name="good_service">Good (18%)</string>
    <string name="ok_service">Okay (15%)</string>
    <string name="round_up_tip">Round up tip?</string>
    <string name="calculate">Calculate</string>
    <string name="tip_amount">Tip Amount</string>
</resources>

XML を再フォーマットする

Android Studio には、コードを整理し、推奨されるコーディング規則に準拠できるようにするための各種ツールが用意されています。

  1. activity_main.xml で、[Edit] > [Select All] を選択します。
  2. [Code] > [Reformat Code] を選択します。

こうすることで、インデントに一貫性を持たせることができます。また、ある要素のすべての android: 属性をまとめるといったように、UI 要素の XML の一部を並べ替えてグループ化することもできます。

8. 解答コード

bcc5260318477c14.png

res/layout/activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    tools:context=".MainActivity">

    <EditText
        android:id="@+id/cost_of_service"
        android:layout_width="160dp"
        android:layout_height="wrap_content"
        android:hint="@string/cost_of_service"
        android:inputType="numberDecimal"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/service_question"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/how_was_the_service"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/cost_of_service" />

    <RadioGroup
        android:id="@+id/tip_options"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:checkedButton="@id/option_twenty_percent"
        android:orientation="vertical"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/service_question">

        <RadioButton
            android:id="@+id/option_twenty_percent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/amazing_service" />

        <RadioButton
            android:id="@+id/option_eighteen_percent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/good_service" />

        <RadioButton
            android:id="@+id/option_fifteen_percent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/ok_service" />
    </RadioGroup>

    <Switch
        android:id="@+id/round_up_switch"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:checked="true"
        android:text="@string/round_up_tip"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="@id/tip_options"
        app:layout_constraintTop_toBottomOf="@id/tip_options" />

    <Button
        android:id="@+id/calculate_button"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="@string/calculate"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/round_up_switch" />

    <TextView
        android:id="@+id/tip_result"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/tip_amount"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@id/calculate_button" />
</androidx.constraintlayout.widget.ConstraintLayout>

res/values/strings.xml

<resources>
   <string name="app_name">Tip Time</string>
   <string name="cost_of_service">Cost of Service</string>
   <string name="how_was_the_service">How was the service?</string>
   <string name="amazing_service">Amazing (20%)</string>
   <string name="good_service">Good (18%)</string>
   <string name="ok_service">Okay (15%)</string>
   <string name="round_up_tip">Round up tip?</string>
   <string name="calculate">Calculate</string>
   <string name="tip_amount">Tip Amount</string>
</resources>

9. 概要

  • XML(拡張マークアップ言語)は、テキストを整理する方法のひとつであり、タグ、要素、属性で構成されています。
  • XML を使用して Android アプリのレイアウトを定義します。
  • EditText を使用して、ユーザーがテキストを入力または編集できるようにします。
  • EditText には、そのフィールドに入力すべき内容を示すヒントを表示できます。
  • android:inputType 属性を指定して、ユーザーが EditText フィールドに入力できるテキストの種類を制限します。
  • RadioButtons を使用して RadioGroup でグループ化し、排他的なオプションのリストを作成します。
  • RadioGroup は、縦向きまたは横向きにすることができます。また、最初に選択する RadioButton を指定できます。
  • Switch を使用して、ユーザーが 2 つのオプションを切り替えられるようにします。
  • 別の TextView を使用せずに Switch にラベルを追加できます。
  • ConstraintLayout のそれぞれの子には、垂直方向と水平方向の制約が必要です。
  • 「start」と「end」の制約を使用すると、左から右に記述する(LTR)言語と右から左に記述する(RTL)言語の両方を処理できます。
  • 制約属性の名前は、layout_constraint<Source>_to<Target>Of の形式をとります。
  • View の幅を ConstraintLayout の幅と同じにするには、View の始端と親の始端の間、View の終端と親の終端の間にそれぞれ制約を設定し、幅を 0 dp に設定します。

10. 詳細

以下の各リンクから、ここで説明したトピックに関するドキュメントをご覧いただけます。Android 開発のドキュメントはすべて developer.android.com から取得できます。また、行き詰まったときには、Google 検索を利用してください。

11. 自習用練習問題

次のことを行います。

  • 別の計算アプリを作成します(例: ミリリットルと液量オンス、グラムとカップなどを相互に変換する、料理用の単位変換アプリ)。このアプリの場合、どのようなフィールドが必要になるでしょうか。