iOSエンジニアのつぶやき

毎朝8:30に iOS 関連の技術について1つぶやいています。まれに釣りについてつぶやく可能性があります。

知識ゼロからの Kotlin Android アプリリリースへの軌跡 / Day3【Codelabs 1-2編】

前回のコードラボに続き、今回のコードラボでは、Android アプリの主要なコンポーネントの詳細を学び、ボタンを追加してアプリに簡単なインタラクティブ機能を追加していきます。

yamato8010.hatenablog.com

学ぶこと

  • アプリのレイアウトファイルを編集する方法
  • インタラクティブな動作を備えたアプリを作成する方法
  • 多くの新しい用語
    • 用語と分かりやすい説明についてはこちらを参照してください

今回作成するアプリ

今回は DiceRoller という新しいアプリプロジェクトを作成し、ボタンを使用して基本的なインタラクティブ機能を追加する。ボタンをクリックするたびに表示されるテキストの値が変化する。

f:id:yum_fishing:20200822214652p:plain

Activity・Layout ファイルを調べる

ステップ1: MainActivity を調べる

MainActivityActivity の例です。ActivityAndroid アプリのユーザインターフェースを描画、入力イベントを受け取るコア Android クラスです。アプリが起動すると、AndroidManifest.xml で指定された Activity が起動します。

AndroidManifest.xml ファイルは、ユーザがアプリのランチャーアイコンをタップした時に MainActivity が起動する必要があることを示しています。アクティビティを起動するために Android OS はマニフェストの情報を使用してアプリの環境を設定し、MainActivity を構築します。 各アクティビティには、関連付けられたレイアウトファイルが存在し、アクティビティとレイアウトはレイアウトインフレーションと呼ばれるプロセスによって接続されます。 アクティビティが開始されると、XML レイアウトファイルで定義されている View がメモリ内の Kotlin オブジェクトに変換されます(インフレート)。これが発生すると、アクティビティはこれらのオブジェクトを画面に描画し、動的に変更することが可能になります。

  1. Abdroid Studio で [File] > [New] > [New Project] を選択して、新しいプロジェクトを作成します。Empty Activity を使用して、[次へ] をクリックします。

  2. プロジェクトを DiceRoller と名付けて、Use AndroidX artifacts がチェックされていることを確認して Finish をクリックします。

  1. [app] > [java] > [com.example.android.diceroller] で MainActivity をクリックしてコードを表示します。

  1. import 文の下に MainActivity class の宣言があります。MainActivity クラスは AppCompatActivity の拡張クラスです。
class MainActivity : AppCompatActivity() { ...}
  1. 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 を拡張するクラスです。TextViewButtonImageViewCheckBox など View には様々な種類が存在します。

=> Swift の View とほとんど認識は同じ感じですね

  1. [app] > [res] > [layout] > [activity_main] をクリックし、レイアウト設計エディターを開きます。このエディターを使用すると、アプリのレイアウトを視覚的に構築し、レイアウト設計をプレビューできます。

  2. レイアウトファイルを XML として表示するには、[Code] or [Desing] で切り替えます。

=> 筆者の場合は、Android Studio のバージョンが違うせいか上部のタブにありました。

  1. レイアウトエディターで既存の XML コードを全て削除します。新しいプロジェクトで得られるデフォルトのレイアウトは、Android Studio デザインエディターを使用している場合に適しています。ここでは、基礎となる XML を操作して、新しいレイアウトを最初から作成します。

  2. 次のコードをコピーしてレイアウトに貼り付けます。

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

コード解説

  1. LinearLayout View は ViewGroup です。ViewGroup は、他の View を保持し、画面上の View の位置を指定するのに役立つコンテナです。

=> SwiftUI の VStacK とか HStack みたいな感じのイメージっぽいですね

新しい Android プロジェクトで取得するデフォルトのルートは ConstraintLayout です。これは、デザインエディターと連携して機能します。LinearLayout は、ConstraintLayout よりもシンプルな ViewGroup を使用します。

  1. LinearLayout タグ内で、android:layout_width 属性に注目してください。LinearLayout の幅は match_parentに設定されているため、親と同じ幅になります。これはルートビューであるため、レイアウトは画面の幅いっぱいに拡大されます。

  2. android:layout_height に設定されている属性に注目してください。wrap_content は、LinearLayout に含まれている全ての View の高さを合わせた高さが設定されます。現時点では TextView だけなので、その高さになります。

=> Swift の AutoLayout のイメージと同じ感じですね(StackView とか)

  1. <TextView> 要素を見てみます。この TextView は、DiceRoller アプリで唯一の視覚的な要素です。android:text 属性は、TextView に表示する実際の文字列を保持しています。ここでは Hellow World! です。

  2. 要素の android:layout_width および android:layout_height 属性に注目してください。どちらも wrap_content に設定されています。TextView のコンテンツはテキスト自体なので、View はテキストに必要なスペースのみ使用します。

ボタンを追加する

レイアウトにボタンを追加してサイコロを転がし、ユーザーが転がしたサイコロの値を示すテキストを追加します。

ステップ1: レイアウトボタンを追加する

  1. 下記のようにして、TextView のしたに Button 要素を追加します。
<Button
   android:layout_width=""
   android:layout_height="" />
  1. layout_widthlayout_height 属性の両方に wrap_content を設定します。これにより、ボタンの幅と高さはボタンに含まれるテキストラベルと同じになります。

  2. android:text 属性に Roll という文字列を追加します。

<Button
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:text="Roll" />

ステップ2: 文字列リソースを抽出する

レイアウトまたはコードファイルに文字列をハードコーディングする代わりに、全てのアプリ文字列を別のファイルに入れることをお勧めします。このファイルは strings.xml と呼ばれ、[app] > [res] > [values] ディレクトリにあります。

別のファイルに文字列を含めると、これらの文字列を複数回使用する場合などに文字列を管理しやすくなります。また、アプリの翻訳とローカライズには文字列リソースが必須です。

  1. タグの android:text 属性の「Roll」文字列を一回クリックします。

  2. Option + Enter(macOs)を押して、ポップアップメニューから Extract string resource を選択します。

  3. OK をクリックします。文字列リソースが res/values/strings.xml ファイルに作成され、Button 要素の文字列がそのリソースへの参照に置き換えられます。android:text="@string/roll"

  4. [app] > [res] > [values] の strings.xml を覗くと下記のようにリソースが追加されています。

<resources>
    <string name="app_name">DiceRoller</string>
    <string name="roll_label">Roll</string>
</resources>

ステップ3: View のスタイルと配置

これで、レイアウトに1つの TextViewButton が含まれました。このステップでは、より良く見えるように ViewGroup 内の View を配置します。

  1. [Design] タブをクリックして、レイアウトのプレビューを表示します。現在は、両方の View が隣あっていて、画面の上部に配置されています。

  1. [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">

デザインは下記のようになります。

  1. 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" />
  1. 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">
  1. 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!" />
  1. アプリをコンパイルして実行してみます。

ステップ4: コードでボタンへの参照を取得する

MainActivity の Kotlin コードは、ボタンをタップした時の動作など、アプリのインタラクティブな部分を定義する役割を果たします。ボタンがクリックされた時に実行される関数を作成するには、MainActivity のインフレーとされたレイアウトで Button Object への参照を取得する必要があります。ボタンへの参照を取得するには:

  • ButtonXML ファイルで ID を割り当てる。
  • View への参照を取得するために、特定の ID を元に findViewById() メソッドで参照を取得する。

Button View への参照を取得したら、その View のメソッドを呼び出して、アプリの実行中に View を動的に変更することができます。例えば、ボタンがタップされた時にコードを実行する Click Handler を追加できます。

  1. activity_main.xml の [Code] タブを開きます。

  2. 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 には、このプレフィックスが必要です。

  1. 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 メソッドを接続します。

  1. MainActivityonCreate()メソッドの後に、rollDice() と名付けた private method を追加します。
private fun rollDice() {
  
}
  1. rollDice() メソッドが呼ばれた時に Toast を表示できるようにするために、rollDice() メソッドに次のコードを追加します。
Toast.makeText(this, "button clicked", 
   Toast.LENGTH_SHORT).show()

Toast を作成するには、Toast.makeText() メソッドを呼び出します。これには次の3つが必要です:

  • Context オブジェとを使用すると、Android OS の現在の状態と通信して情報を取得できます。オブジェクトが OS にトーストを表示するように指示するには、この Context が必要です。AppCompactActivityContext のサブクラスなので、キーワードに 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()
    }
}
  1. アプリをコンパイルして実行します。ボタンをタップするたびに、トーストが表示されるようになります。

テキストを変更する

ここでは、TextView のテキストを変更するために、rollDice() メソッドを修正します。最初のステップとして、そのテキストを Hello World! から Dice Rolled! に変更します。次のステップでは、1~6 の乱数を表示します。

ステップ1: 文字列を表示する

  1. activity_main.xml を開き、TextView に ID を追加します。

  2. MainActivity ファイル開き、rollDice() の Toast を表示する処理をコメントアウトします。

  3. TextView の参照を取得するために findViewById() と ID を使用して、resultText に割り当てます。

val resultText: TextView = findViewById(R.id.result_text)
  1. 表示されるテキストを変更するには、resultText.text プロパティに新しい文字列をあてます。
resultText.text = "Dice Rolled!"
  1. アプリをコンパイルして実行します。ボタンをタップすると、TextView が更新されることを確認します。

ステップ2: 乱数を表示する

ここでは、ボタンクリックにランダム性を追加し、サイコロの転がりをシュミレートします。ボタンがクリックまたはタップされるたびに、コードは 1~6 の乱数を選択し、TextView を更新します。乱数を生成するタスクは range の Random 関数を使用して行います。

  1. rollDice() メソッドの上部で、(1..6)random() メソッドを使用して 1~6 までの乱数を取得します。
val randomInt = (1..6).random()
  1. ランダムの整数を文字列として、text プロパティに追加します。
resultText.text = randomInt.toString()
  1. アプリをコンパイルして実行します。ボタンがタップされるたびに TextView がかわります。

まとめ

Activities

  • MainActivityAppCompatActivity のサブクラスであり、AppCompatActivityActivity のサブクラスである。また、ActivityAndroid App の UI を描画したり、入力イベントを受信したりする、Android の コアクラスです。

  • 全ての Activity には、関連付けられたレイアウトファイルがあります。これは、アプリのリソース内の XML ファイルです。レイアウトファイルには、Activity の名前が名付けられます。(例: activity_main.xml)

  • MainActivitysetContentView() メソッドは、レイアウトを 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_widthandroid: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 は XMLandroid:text の属性によって定義され、Kotlin の text プロパティでも定義されます。

  • Click Handler は、ユーザがクリックまたはタップした時に呼び出されるメソッドです。Click Handler メソッドをボタンなどの View にアタッチするには、setOnClickListener() メソッドを使用します。

Using toasts

Toast は小さな Window に簡単なメッセージが表示される View です。

Toast を作成するには。Toast クラスで makeText() ファクトリメソッドを下記の3つの引数を付けて呼び出します。

  • app の Activity Context
  • 表示するためのメッセージ
  • 表示する時間の長さ

toast を表示させるには、show() メソッドを呼び出します。

その他の記事

yamato8010.hatenablog.com

yamato8010.hatenablog.com

yamato8010.hatenablog.com