この記事は,フラー株式会社 Advent Calendar 2021 の 9 日目の記事です。
8 日目の記事は @inoriko711 さんによる きらめく星(の力で憧れの私)描くよ でした。
はじめに
僕は フラー株式会社 で Android エンジニアのアルバイトのオンボーディング(研修)期間中に PokéAPI を使ったポケモン図鑑アプリ作りました。これ がそのアプリです。恥ずかしいからみないでください。MVVM とかあまりよくわからないまま適当に作ったと思います。
もう,バイトを始めて 8 ヶ月くらい経ちました1。色々なライブラリや MVVM とは何か,Jetpack Compose... などの色々なことを触れることができたので,改めて簡単なポケモン図鑑 Android アプリを作ります。今回は Jetpack Compose による Android MVVM アーキテクチャ入門 を全面的にパクリって参考にして Compose を使ったポケモン図鑑を作成します2。ちゃんと説明するのがめんどくさいので参考書や これ(今回書いたコード) を見てください。
PokéAPI とは
PokéAPI はポケモンに関する大量のデータ(ポケモン,技,タイプ,特性,ゲームバージョン,図鑑説明,アイテムなど)があります。詳しくは PokéAPI/About を確認してください。
例えば https://pokeapi.co/api/v2/pokemon/ditto/ のように API を叩くと,メタモンに関するデータが取得できます。API の叩き方や取得できるデータについては PokéAPI/Doc を確認してください。とっても詳しく書いてあります。
仕様を決める
ポケモン図鑑(リスト) | ポケモン詳細 |
---|---|
↑のように,番号順でポケモンが並んでいて,ポケモンをタップすることで,そのポケモンの詳細画面を表示しようと思います。詳細画面には,
- 全国図鑑でのポケモンの番号
- ポケモンの名前(英名)
- ポケモンの画像(正面から 1 種類だけ)
を表示しようと思います。
タイプや図鑑説明は pokemon-species を別途叩かないといけないので,めんどくさいのでやりません。
いざ作成
基本的な流れは Android MVVM アーキテクチャ入門 の通りに進めていきます。特に変えた点を説明していきます。わからないところは これ を見て気合いで乗り越えてください。
Android Studio の New Project から Empty Comose Activity を選択してプロジェクトを作成してください。
ライブラリ
build.gradle
/ app/build.gradle
には以下のものを追加します。簡単に説明すると OkHttp と Retrofit で API を叩き,Kotlin Serialization で Json をパースします。Coil はポケモンの画像を表示するために使います。ViewModel は ViewModel 用で(あたりまえ体操)Compose で UI の実装をします。Hilt は DI ライブラリです。
buildscript {
...
dependencies {
...
classpath "org.jetbrains.kotlin:kotlin-serialization:1.5.31"
classpath "com.google.dagger:hilt-android-gradle-plugin:2.37"
}
}
dependencies {
...
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.4.0"
implementation "com.squareup.okhttp3:okhttp:4.9.2"
implementation "com.squareup.retrofit2:retrofit:2.9.0"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.2"
implementation "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.8.0"
implementation "com.google.dagger:hilt-android:2.39.1"
implementation "io.coil-kt:coil-compose:1.4.0"
kapt "com.google.dagger:hilt-compiler:2.39.1"
}
API
PokéAPI を叩きましょう。全国図鑑のリストを返す pokedex/national とポケモンの情報を取得する pokemon/{name} が叩けるようにします。{name}
でわかると思いますが,getPokeApiPokemon
はポケモンの名前または番号を引数とします。それぞれ PokeApiPokedex
と PokeApiPokemon
を戻り値とします。
Porovider
と Module
及び RemoteDataSource...
の説明は書略します。
interface ApiClient {
@GET("pokedex/national")
suspend fun getPokeApiPokedex(): Response<PokeApiPokedex>
@GET("pokemon/{name}")
suspend fun getPokeApiPokemon(@Path("name") name: String): Response<PokeApiPokemon>
}
data class
ポケモン図鑑(リスト)用と,ポケモンの詳細用のデータクラスを作成します。Retrofit の依存関係を含んだ API 側と,依存関係を含まないデータクラスを両方作成します。
ここではめんどくさいので Pokemon
の説明だけをします。
まず API からの情報のうち,id
, name
, sprites
を取得します。sprites
はポケモンの画像色々な画像の URL です。取得したデータを使うときは,一度依存関係のない Pokemon
にデータを変換して使います。
@Serializable
data class PokeApiPokemon(
@SerialName("id") val id: Int,
@SerialName("name") val name: String,
@SerialName("sprites") val sprites: PokeApiSprites,
)
@Serializable
data class PokeApiSprites(
@SerialName("back_default") val back_default: String,
@SerialName("front_default") val front_default: String,
)
data class Pokemon(
val id: Int,
val name: String,
val frontImage: NetworkImage,
)
Repository
そんなに参考書と変えてないので省略です。
UI
ポケモン図鑑の情報を取得するために,MainViewModel
に次のコードを書きます。
fun getPokedex() {
viewModelScope.launch {
uiState.value = UiState.Loading
runCatching {
pokemonRepository.getPokedex()
}.onSuccess {
uiState.value = UiState.SuccessPokedex(pokedex = it)
}.onFailure {
uiState.value = UiState.Failure
}
}
}
ポケモン図鑑(リスト)が表示される PokedexView
と,ポケモンの詳細が表示される PokemonDetailView
を作ります。
PokedexView
は全てのポケモンを並べて表示するので,LazyColumn
を使います。Text
は番号と名前を適当にくっつけたものとして,タップすると onPokemonTapped
を呼び出し ViewModel に伝えます。ViewModel ではポケモンの情報を取得します。
@Composable
fun PokedexView(pokedex: Pokedex, onPokemonTapped: (name: String) -> Unit) {
LazyColumn {
pokedex.pokemonEntries.forEach { pokemonEntry ->
item {
Text(
text = "No. " + pokemonEntry.entryNumber.toString() +
": " + pokemonEntry.pokemonSpecies.name,
modifier = Modifier
.height(30.dp)
.clickable { onPokemonTapped(pokemonEntry.entryNumber.toString()) },
)
}
}
}
}
PokemonDetailView
では id
や name
の他にポケモンの正面の画像を表示します。painter = rememberImagePainter(URL)
で URL にある画像を簡単に表示しています。BackHandler
で戻るボタンが押されたときに backHome()
します。実際には getPokedex()
を呼び出しています。
@Composable
fun PokemonDetailView(pokemon: Pokemon, backHome: () -> Unit) {
Column {
Text(
text = "No. " + pokemon.id.toString()
)
Text(
text = pokemon.name.replaceFirstChar {
if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString()
}
)
Image(
painter = rememberImagePainter(pokemon.frontImage.url.value),
contentDescription = null,
modifier = Modifier.size(128.dp)
)
}
BackHandler(enabled = true) {
backHome()
}
}
最後に
Compose はすごく便利です。ポケモン図鑑表示みたいに複数のものを並べるのも簡単ですし,データへのアクセスも XML を使うときより簡単に感じます。
時間の関係で結構適当なアプリ / 記事になってしまい残念です。アプリ修正して,丁寧な説明を追加して個人ブログに載せたいな〜と思ってます。めんどくさいからやらない気がするけど...
文献
- 作成したソースコード (GitHub)
- PokéAPI
- Jetpack Compose による Android MVVM アーキテクチャ入門
- zsoltk/compose-pokedex (GitHub)
-
週に 2 日くらいです。合計 300 時間くらいですね。 ↩
-
完成度が高い Compose を使ったポケモン図鑑をみたい人は,zsoltk/compose-pokedex (GitHub) が参考になると思います。 ↩