Androidアプリ開発をやってみる (Kotlin)
業務でAndroidアプリの開発をすることになったので、学びながら実装していく過程を書いていく。
UIについて
UIの構築方法は2種類
View APIを使う方法と、Compose APIを使う方法。
View API
View APIを使う際はXMLに要素を書く。
XMLに書いた要素にアクセスする際は、idを付与し、idを検索してアクセスする。
こっちの書き方で説明する入門書が多い印象。
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout ...>
<ImageView
android:id="@+id/img"
...
/>
</FrameLayout ...>
// 先にレイアウトファイルを指定する
override fun onCreateView(
inflate: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
) : View {
return inflater.inflate(R.layout.<XML FILE NAME>, container, false)
}
// 指定したレイアウトファイル内の要素を取得
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// ...
val viewImage = view.findViewById<ImageView>(R.id.img)
// ...
}
Binding
上記のように、findViewById
を使う方法では、XML上にその要素が存在することを保証できない (実行時エラーになる恐れがある)。
その対応として、Data Bindingという手法がある (こちらを使う場合は存在しない場合コンパイルエラーになる)。
ただ、こちらを利用すると、Compose APIへの移行が面倒になったので、個人的には微妙に感じてる。
Compose API
Googleが推奨する宣言的にUIを構築する方法。
書き方的には、FlutterとかReact Nativeに近い印象で、要素の入れ子を書いていく。
@Composable
private fun Screen() {
ScaffoldTopAppbar(title = "Home") {
val modifier = Modifier.padding(it)
Box(
modifier = modifier.fillMaxSize().background(Color.White),
contentAlignment = Alignment.Center
) {
// ...
}
}
}
View APIの良いところ
デザインエディターが充実しているため、GUIでUIを構築できる。
UIだけでなく、Navigationも画面間でどのように繋げるかをGUIで記述できる。
そのため、画面ごとにどのような遷移があるかが非常にわかりやすい。
View APIの微妙なところ
layoutファイルはres/layout配下に置く必要があり、ファイルが増えると訳が分からなくなる。
ただし、モジュール化すれば対応できる。
UIの微修正が少々大変。ファイルを行ったり来たりしたり、場合によっては新規ファイル追加が必要になる。
Compose APIの良いところ
UIとロジックが比較的近くにあるため、処理が分かりやすい。
一画面内で要素を切り替えたりするのがやりやすい (フラッシュメッセージとかの実装が割と簡単)。
Compose APIの微妙なところ
Android StudioのDesign Editorが使えないため、GUIで画面を構築したり、Navigationを視覚的に確認したりができない。
UIのプレビューがイマイチ。
引数ありcomposableのプレビューが簡単にできないため、ビルド時には使わないプレビュー用composableの用意などが必要になる。
@Preview
@Composable
fun PreviewCustomButton() {
CustomButton(text = "preview", ....)
}
// ここに@Previewをつけたいが引数のデフォルト値を設定できないため、プレビューできない。
@Composable
fun CustomButton(text: String, ...) {
// ...
}
デバッグ
基本はログに出すか、Layout Inspectorで値を見るか。
Timber
Androidの標準ライブラリでログを出力する際は以下のようにする。
これを使う場合、タグ名はここに設定する必要があり、本番ビルド時にもログが出てしまう。
Log.d("<タグ名>", "ログ内容")
Timberを使うと、クラス名がタグ名になるため、記述を省略できる。
また、本番ビルド時にはログを出さないとか、別サービスにログレポートを送ったりできるらしい。
Timber.d("ログ内容")