📱

Androidアプリ開発入門(Kotlinお勉強編)

2025/02/26に公開

はじめに

勉強がてら、まとめてみました。
こちらはお勉強編として

  • Kotlinの文法
  • Jetpackについて
  • アーキテクチャー
  • Unit Test

などについて書いていきます。このあと実装編を書くつもりで、そこで簡単なアプリ開発のメモを書いていく予定です。

言語はなにか?

Kotlinを使います。理由は以下です。

  • これまではJavaだったが、今ではAndroidアプリの推奨言語になっている
  • Kotlinでしか使えない独自APIがある
  • 簡潔に記述できる
  • nullを代入できる/できない変数を宣言できる(null安全)
  • Javaと互換性あり

超基礎

そもそも実行に関して

開発時はAndroid Studioを用いて開発することが多いですが、ここで書かれたコードを元に、実行可能ファイルであるAPK(Android Application Package)を作成し、これをデバイスにインストールすることでアプリが実行されます。

さらにGoogle Playに載せたい場合は、APKではなく、ABB(Android App Bundle)をGoogle Playにアップロードします。これによりデバイスごとに最適化されたAPKを生成してくれるので開発側としては嬉しい限りです。

変数宣言

  • val: 再代入できない変数
  • var: 再代入できる変数

条件分岐とループ処理

これは覚えるしかないでしょう。

条件分岐

if (条件式1) {
    条件式1がTrueの場合の処理
} else if (条件式2) {
    条件式2がTrueの場合の処理
} else {
    それ以外の処理
}

またKotlinではwhenを使って記述もできます。
処理が1行なら、以下の処理2のようにも書けます。

when (変数) {
    条件1がTrue -> {
        処理1
    }
    条件2がTrue -> 処理2
    ...
}

ループ処理

for (ループ内の変数 in 変数) {
    処理
}

ラムダ式を使ってもう少し簡素に書けます。

Listの変数.forEach { ループ内の変数 ->
    処理
}

関数の記述方法

型は以下

fun クラス名(引数1:, 引数2:, ...): 戻り値の型 {
    ここに処理を書く
}

fun addNumbers(a: Int, b: Int): Int {
    return a + b
}

ちなみに関数内の処理が1行のみなら簡潔に以下のように書けます。

fun addNumbers(a: Int, b: Int): Int = a + b

クラスの書き方

継承や、データクラスなど細かい話は実装しながら記載しますということでまずは基礎的な型から。

class クラス名(コンストラクタ) {
    メンバ変数の定義

    fun メソッド名(引数) {
        処理
    }
}

サイズに関する話

DP(Density-independent Pixels)

pxではなく、dpを使うことが多い。
デバイスの画面密度に依存しないサイズ。幅や高さ、余白の設定などに使う。

PXを使った場合

DPを使った場合

SP(Scalable Pixels)

デバイスのフォントサイズ設定に応じて決まるサイズ。
テキストのサイズを指定する時に使う。

アンドロイドのシステム設定でフォントサイズを変えることができるが、その時に設定が変わるようにするためにはSPで定義しておく必要がある

Jetpack

  • 公式に提供されているライブラリ群
  • ボイラープレート(お決まりコード)を減らし負担を軽減できる
  • アプリの後方互換性を保つ
    • AndroidのVersionごとにAPI仕様が異なっていた場合は分岐が必要になるが、Jetpackが吸収してくれる

Jetpack Compose

  • これまではViewを使っていた
  • KotlinでUIを書きながら開発できる
  • Composable関数を実行してUIを構築する

Composable関数

  • UIを構築する関数

特徴

  • 関数の前に@Composableというアノテーションをつけることでコンパイラに対してComposable関数であることを伝える
  • 関数名が大文字スタートの名詞であるのが通例
  • 関数の中はComposeライブラリに含まれる標準Composable関数を呼び出す

定義方法の例:文字を赤くする関数(Modifierはこの後説明します)

@Composable
fun RedText(
    text: String,
    modifier: Modifier = Modifier
) {
    Text(
        text = text,
        modifier = modifier,
        color = Color.red
    )
}

Modifier

Modifierという引数はUIの装飾や振る舞いを汎用的にカスタマイズするためのObject。

例: サイズを指定するModifier

  • 周囲の余白
Modifier.padding(all = 8.dp)
Modifier.padding(horizontal = 8.dp, vertical = 4.dp)
Modifier.padding(start = 8.dp, top = 4.dp, end = 8.dp, bottom = 16.dp)
  • 幅、高さ
// 96 x 48 dp
Modifier.width(96.dp).height(48.dp)
// 48 x 48 dp
Modifier.size(48.dp)
// 幅を最大、高さを最小
Modifier.fillMaxWidth().wrapContentHeight()
// 幅と高さを最大
Modifier.fillMaxSize()

ネスト

  • いくつかのComposableには別のComposableを入力できる(これをスロットAPIと呼ぶ)

例1: Button

  • ButtonというComposable関数の中にTextというComposable関数が入ってる
  • 関数名のあとに{}がある書き方は補足で解説
Button {
    Text(text = "Hello")
}

例2: Column, Row

  • Composableを並べるためのComposable
  • Columnを使うと縦に、Rowを使うと縦に並べられる

Column(
    horizontalAlignment = Alignment.CenterHorizontally,
    verticalArrangement = Arrangement.spaceBy(8.dp)
) {
    Text(text = "Top")
    Text(text = "Center")
    Text(text = "Bottom")
}

配置、Paddingについて

  • horizontalAlignment = Alignment.CenterHorizontally
    • 横方向の配置方法を指定。中央に配置
  • verticalArrangement = Arrangement.spaceBy(8.dp)
    • 縦方向の配置方法を指定。それぞれ8dp離す
    • 全て等間隔ではなく、個別に設定したい場合は、Textの間にSpacer(modifier=Modifier.height(8.dp))などと書く

重みをつけて並べる

  • 1つの要素にだけ重みをつけると、残りのスペースを全て埋めてくれる
Column(modifier = Modifier.height(160.dp)) {
    Text(text = "Top", modifier = Modifer.weight(1f))
    Text(text = "Center")
    Text(text = "Bottom")
}
  • 1:2:3の割合で埋める
Column(modifier = Modifier.height(160.dp)) {
    Text(text = "Top", modifier = Modifer.weight(1f))
    Text(text = "Center", modifier = Modifer.weight(2f))
    Text(text = "Bottom", modifier = Modifer.weight(3f))
}

Lazy Column

多くのアイテムが存在する場合、全て表示すると処理速度やメモリを余計に食ってしまうため、画面に表示されているアイテムのみComposeされる仕組みがLazy Column

LazyColumn(
    horizontalAlignment = Alignment.CenterHorizontally,
    verticalArrangement = Arrangement.spaceBy(8.dp)
) {
    // 1つ追加
    item {
        Text(text = "Header")
    }
    // 複数追加
    items(listOf("1", "2", "3")) { text ->
        Text(text = text)
    }
}

Recompose

Composable関数の引数を変えて再実行すること。

細かい話は実装編でやります。

リソースについて

  • Androidアプリ内のアイコンや画像、文字列などをxml形式で定義
  • src/main/resに格納される
  • メリット
    • 一元管理できるので、運用が楽になる
    • アプリの多言語対応ができる
      • 英語用、日本語用のファイルを用意しておけばすぐに表示言語を変えられる

String resource

  • アプリ内で表示する文字列
  • res/values/strings.xmlに定義
<resources>
    <string name="settings">設定</string>
    ...
</resources>

UIから参照する時はktファイル内で...

Text(text = stringResource(id = R.string.settings))

Drawable resource

  • アイコンや写真などの画像
  • 画像だと2種類の形式が存在

ラスター画像

  • ピクセルの集まり
  • 複雑な色構成を表示するのに適している
  • 拡大すると荒くなる
  • 拡張子:JPG, PNG, WebPなど
  • 保存場所(Adobeのようなルーツで一気に生成可能)
    • res/drawable-mdpi: 基準で倍率1倍
    • res/drawable-hdpi: 倍率1.5倍
    • res/drawable-xhdpi: 倍率2倍
    • res/drawable-xxhdpi: 倍率3倍
    • res/drawable-xxxhdpi: 倍率4倍

Image(
    painter = painterResource(id = R.drawable.リソースid),
    contentDescription = stringResource(R.string.リソースid)
)

contentDescriptionは画像を触ったら画像を読み上げる機能(目の不自由な方向け)に使う

ベクター画像

  • 点や線の集合
  • 平面的なイラストやアイコン画像に適している
  • 拡大しても荒くならない
  • 拡張子:SVGなど
  • 保存場所
    • res/drawable
  • Android StudioのVectorDrawableで生成できる
    • res/drawableで右クリック > New > Vector Asset > SVGをインポート or Clip Artを選択

例:Icon composableを使うと、色を変更できる

Icon(
    painter = painterResource(id = R.drawable.リソースid),
    contentDescription = stringResource(R.string.リソースid),
    tint = Color.Red
)

画像の切り抜き

Modifierを使ってClipできる。

// 円状にclip
Image(
    ...
    modifier = Modifier.clip(CircleShape)
)

// 角を指定した半径で丸める
Image(
    ...
    modifier = Modifier.clip(ResourceCornerShape(size = 8.dp))
)

Raw resoruce

動画、音声などの生データを入れる場所

アーキテクチャーについて

どこに何を書けばいいのか?が公式の推奨アーキテクチャにまとめられている。
拡張性、再利用性、テストのしやすさより、責務によってプログラムを分離する必要がある。

Google公式の推奨アーキテクチャ

基本構成

大きくはUIレイヤとデータレイヤが存在。

UIレイヤ

UIレイヤはUIと状態ホルダーの2つのコンポーネントが存在する。

  • UIの役割 (Jetpack Composeの機能)
    • UIを表示する
    • データが変わったらUIを更新する
  • 状態ホルダーの役割(ViewModel)
    • データレイヤから取得したデータをUIの状態に変換して保持

データレイヤ

データレイヤにはリポジトリと、データソースという2つのコンポーネントが存在。

  • 主な役割

    • データの作成、取得、保存、削除
    • UIレイヤにデータを公開
    • ロジック処理
  • リポジトリの役割

    • 複数のデータソースから得られたデータを抽象化する(データソースによる差を意識しなくていい)

データソースを実装するには以下の外部ライブラリを使うことが多い

  • ローカルデータソース
    • Jetpack Room
      • SQLiteをアプリに組み込むためのライブラリ
    • Jetpack DataStore
      • 簡単なkey-valueを保存できる
  • リモートデータソース
    • Retrofit
      • 昔から使用されているHTTPクライアント
    • Ktor
      • Kotlinで書かれたHTTPクライアント

unit testについて

  • ViewModelやリポジトリなどをテスト
  • ローカルマシン上で動作する(エミュレーターや物理デバイスは不要)

Testの書き方

こちらも実装編でやります

補足: 関数名の後に{}がある書き方について

  • Kotlinの文法
  • Buttonはcontentという関数型が必須の引数になっている
@Composable
fun Button(
    ...
    content: @Composable () -> Unit
)
  • Kotlinでは関数型(引数)と書く(引数なしのため上の例では()のみ)

  • Unitは戻り値なしを表す

  • 関数型のオブジェクトにはラムダ式を使う

Button(content = { Text(text = "Hello") })
  • 末尾のラムダ式はカッコの外に出せる
Button() { Text(text = "Hello")}
  • 引数のないカッコは省略可能
Button {Text(text = "Hello")}

Reference

Discussion