知識ゼロからの Kotlin Android アプリリリースへの軌跡 / Day3【Codelabs 1-2編】
前回のコードラボに続き、今回のコードラボでは、Android アプリの主要なコンポーネントの詳細を学び、ボタンを追加してアプリに簡単なインタラクティブ機能を追加していきます。
学ぶこと
今回作成するアプリ
今回は DiceRoller
という新しいアプリプロジェクトを作成し、ボタンを使用して基本的なインタラクティブ機能を追加する。ボタンをクリックするたびに表示されるテキストの値が変化する。
Activity・Layout ファイルを調べる
ステップ1: MainActivity を調べる
MainActivity
は Activity
の例です。Activity
は Android アプリのユーザインターフェースを描画、入力イベントを受け取るコア Android クラスです。アプリが起動すると、AndroidManifest.xml
で指定された Activity が起動します。
AndroidManifest.xml
ファイルは、ユーザがアプリのランチャーアイコンをタップした時に MainActivity
が起動する必要があることを示しています。アクティビティを起動するために Android OS はマニフェストの情報を使用してアプリの環境を設定し、MainActivity
を構築します。
各アクティビティには、関連付けられたレイアウトファイルが存在し、アクティビティとレイアウトはレイアウトインフレーションと呼ばれるプロセスによって接続されます。
アクティビティが開始されると、XML レイアウトファイルで定義されている View がメモリ内の Kotlin オブジェクトに変換されます(インフレート)。これが発生すると、アクティビティはこれらのオブジェクトを画面に描画し、動的に変更することが可能になります。
Abdroid Studio で [File] > [New] > [New Project] を選択して、新しいプロジェクトを作成します。Empty Activity を使用して、[次へ] をクリックします。
プロジェクトを
DiceRoller
と名付けて、Use AndroidX artifacts
がチェックされていることを確認してFinish
をクリックします。
- import 文の下に
MainActivity
class の宣言があります。MainActivity
クラスはAppCompatActivity
の拡張クラスです。
class MainActivity : AppCompatActivity() { ...}
onCreate()
に注目してください。Activity は、コンストラクターを使用してオブジェクトを初期化しません。代わりに、一連の事前定義されたメソッド(ライフサイクルメソッド)が Activity 設定の一部として呼び出されます。これらのライフサイクルメソッドの1つはonCreate()
です。これは、自分のアプリで常に Override します。
onCreate()
で Activity に関連付けるレイアウトを指定し、レイアウトをインフレーとします。setContentView()
がこれら2つの役割を担っています。
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) }
この setContentView()
メソッドは R.layout.activity_main
を実際には Int 参照を使用して参照します。R
クラスはアプリをビルドする際に生成され、これらにはアプリの全てのリソースを含む res
ディレクトリの内容が含まれています。
R
クラス内の同様の参照を使用して、アプリのリソースの多く(画像、文字列など)を参照します。
ステップ2: アプリのレイアウトファイルを調べる
アプリの全てのアクティビティには、アプリの res/layout
ディレクトに関連するレイアウトファイルが存在します。レイアウトファイルは、Activity がどのように見えるかを表現するための XML ファイルです。レイアウトファイルは、View を定義し、ビューが画面のどこに表示されるかを定義します。
View は、Text、Image、Button など View を拡張するクラスです。TextView
、Button
、ImageView
、CheckBox
など View には様々な種類が存在します。
=> Swift の View とほとんど認識は同じ感じですね
[app] > [res] > [layout] > [activity_main] をクリックし、レイアウト設計エディターを開きます。このエディターを使用すると、アプリのレイアウトを視覚的に構築し、レイアウト設計をプレビューできます。
レイアウトファイルを XML として表示するには、[Code] or [Desing] で切り替えます。
=> 筆者の場合は、Android Studio のバージョンが違うせいか上部のタブにありました。
レイアウトエディターで既存の XML コードを全て削除します。新しいプロジェクトで得られるデフォルトのレイアウトは、Android Studio デザインエディターを使用している場合に適しています。ここでは、基礎となる XML を操作して、新しいレイアウトを最初から作成します。
次のコードをコピーしてレイアウトに貼り付けます。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" tools:context=".MainActivity" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" /> </LinearLayout>
コード解説
LinearLayout
View はViewGroup
です。ViewGroup は、他の View を保持し、画面上の View の位置を指定するのに役立つコンテナです。
=> SwiftUI の VStacK
とか HStack
みたいな感じのイメージっぽいですね
新しい Android プロジェクトで取得するデフォルトのルートは ConstraintLayout
です。これは、デザインエディターと連携して機能します。LinearLayout
は、ConstraintLayout
よりもシンプルな ViewGroup を使用します。
LinearLayout
タグ内で、android:layout_width
属性に注目してください。LinearLayout
の幅はmatch_parent
に設定されているため、親と同じ幅になります。これはルートビューであるため、レイアウトは画面の幅いっぱいに拡大されます。android:layout_height
に設定されている属性に注目してください。wrap_content
は、LinearLayout
に含まれている全ての View の高さを合わせた高さが設定されます。現時点では TextView だけなので、その高さになります。
=> Swift の AutoLayout のイメージと同じ感じですね(StackView とか)
<TextView>
要素を見てみます。このTextView
は、DiceRoller アプリで唯一の視覚的な要素です。android:text
属性は、TextView に表示する実際の文字列を保持しています。ここではHellow World!
です。要素の
android:layout_width
およびandroid:layout_height
属性に注目してください。どちらもwrap_content
に設定されています。TextView のコンテンツはテキスト自体なので、View はテキストに必要なスペースのみ使用します。
ボタンを追加する
レイアウトにボタンを追加してサイコロを転がし、ユーザーが転がしたサイコロの値を示すテキストを追加します。
ステップ1: レイアウトボタンを追加する
- 下記のようにして、TextView のしたに
Button
要素を追加します。
<Button android:layout_width="" android:layout_height="" />
layout_width
とlayout_height
属性の両方にwrap_content
を設定します。これにより、ボタンの幅と高さはボタンに含まれるテキストラベルと同じになります。android:text
属性にRoll
という文字列を追加します。
<Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Roll" />
ステップ2: 文字列リソースを抽出する
レイアウトまたはコードファイルに文字列をハードコーディングする代わりに、全てのアプリ文字列を別のファイルに入れることをお勧めします。このファイルは strings.xml
と呼ばれ、[app] > [res] > [values] ディレクトリにあります。
別のファイルに文字列を含めると、これらの文字列を複数回使用する場合などに文字列を管理しやすくなります。また、アプリの翻訳とローカライズには文字列リソースが必須です。
タグの
android:text
属性の「Roll」文字列を一回クリックします。Option + Enter
(macOs)を押して、ポップアップメニューからExtract string resource
を選択します。OK をクリックします。文字列リソースが
res/values/strings.xml
ファイルに作成され、Button 要素の文字列がそのリソースへの参照に置き換えられます。android:text="@string/roll"
[app] > [res] > [values] の
strings.xml
を覗くと下記のようにリソースが追加されています。
<resources> <string name="app_name">DiceRoller</string> <string name="roll_label">Roll</string> </resources>
ステップ3: View のスタイルと配置
これで、レイアウトに1つの TextView
と Button
が含まれました。このステップでは、より良く見えるように ViewGroup 内の View を配置します。
- [Design] タブをクリックして、レイアウトのプレビューを表示します。現在は、両方の View が隣あっていて、画面の上部に配置されています。
- [Code] タブをクリックして、XML エディターに戻ります。
LinearLayout
タグにandroid:orientation
属性を追加して、値をvertical
に設定します。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" tools:context=".MainActivity">
デザインは下記のようになります。
- TextView と Button それぞれに
android:layout_gravity
を追加して値をcenter_horizontal
に設定します。これによって両方の View が水平軸の中心に配置されるようになります。
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="Hello World!" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="@string/roll_label" />
LinearLayout
タグにandroid:layout_gravity
属性を追加して、値をcenter_vertical
にセットします。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:layout_gravity="center_vertical" tools:context=".MainActivity">
- TextView のテキストサイズを大きくするには、
android:textSize
属性を追加し、値に30sp
を追加してみます。
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:textSize="30sp" android:text="Hello World!" />
- アプリをコンパイルして実行してみます。
ステップ4: コードでボタンへの参照を取得する
MainActivity
の Kotlin コードは、ボタンをタップした時の動作など、アプリのインタラクティブな部分を定義する役割を果たします。ボタンがクリックされた時に実行される関数を作成するには、MainActivity
のインフレーとされたレイアウトで Button Object への参照を取得する必要があります。ボタンへの参照を取得するには:
Button
に XML ファイルで ID を割り当てる。- View への参照を取得するために、特定の ID を元に
findViewById()
メソッドで参照を取得する。
Button
View への参照を取得したら、その View のメソッドを呼び出して、アプリの実行中に View を動的に変更することができます。例えば、ボタンがタップされた時にコードを実行する Click Handler を追加できます。
activity_main.xml
の [Code] タブを開きます。android:id
属性を Button に追加し、名前を付けます。(この場合は@+id/roll_button
)
<Button android:id="@+id/roll_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="@string/roll_label" />
XML レイアウトファイルで View の ID を作成すると、Android Studio は、生成された R
クラスにその ID の名前で整数定数を作成します。したがって、R
クラスで呼び出される整数定数を使用して参照を生成することができるようになります。また、@+id
プレフィックスは、その ID 定数を R
クラスに追加するようにコンパイラーに指示します。XML ファイルの全ての View ID には、このプレフィックスが必要です。
MainActivity
の Kotlin ファイルを開きます。onCreate()
内のsetContentView()
の後に次の行を追加します。
val rollButton: Button = findViewById(R.id.roll_button)
findViewById()
メソッドを使用して、XML クラスで定義した View の参照を取得します。この場合、R
クラスと ID から Button への参照を取得して、変数に割り当てます。
ステップ5: トーストを表示するクリックハンドラーを追加する
Click Handler は、ボタンなど、クリック可能な UI 要素にユーザがクリックまたはタップをするたびに呼び出されるメソッドです。必要なクリックハンドラーを作成するには:
- クリック時に何らかの操作を実行するメソッドの作成
setOnClickListener()
を Button に接続
ここでは、Toast
を表示するために Click Handler を作成します。(Toast は、短時間画面に表示されるポップアップメッセージです。)
Button に Click Handler メソッドを接続します。
MainActivity
でonCreate()
メソッドの後に、rollDice()
と名付けた private method を追加します。
private fun rollDice() { }
rollDice()
メソッドが呼ばれた時にToast
を表示できるようにするために、rollDice()
メソッドに次のコードを追加します。
Toast.makeText(this, "button clicked", Toast.LENGTH_SHORT).show()
Toast を作成するには、Toast.makeText()
メソッドを呼び出します。これには次の3つが必要です:
Context
オブジェとを使用すると、Android OS の現在の状態と通信して情報を取得できます。オブジェクトが OS にトーストを表示するように指示するには、このContext
が必要です。AppCompactActivity
はContext
のサブクラスなので、キーワードにthis
を使用できます。 => Swift では自身のクラスを表すのは self だけど、Kotlin の場合は this って感じでしょうか表示されるメッセージ
メッセージを表示する時間
rollButton
の Click Handler としてrollDice()
を割り当てるために、findViewById()
の下に下記のコードを追加します。
rollButton.setOnClickListener { rollDice() }
MainActivity
の完全な定義は下記のようになります。
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val rollButton: Button = findViewById(R.id.roll_button) rollButton.setOnClickListener { rollDice() } } private fun rollDice() { Toast.makeText(this, "button clicked", Toast.LENGTH_SHORT).show() } }
- アプリをコンパイルして実行します。ボタンをタップするたびに、トーストが表示されるようになります。
テキストを変更する
ここでは、TextView のテキストを変更するために、rollDice()
メソッドを修正します。最初のステップとして、そのテキストを Hello World!
から Dice Rolled!
に変更します。次のステップでは、1~6 の乱数を表示します。
ステップ1: 文字列を表示する
activity_main.xml
を開き、TextView に ID を追加します。MainActivity
ファイル開き、rollDice()
の Toast を表示する処理をコメントアウトします。TextView の参照を取得するために
findViewById()
と ID を使用して、resultText
に割り当てます。
val resultText: TextView = findViewById(R.id.result_text)
- 表示されるテキストを変更するには、
resultText.text
プロパティに新しい文字列をあてます。
resultText.text = "Dice Rolled!"
- アプリをコンパイルして実行します。ボタンをタップすると、TextView が更新されることを確認します。
ステップ2: 乱数を表示する
ここでは、ボタンクリックにランダム性を追加し、サイコロの転がりをシュミレートします。ボタンがクリックまたはタップされるたびに、コードは 1~6 の乱数を選択し、TextView を更新します。乱数を生成するタスクは range の Random
関数を使用して行います。
rollDice()
メソッドの上部で、(1..6)random()
メソッドを使用して 1~6 までの乱数を取得します。
val randomInt = (1..6).random()
- ランダムの整数を文字列として、text プロパティに追加します。
resultText.text = randomInt.toString()
- アプリをコンパイルして実行します。ボタンがタップされるたびに TextView がかわります。
まとめ
Activities
MainActivity
はAppCompatActivity
のサブクラスであり、AppCompatActivity
はActivity
のサブクラスである。また、Activity
は Android App の UI を描画したり、入力イベントを受信したりする、Android の コアクラスです。全ての Activity には、関連付けられたレイアウトファイルがあります。これは、アプリのリソース内の XML ファイルです。レイアウトファイルには、Activity の名前が名付けられます。(例:
activity_main.xml
)MainActivity
のsetContentView()
メソッドは、レイアウトを Activity に関連付け、Activity が作成される時にそのレイアウトをインフレーとします。レイアウトインフレーションは、XML レイアウトファイルで定義された View がメモリ内の Kotlin View オブジェクトに変換(インフレート)されるプロセスです。レイアウトインフレーションが発生すると、
Activity
はこれらのオブジェクトを画面に描画し、動的に変更できるようになります。
Views
アプリレイアウトの全ての UI 要素は、
View
クラスのサブクラスです。TextView
およびButton
は View の例です。View 要素は、
ViewGroup
の中でグループ化することができます。ViewGroup は、View またはその中の ViewGroup のコンテナとして機能します。LinearLayout
は、View を直線的に配置する ViewGroup の例です。
View attributes
android:layout_width
とandroid:layout_height
属性は、View の高さと幅を表しています。これらの属性にmatch_parent
の値を設定すると親と同じ高さまたは幅になります。wrap_content
を設定すると View 内のコンテンツに大きさに合わせてサイズが縮小します。android:text
属性は、View に表示するテキストを表しています(View にテキストが表示されている場合)。LinearLayout
内のViewGroup のandroid:orientation
属性は、そのグループに含まれる View を配置します。hotizontal
に設定すると View は左から右に配置され、vertical
に設定すると View は上から下に配置されます。android:layout_gravity
属性は、View とその子 View の配置を決定します。android:textSize
属性は、TextView のテキストのサイズを定義します。テキストは sp単位(スケーラブルピクセル)で指定されます。sp ユニットを使用すると、デバイスの表示品質とは関係なくテキストのサイズを変更できます。
### Strings
レイアウトで文字列をハードコーディングする代わりに、文字列のリソースを使用することがベストプラクティスです。
文字列リソースは
app/res/values/res/string.xml
ファイルに含まれています。文字列を抽出するには、
Option + Enter
(Mac)を使用し、ポップアップメニューからExtract string resources
を選択します。
Using views
Kotlin コードをレイアウトで定義した View に接続するには、View がインフレートされた後で、View Object への参照を取得する必要があります。レイアウトファイルで View に
android:id
で ID を割り当て、findViewById()
メソッドを使用して、関連づけられた View Object を取得します。XML レイアウトファイルで View の ID を作成すると、Android Studio は、生成された
R
クラスにその ID の名前で整数定数を作成します。その後、そのR.id
参照をfindViewById()
メソッドで使用できます。プロパティを使用することで、Kotlin コードで View Object の属性を直接設定できます。例えば、TextView の text は XML の
android:text
の属性によって定義され、Kotlin の text プロパティでも定義されます。Click Handler は、ユーザがクリックまたはタップした時に呼び出されるメソッドです。Click Handler メソッドをボタンなどの View にアタッチするには、
setOnClickListener()
メソッドを使用します。
Using toasts
Toast は小さな Window に簡単なメッセージが表示される View です。
Toast を作成するには。Toast クラスで makeText()
ファクトリメソッドを下記の3つの引数を付けて呼び出します。
- app の Activity Context
- 表示するためのメッセージ
- 表示する時間の長さ
toast を表示させるには、show()
メソッドを呼び出します。