PokéAPI を使ってポケモン図鑑の Android を作ってみた


この記事は，[フラー株式会社 Advent Calendar 2021](https://qiita.com/advent-calendar/2021/fuller-inc/) の 9 日目の記事です。

8 日目の記事は [@inoriko711](https://qiita.com/inoriko711/) さんによる [きらめく星（の力で憧れの私）描くよ](https://qiita.com/inoriko711/items/289766bd1088faa406d8/) でした。

## はじめに

僕は [フラー株式会社](https://www.fuller-inc.com/) で Android エンジニアのアルバイトのオンボーディング（研修）期間中に PokéAPI を使ったポケモン図鑑アプリ作りました。[これ](https://github.com/Daiji256/MyPokedex/) がそのアプリです。恥ずかしいからみないでください。MVVM とかあまりよくわからないまま適当に作ったと思います。

もう，バイトを始めて 8 ヶ月くらい経ちました[^schedule]。色々なライブラリや MVVM とは何か，Jetpack Compose... などの色々なことを触れることができたので，改めて簡単なポケモン図鑑 Android アプリを作ります。今回は [Jetpack Compose による Android MVVM アーキテクチャ入門](https://www.amazon.co.jp/gp/product/B09DNV323P/) を全面的に~~パクリって~~参考にして Compose を使ったポケモン図鑑を作成します[^compose-pokedex]。ちゃんと説明するのがめんどくさいので参考書や [これ（今回書いたコード）](https://github.com/Daiji256/Pokedex/) を見てください。

[^schedule]: 週に 2 日くらいです。合計 300 時間くらいですね。

[^compose-pokedex]: 完成度が高い Compose を使ったポケモン図鑑をみたい人は，[zsoltk/compose-pokedex (GitHub)](https://github.com/zsoltk/compose-pokedex/) が参考になると思います。

## PokéAPI とは

[PokéAPI](https://pokeapi.co/) はポケモンに関する大量のデータ（ポケモン，技，タイプ，特性，ゲームバージョン，図鑑説明，アイテムなど）があります。詳しくは [PokéAPI/About](https://pokeapi.co/about/) を確認してください。

例えば https://pokeapi.co/api/v2/pokemon/ditto/ のように API を叩くと，メタモンに関するデータが取得できます。API の叩き方や取得できるデータについては [PokéAPI/Doc](https://pokeapi.co/docs/v2/) を確認してください。とっても詳しく書いてあります。

## 仕様を決める

| ポケモン図鑑（リスト）                                                                                                                         | ポケモン詳細                                                                                                                                   |
| ---------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| ![Screenshot_20211207-175546.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/699841/33f39168-fbba-4c85-b9ec-735ce2b3ef31.png) | ![Screenshot_20211207-175550.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/699841/3a63796f-04b7-609b-86fa-daceddd57fce.png) |

↑のように，番号順でポケモンが並んでいて，ポケモンをタップすることで，そのポケモンの詳細画面を表示しようと思います。詳細画面には，

- 全国図鑑でのポケモンの番号
- ポケモンの名前（英名）
- ポケモンの画像（正面から 1 種類だけ）

を表示しようと思います。

タイプや図鑑説明は [pokemon-species](https://pokeapi.co/api/v2/pokemon-species/) を別途叩かないといけないので，めんどくさいのでやりません。

## いざ作成

基本的な流れは Android MVVM アーキテクチャ入門 の通りに進めていきます。特に変えた点を説明していきます。わからないところは [これ](https://github.com/Daiji256/Pokedex/) を見て気合いで乗り越えてください。

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 ライブラリです。

```gradle:build.gradle
buildscript {
    ...
    dependencies {
        ...
        classpath "org.jetbrains.kotlin:kotlin-serialization:1.5.31"
        classpath "com.google.dagger:hilt-android-gradle-plugin:2.37"
    }
}
```

```gradle:app/build.gradle
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](https://pokeapi.co/api/v2/pokedex/national/) とポケモンの情報を取得する [pokemon/{name}](https://pokeapi.co/api/v2/pokemon/) が叩けるようにします。`{name}` でわかると思いますが，`getPokeApiPokemon` はポケモンの名前または番号を引数とします。それぞれ `PokeApiPokedex` と `PokeApiPokemon` を戻り値とします。

`Porovider` と `Module` 及び `RemoteDataSource...` の説明は書略します。

```kotlin:ApiClient
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` にデータを変換して使います。

```kotlin:PokeApiPokemon
@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,
)
```

```kotlin:Pokemon
data class Pokemon(
    val id: Int,
    val name: String,
    val frontImage: NetworkImage,
)
```

### Repository

そんなに参考書と変えてないので省略です。

### UI

ポケモン図鑑の情報を取得するために，`MainViewModel` に次のコードを書きます。

```kotlin: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 ではポケモンの情報を取得します。

```kotlin:PokedexView
@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()` を呼び出しています。

```kotlin:PokemonDetailView
@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 を使うときより簡単に感じます。

時間の関係で結構適当なアプリ / 記事になってしまい残念です。アプリ修正して，丁寧な説明を追加して個人ブログに載せたいな〜と思ってます。めんどくさいからやらない気がするけど...

## 文献

1. [作成したソースコード (GitHub)](https://github.com/Daiji256/Pokedex/)
2. [PokéAPI](https://pokeapi.co/)
3. [Jetpack Compose による Android MVVM アーキテクチャ入門](https://www.amazon.co.jp/gp/product/B09DNV323P/)
4. [zsoltk/compose-pokedex (GitHub)](https://github.com/zsoltk/compose-pokedex/)