1. 始める前に
この Codelab では、簡単なチップ計算アプリのレイアウトを作成します。Codelab の最後にはアプリの UI が機能するようになりますが、チップの計算はまだ行えません。後続の Codelab で実際にアプリを動作させ、デザインをより洗練されたものにしていきます。
前提条件
- Android Studio でテンプレートから Android アプリを作成して実行できること
学習内容
- Android での XML レイアウトの読み取りおよび書き込み方法
- ユーザーのテキスト入力と選択を受け取る簡単なフォーム用のレイアウトを作成する方法
作成するアプリの概要
- チップ計算用 Android アプリの UI
必要なもの
- Android Studio の最新の安定版がインストールされているパソコン
- Android デベロッパー向けドキュメントにアクセスするためのインターネット接続
2. プロジェクトを開始する
Google でチップ計算ツール(https://www.google.com/search?q=tip+calculator)をご覧ください。
ここでは、チップ計算ツールの簡易版を Android アプリとして作成します。
デベロッパーは多くの場合、このようにアプリの簡易版を作成し、一部の機能が動作することを確認したうえで、完全な機能を実装してデザインを洗練されたものにします。
この Codelab を終了する時点では、チップ計算アプリは次のようになります。
Android で用意されている次の UI 要素を使用します。
EditText
- テキストの入力と編集TextView
- サービスに関する質問やチップ金額などのテキストの表示RadioButton
- チップ オプション用の選択可能なラジオボタンRadioGroup
- ラジオボタン オプションのグループ化Switch
- チップを切り上げるかどうかのオン / オフの切り替え
Empty Activity プロジェクトを作成する
- まず、Android Studio で Empty Activity テンプレートを使用して新しい Kotlin プロジェクトを作成します。
- アプリの名前を「Tip Time」とし、最小 API レベルに 19(KitKat)を選びます。パッケージ名は com.example.tiptime です。
- [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
(親)には、Buttons
、TextViews
、ImageViews
などのビュー(子)を含めることができます。ConstraintLayout
は ViewGroup
のサブクラスであり、これを使用して子ビューの配置やサイズ調整を柔軟に行うことができます。
Android アプリの包含階層
各 UI 要素は、XML ファイルの XML 要素で表されます。各要素の開始と終了はタグで示され、各タグは <
で始まり >
で終わります。Layout Editor(Design)を使用して UI 要素の属性を設定するのと同じように、XML 要素にも属性を設定できます。上記の UI 要素の XML は、簡略化すると次のようになります。
<ConstraintLayout>
<TextView
text="Hello World!">
</TextView>
</ConstraintLayout>
具体的な例を見てみましょう。
activity_main.xml
を開きます([res] > [layout] > [activity_main.xml])。- このテンプレートから作成した以前のプロジェクトと同様に、アプリの
ConstraintLayout
内に「Hello World!」というTextView
が表示されます。
- Layout Editor の右上で、[Code]、[Split]、[Design] の各ビューを選択できるようになっています。
- [Code] ビューを選択します。
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 を読みやすくするための処理が行われています。
- インデント。Android Studio は、要素の階層を示すためにインデントを自動的に行います。
TextView
はConstraintLayout
に含まれているため、インデントされています。ConstraintLayout
が親で、TextView
は子です。各要素の属性がインデントされて、その要素の一部であることが示されます。 - 色分け(青や緑など)。ファイルの類似部分は同じ色で描画されるため、マッチングしやすくなります。特に、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>
別の View
を ConstraintLayout
の子として追加する場合(TextView
の下に Button
を追加する場合など)、次のように、TextView
タグの末尾 />
と ConstraintLayout
の終了タグの間に配置します。
<androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:text="Hello World!" />
<Button
android:text="Calculate" />
</androidx.constraintlayout.widget.ConstraintLayout>
レイアウト用の XML の詳細
ConstraintLayout
のタグを見ると、TextView
のように単に「ConstraintLayout
」ではなく、「androidx.constraintlayout.widget.ConstraintLayout
」と記述されています。これは、ConstraintLayout
が Android Jetpack の一部であるためです。Jetpack には、基盤となる Android プラットフォーム上で追加の機能を提供するコードのライブラリが含まれています。Jetpack には、アプリを簡単に作成できる便利な機能が用意されています。この UI コンポーネントは「androidx」で始まっているので、Jetpack の一部だとわかります。- 次のように
xmlns:
で始まり、その後にandroid
、app
、tools
が続く行があります。
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 のすべての属性が、それらの名前空間のいずれかで始まります。
- XML 要素間に空白文字があることで、コンピュータにとっての意味は変わりませんが、人間にとって XML が読みやすくなります。
Android Studio では、読みやすくなるように自動的に空白文字とインデントが追加されます。Android Studio で XML がコーディング スタイル規則に準拠していることを確認する方法については、後ほど説明します。
- Kotlin コードの場合と同様に、XML にもコメントを追加できます。コメントは
<!--
で開始し、-->
で終了します。
<!-- this is a comment in XML -->
<!-- this is a
multi-line
Comment.
And another
Multi-line comment -->
- ファイルの 1 行目に注目してください。
<?xml version="1.0" encoding="utf-8"?>
これは、ファイルが XML ファイルであることを示していますが、すべての XML ファイルにこれが含まれているわけではありません。
4. XML でレイアウトを作成する
activity_main.xml
で [Split] 画面ビューに切り替えると、Design Editor の横に XML が表示されます。Design Editor を使用すると、UI レイアウトをプレビューできます。
- 好みに応じてどのビューを使用しても構いませんが、この Codelab では、XML を編集しながらその変更結果を Design Editor で確認できるように、[Split] ビューを使用します。
ConstraintLayout
の下の行をクリックし、その後TextView
の下の行をクリックして、Design Editor で対応するビューが選択されることを確認します。逆も同様で、たとえば、Design Editor でTextView
をクリックすると、対応する XML がハイライト表示されます。
TextView を削除する
- ここでは
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>
- 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
要素を使用して、ユーザーがアプリでテキストを入力または変更できるようにします。
EditText
のドキュメントを参照し、サンプル XML を確認します。ConstraintLayout
の開始タグと終了タグの間にある空白を見つけます。- ドキュメントから 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>
まだ理解できない部分があるかもしれませんが、それについては後のステップで説明します。
EditText
に赤い下線が表示されます。- 下線部分にカーソルを合わせると、「ビューに制約が設定されていない」というエラーが表示されます。前の Codelab で説明したように、
ConstraintLayout
の子には制約を設定する必要があります。これにより、レイアウトが子の配置方法を理解できるようになります。
- 次の制約を
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
のすべての属性をもう一度確認して、アプリで適切に機能するようにします。
@+id/plain_text_input
に設定されているid
属性を探します。id
属性をより適切な名前(@+id/cost_of_service
)に変更します。
layout_height
属性はwrap_content
に設定されています。これは、高さが内部のコンテンツと同じになることを意味します。ここでは 1 行のテキストしか表示されないため、この設定で問題ありません。layout_width
属性はmatch_parent
に設定されていますが、ConstraintLayout
の子にmatch_parent
を設定することはできません。また、テキスト フィールドの幅はそれほど必要がないため、160dp
の固定幅に設定します。これで、ユーザーがサービス料金を入力するのに十分なスペースを確保できます。
- ここで新たに
inputType
属性に注目します。この属性の値は"text"
であるため、ユーザーは画面上のフィールドに任意のテキスト(アルファベット、記号など)を入力できます。
android:inputType="text"
しかし、このフィールドは金額を表すため、EditText
には数値のみを入力してもらう必要があります。
- 「
text
」という単語を削除し、引用符はそのままにします。 - 代わりに「
number
」と入力します。「n」と入力すると、「n」を含む候補のリストが表示されます。
numberDecimal
を選択します。これにより、入力できる値は小数点を含む数値のみに制限されます。
android:inputType="numberDecimal"
入力タイプのその他のオプションについては、デベロッパー向けドキュメントの入力方法のタイプの指定をご覧ください。
さらに変更を加えて、このフィールドに何を入力すべきかを示すヒントが表示されるようにしましょう。
EditText
に、このフィールドに入力する内容を説明するためのhint
属性を追加します。
android:hint="Cost of Service"
Design Editor も更新されます。
- エミュレータでアプリを実行すると、次のようになります。
これで、アプリはまだ完全には動作しませんが、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 に候補が表示されます。
EditText
タグの末尾の/>
の後に新しい行を追加して、<TextView
の入力を始めます。- 候補から
TextView
を選択すると、TextView
のlayout_width
属性とlayout_height
属性が Android Studio によって自動的に追加されます。 - いずれの属性についても
wrap_content
を選択します。TextView
は内部のテキスト コンテンツと同じ大きさであれば十分であるためです。 text
属性を追加し、"How was the service?"
と指定します。
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="How was the service?"
/>
でタグを閉じます。- Design Editor で確認すると、
TextView
がEditText
に重なっています。
次に、これが適切に表示されるよう TextView
に制約を追加しましょう。この場合、どのような制約が必要になるか考えてみてください。TextView
は、水平方向と垂直方向で、それぞれどこに配置すべきでしょうか?次に示すアプリのスクリーンショットを参考にしてください。
垂直方向については、TextView
をサービス料金のテキスト フィールドの下に配置します。水平方向については、TextView
を親の始端に揃えます。
TextView
に水平方向の制約を追加して、その始端と親の始端の間に制約を設定します。
app:layout_constraintStart_toStartOf="parent"
TextView
に垂直方向の制約を追加して、TextView
の上端とサービス料金View
の下端の間に制約を設定します。
app:layout_constraintTop_toBottomOf="@id/cost_of_service"
ID はすでに定義されているため、@id/cost_of_service
には何も追加しません。
見た目は洗練されていませんが、現時点では問題ありません。ここでは必要なすべての要素が画面に表示され、機能が正しく動作することだけを確認してください。デザインについては次の Codelab で修正します。
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 検索は、デベロッパーが行き詰ったときに使用できる優れたツールです。
- Google で「
radio button android
」を検索します。検索結果の最上位に、Android デベロッパー サイトのラジオボタンの使用に関するガイドが表示されます。
- ラジオボタンに関するガイドに目を通します。
説明を読むと、必要なラジオボタンのレイアウトごとに RadioButton
UI 要素を使用できることがわかります。また、一度に選択できるオプションは 1 つのみであるため、RadioGroup
内のラジオボタンもグループ化する必要があります。
このニーズに合うと思われる XML があります。説明を最後まで読むと、RadioGroup
が親ビューで、RadioButtons
がその子ビューであることがわかります。
- Android Studio のレイアウトに戻り、
RadioGroup
とRadioButton
をアプリに追加します。 ConstraintLayout
内のTextView
要素の後で、<RadioGroup
の入力を開始します。XML を完成させるのに便利な候補が Android Studio に表示されます。RadioGroup
のlayout_width
とlayout_height
をwrap_content
に設定します。- リソース ID を追加し、
@+id/tip_options
に設定します。 >
で開始タグを閉じます。- Android Studio が
</RadioGroup>
を追加します。ConstraintLayout
と同様に、RadioGroup
要素内には他の要素も配置されるため、この終了タグを別個の行に移動することをおすすめします。 RadioGroup
と、サービスに関する質問の下端の間(垂直方向)、および親の始端との間(水平方向)に制約を設定します。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 を追加する
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>
layout_width
とlayout_height
をwrap_content
に設定します。@+id/option_twenty_percent
のリソース ID をRadioButton
に割り当てます。- テキストを
Amazing (20%)
に設定します。 />
でタグを閉じます。
<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>
これで RadioButton
を 1 つ追加できたので、この XML を編集し、Good (18%)
と Okay (15%)
用としてラジオボタンをさらに 2 つ追加してみましょう。
RadioGroup
と RadioButtons
の 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>
デフォルトの選択を追加する
チップ オプションは現在選択されていませんが、デフォルトでラジオボタンを 1 つ選択することをおすすめします。
RadioGroup
に、最初にチェックするボタンを指定する属性があります。これは checkedButton
と呼ばれ、選択するラジオボタンのリソース ID にこの属性を設定します。
RadioGroup
で、android:checkedButton
属性を@id/option_twenty_percent
に設定します。
<RadioGroup
android:id="@+id/tip_options"
android:checkedButton="@id/option_twenty_percent"
...
Design Editor でレイアウトが更新され、デフォルトで 20% のチップが選択されています。だんだんチップ計算ツールらしくなってきました。
ここまでの 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. レイアウトの残りを完成させる
次はいよいよレイアウトの最後の部分です。Switch
、Button
、TextView
を追加して、チップの金額を表示します。
チップの端数を切り上げる Switch を追加する
次に、Switch
ウィジェットを使用して、ユーザーがチップの端数の切り上げについて「はい」か「いいえ」を選択できるようにします。
Switch
の幅を親と同じにするには、幅を match_parent
に設定すればよいと考えるかもしれません。前述のとおり、ConstraintLayout
の UI 要素に match_parent
を設定することはできません。代わりに、ビューの始端と終端に制約を設定し、幅を 0dp
に設定する必要があります。幅を 0dp
に設定すると、システムは幅を計算せずに、ビューに設定されている制約に合致させようとします。
RadioGroup
の XML の後にSwitch
要素を追加します。- 上記のとおり、
layout_width
を0dp
に設定します。 layout_height
をwrap_content
に設定します。これにより、Switch
ビューの高さを内部のコンテンツに合わせます。id
属性を@+id/round_up_switch
に設定します。text
属性をRound up tip?
に設定します。これはSwitch
のラベルとして使用されます。Switch
の始端とtip_options
の始端の間、Switch の終端と親の終端の間に制約を設定します。Switch
の上端とtip_options
の下端の間に制約を設定します。/>
でタグを閉じます。
Switch がデフォルトで有効になっていると便利ですが、そのために android:checked
という属性が用意されています。可能な値は true
(オン)または false
(オフ)です。
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" />
[Calculate] ボタンを追加する
次に、Button
を追加して、ユーザーがチップの計算をリクエストできるようにします。ボタンの幅は、親の幅と同じにする必要があります。そのため、水平方向の制約と幅は Switch
の場合と同じように設定します。
Switch
の後にButton
を追加します。Switch
の場合と同様に、幅を0dp
に設定します。- 高さを
wrap_content
に設定します。 - リソース ID を
@+id/calculate_button
とし、テキストは"Calculate"
とします。 Button
の上端と [Round up tip?]Switch
の下端の間に制約を設定します。- Button の始端と親の始端の間、Button の終端と親の終端の間に制約を設定します。
/>
でタグを閉じます。
[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" />
チップの計算結果を追加する
レイアウトの完成まであと少しです。このステップでは、チップの計算結果の TextView
を追加します。これを [Calculate] ボタンの下に配置しますが、他の UI 要素のように開始位置ではなく、終了位置に合わせて配置します。
TextView
を追加して、リソース ID にtip_result
、テキストにTip Amount
を指定します。TextView
の終端と親の終端の間に制約を設定します。- 上端と [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" />
- アプリを実行します。次に示すスクリーンショットのようになります。
お疲れさまでした。今回初めて XML を使用したかもしれませんが、問題なくレイアウトを作成することができました。
なお、Android Studio の新しいバージョンではテンプレートが変更されている可能性があるため、実際のアプリはスクリーンショットと一部異なる場合があります。[Calculate] ボタンはまだ機能しませんが、料金を入力し、チップの割合を選択して、チップの端数を切り上げるかどうかのオプションを切り替えることができます。[Calculate] ボタンは次の Codelab で動作するようにしますので、それまでお待ちください。
7. 適切なコーディング慣習を採用する
文字列を抽出する
ハードコードされた文字列に関する警告が表示されていることに気づいていたでしょうか。前の Codelab で、文字列をリソース ファイルに抽出すると、アプリを他の言語に翻訳したり、文字列を再利用したりしやすくなることを説明しました。activity_main.xml
を確認して、すべての文字列リソースを抽出します。
- 文字列をクリックして、表示される黄色の電球アイコンの上にカーソルを合わせ、その横にある三角形をクリックします。表示されるメニューの中から、[Extract String Resource] を選択します。文字列リソースの名前はデフォルトのままでも構いませんが、チップ オプションに
amazing_service
、good_service
、ok_service
を使用して、よりわかりやすい名前を付けることもできます。
追加した文字列リソースを確認します。
- [Project] ウィンドウが表示されない場合は、ウィンドウの左側にある [Project] タブをクリックします。
- [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 には、コードを整理し、推奨されるコーディング規則に準拠できるようにするための各種ツールが用意されています。
activity_main.xml
で、[Edit] > [Select All] を選択します。- [Code] > [Reformat Code] を選択します。
こうすることで、インデントに一貫性を持たせることができます。また、ある要素のすべての android:
属性をまとめるといったように、UI 要素の XML の一部を並べ替えてグループ化することもできます。
8. 解答コード
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. 自習用練習問題
次のことを行います。
- 別の計算アプリを作成します(例: ミリリットルと液量オンス、グラムとカップなどを相互に変換する、料理用の単位変換アプリ)。このアプリの場合、どのようなフィールドが必要になるでしょうか。