カテゴリー
Kotlin言語

ゼロから始めるAndroidアプリ(Kotlin編)Unit2 – 1 第4回:XMLでUI画面を書く

Android Developers > スタートガイド > Kotlin と Android をゼロから学ぶ > Unit2 – レイアウトUnit2-1 第4回 の内容をまとめたものです(翻訳ではありません)


Unit2 – 1 第4回 のポイント

  • AndroidアプリのUI画面を、Android Studio の Layout Editor をつかう代わりにXMLエディタを使って書いてみます
  • XMLはいろいろな用途に使われています。Android Studio でもUI画面の記述だけでなく、strings.xml のようにUIで使う文字列を保存しておくのにも使われています
  • UI画面に、編集可能なテキストフィールド、ラジオボタン、スイッチなどを作成してみます
  • Androidにある次のUI部品(View)を使ってみることにします
    • EditText – 編集可能なテキスト入力フィールド
    • TextView – 結果をテキスト表示する
    • RadioButton – ひとつひとつのラジオボタン
    • RadioGroup – ラジオボタンをまとめる部品
    • Switch – on/off できるスイッチ
  • 新規にプロジェクトを作成しておきます
    • アプリの名前は Tip Time
    • テンプレートは Empty Activity
    • 言語は Kotlin
    • Minimum SDK は API 19 : Android 4.4 (KitKat)

UI部品(View)と階層

レイアウトには階層があるんですね

  • AndroidアプリのUI部品(View,コンポーネント,ウィジェット)には階層があって、それが画面に表示されている。レイアウト自体もUI部品(View)のひとつと考えます
  • XMLをつかって画面に表示するUI部品(View)の階層(containment hierarchy)を記述します。
    • たとえば、ConstraintLayout(親のView)は ButtonsTextViewsImageViews などの View を子として持ちます
    • ConstraintLayout は ViewGroup(View の一種)のクラスです。子になる View 部品を取りまとめます
  • UI部品(View)の階層(containment hierarchy)は親が子を完全に取り込んでいく構造です。前回出てきたクラスの階層(class hierarchy)は子が親の機能を引き継いで飲み込んでいく感じなので同じ親子階層でも考え方が違います
  • UI部品(View)はXMLファイル中でタグで囲まれたXML要素であらわされます。書き方はHTMLと似ています。Layout Editor でUI部品(View)を配置すると、こんな感じのXMLファイルが作られます(簡略に書きました。実際はもっといろいろ書き込まれます)
<ConstraintLayout>
    <TextView
        text="Hello World!">
    </TextView>
</ConstraintLayout>

XMLでのViewの表し方

レイアウトはこんな風にXMLで表されます

  • <タグ タグの属性・・・> 子になるUI部品・・・ </タグ>
  • 通常は構造が見やすいようにインデントされてこんな風になっている
<タグ
    タグの属性1
    タグの属性2
    ・・・ >

    子になるUI部品1
    子になるUI部品2
    ・・・
</タグ>

子になるUI部品がない場合・・・空要素タグ(empty-element tag

コンパクトにも書けるんですね

  • <TextView 属性いろいろ ></TextView>
<TextView
    android:text="Hello World!"
></TextView>
  • <TextView 属性いろいろ /> ・・・こんな風に書いてもよい
<TextView
    android:text="Hello World!"
/>

activity_main.xml を表示してみる

Android Studio が最初に生成した XML ファイルを見てみますね

  • Layout Editor で activity_main.xml を開いて、画面右上のほうにある「Code」をクリックするとXMLでの表示をみることができます
  • 「Split」をクリックすると Design Editorと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>
  • 1行目はこれがXMLファイルであることを示している宣言。ただしすべてのXMLファイルに書いてあるとは限りません
  • インデントされていて階層が分かりやすくなっています
  • Android Studio 上では見やすいように色分けもされています
  • ConstraintLayout は TextView と違って、実際には androidx.constraintlayout.widget.ConstraintLayout と記述されています。これは ConstraintLayout が Android Jetpack という追加機能ライブラリで提供されているためです。Jetpack で提供されているものは androidx で始まります
  • タグの属性(attribute)
    • 属性はネームスペースと呼ばれるグループに分けられています
    • xmlns: は XML namespace の意味。xmlns: に続けて属性のネームスペースを定義しています
    • たとえばandroid: は Android システムで定義された属性であることを示しています
    • レイアウト用のXMLに記述する属性は、かならずこのネームスペースから指定します

むずかしそうだけど、表示が整っていて見やすいですね!

  • Android Studio にはXMLを見やすいように自動で整形してくれる機能があります。XMLの規約に従うようにフォーマットしなおす方法もあります
  • XMLファイルにコメントをしたいときは <!----> で囲みます
  • Android Studio ではXMLに問題があると赤で表示されます。赤く表示されているところにマウスカーソルを合わせて何が問題なのか確認しておきます。原因がはっきりしないときはインデントや色の指定のあたりに問題がないかチェックしてみます

XMLで画面レイアウトを作ってみる

直接 XML ファイルを書き換えてみますね

  • Design View で、画面右上の「Split」を選んで XML と Desing Editor を両方表示しておきます
    • XML あるいは Design Editor に表示されている部品をクリックすると、それぞれの該当する箇所がどこなのかわかるようになっています
  • XMLファイルを書き換えてみる
    • TextView を削除してみる・・・XMLで <TextView ~ /> までを削除する。Desing Editor 上からも消えることを確認します
    • ConstraintLayout の枠から 16dp だけ、かならず部品を離すようにする・・・XMLのConstraintLayout の属性の中に android:padding="16dp" を追加する
<androidx.constraintlayout.widget.ConstraintLayout  
    ...
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    tools:context=".MainActivity">

EditText を追加してみる

UI部品の追加って、XMLに直接書いてもできるんですね

XMLファイルにUI部品を追加する手順

  1. Android Developers の APIレファレンスで追加したいUI部品(View)のドキュメントを探します
  2. ドキュメント中のサンプルXMLを、アプリのXMLファイルにコピペします
  3. 追加した部品の constraint(制約)をXMLで書き加えます

うん、EditText だったら ドキュメントのこの辺 を見てこんな感じにコピペするの

<?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">

    <EditText
        android:id="@+id/plain_text_input"
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        android:inputType="text"/>
  
</androidx.constraintlayout.widget.ConstraintLayout>

UI部品(View)に constraint(制約)を設定する

EditText が赤く表示されちゃってます

うん、constraint(制約)が設定されないとそうなるの。以下の行を追加しますね

app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
  • Androidを日本語や英語で使う場合、レイアウトファイルの Start は Left の意味、End は Right の意味です
  • layout_constraint<Source>_to<Target>Of は、<Source>(constraintを設定する部品の場所)から <Target>(指定した部品の場所) に constraint(制約)を設定するという意味です
  • まとめると、こんな感じになります
<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"/>

リソースID を書き換える

EditText の リソースID を変えてみます

  • リソースIDは @+id/plain_text_input となっているので、これを @+id/cost_of_service と書き換えます
    • リソースIDは @+id ではじめます
    • リソースID名は snake_case で記述します
  • リソースIDをKotlinコードから参照したいときは R.<type>.<name> と記述します
  • たとえば View(UI部品)のIDの <type> は id なので R.id.button で参照できる

大きさを調整する

表示する大きさも変えてみます

  • wrap_content は、部品に表示する内容の大きさに合うように大きさが変化する
  • match_parent は ConstraintLayout の子Viewには使えません。とりあえず 160dp に変更しておきます

入力についての属性を設定する

入力についての細かい設定もできるの

  • inputType は text と設定されていますが、数値(小数)だけ受け付けるように変更してみます
    • text のところを消す(ダブルコーテーションは残しておく)
    • 消したあとに n と入れると、リストが出てくるのでその中から numberDecimal を選択する(小数だけが入力できるようになる)
    • Android Studio にはその部分で可能な選択肢を提案してきたり、最低限必要なものを自動で書き込んでくれたりする機能があります。これらをうまく利用します(タイピングや勘違いのミスも減る)
    • text や numberDecimal 以外の入力方法についてはドキュメント(ここ)を読んで確認しましょう
  • 入力欄にユーザーへのメッセージ(入力例など)を表示するには次のようにします
android:hint="Cost of Service"

メッセージ表示用の TextView を追加する

TextView を追加してみますね

<?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">

    <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>

ラジオボタンを作ってみる

ラジオボタンも作ってみますね

  • Android でのラジオボタンの作り方が知りたいときは “radio button android” でググる(Googleで検索する)ようにします。他の機能も同じようにググって探し当てるのがおすすめです
  • ラジオボタンは、まず RadioGroup を作ってその中に RadioButton を入れていきます。RadioGroup が親View で RadioButtonが子Viewになります

こんな感じですね

<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="OK (15%)" />

</RadioGroup>

デフォルトで選択されるボタンを指定します

最初に選択されているボタンは、RadioGroup の属性に android:checkedButton で指定するの

<RadioGroup
   android:id="@+id/tip_options"
   android:checkedButton="@id/option_twenty_percent"
   ...

全体はこんなかんじかな?

<?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">

    <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="OK (15%)" />
    </RadioGroup>
</androidx.constraintlayout.widget.ConstraintLayout>

Switch, Button, TextView の追加

Switch(UI部品)を追加します

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="parent"
    app:layout_constraintTop_toBottomOf="@id/tip_options" />
  • Switch をレイアウトの幅いっぱいにしたいとき、ConstraintLayout では幅の constraint(制約)を 0dp とします
  • ConstraintLayout に含める UI部品(View)では match_parent は使えません。代わりに 0dp を使うので注意しましょう

Button(UI部品)を追加します

うん、これは Button

<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を追加します

TextView はすこし見慣れてきたかも

<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" />

strings.xml の利用

UI画面(レイアウト)で使う文字列には、できるだけ strings.xml を使ってくださいね

  • 以前のチュートリアルでも出てきたんですけれども、XML中に文字列をハードコード(コードの中に直接書き込む)すると警告の黄が表示されます。activity_main.xml を開いてこれらをクリック、strings.xml を利用する形に修正します
  • 修正した strings.xml を確認しておきます。Project から開くことができます( app > res > values > strings.xml
  • 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>

XMLファイルの整形

Android Studio を使うと、自動で見やすくしてくれるんですね

  • Android Studio には XMLファイルの自動整形機能があります。
  • XMLの書き方として推奨されているような、見やすくて一貫性のある書き方に直すことができます
  • 次のようにすると整形できます
    1. XMLファイル(たとえばactivity_main.xml)を開いて Ctrl+a ですべてを選択します
    2. Code > Reformat Code を選択します

ということで、今回作成した activity_main.xml はこんな感じですね

うーん、やっぱりXMLを手で書くのはたいへん。Layout Editor ってすごいかも…

<?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="parent"
        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>

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>

Unit2 – 1 第4回のまとめ

Layout Editor を使うときに XML をイメージしてみるといいかも

  • XML (Extensible Markup Language) ・・・タグ(tag), 要素(element), 属性(attribute)を使って構造的に記述されたテキストです
  • Androidアプリのレイアウトは XML で記述されています
  • EditText(UI部品のView)は、アプリのユーザがテキストを編集・入力するのに使います
    • EditText には hint の機能があります
    • android:inputType 属性を使うと、ユーザが入力できるテキストの種類を制限できます
  • ラジオボタンを作るには RadioButtons と RadioGroup を使います
    • RadioGroup では、ボタンを並べる方向(縦あるいは横)、デフォルトで選択されているボタンを決めておくことができます
  • 2つの状態(on/off)を選択できるようにするには Switch(UI部品のView)を使います
    • Switch には属性でラベルが付けられます(TextViewを別途追加する必要がありません)
  • ConstraintLayout に置かれたUI部品(View)には、必ずそれぞれに縦方向と横方向の constraint(制約)を設定します
  • constraint(制約)の Start と End は、日本語や英語では Left と Right という意味です(アラビア語のような右から左に書く言語では逆になります)
  • constraint(制約)は、次のような書式で設定します・・・layout_constraint<Source>_to<Target>Of
  • UI部品(View)の横幅を ConstraintLayout の枠いっぱいに表示したいときは、左右の constraint(制約)を match_parent ではなく、0dp に設定します(ConstraintLayout で match_parent は使えません)

長かったですね、お疲れさま!