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


この記事は,フラー株式会社 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 ライブラリです。

build.gradle
buildscript {
    ...
    dependencies {
        ...
        classpath "org.jetbrains.kotlin:kotlin-serialization:1.5.31"
        classpath "com.google.dagger:hilt-android-gradle-plugin:2.37"
    }
}
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 とポケモンの情報を取得する pokemon/{name} が叩けるようにします。{name} でわかると思いますが,getPokeApiPokemon はポケモンの名前または番号を引数とします。それぞれ PokeApiPokedexPokeApiPokemon を戻り値とします。

PoroviderModule 及び RemoteDataSource... の説明は書略します。

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

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,
)
Pokemon
data class Pokemon(
    val id: Int,
    val name: String,
    val frontImage: NetworkImage,
)

Repository

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

UI

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

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

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 では idname の他にポケモンの正面の画像を表示します。painter = rememberImagePainter(URL) で URL にある画像を簡単に表示しています。BackHandler で戻るボタンが押されたときに backHome() します。実際には getPokedex() を呼び出しています。

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)
  2. PokéAPI
  3. Jetpack Compose による Android MVVM アーキテクチャ入門
  4. zsoltk/compose-pokedex (GitHub)

  1. 週に 2 日くらいです。合計 300 時間くらいですね。 

  2. 完成度が高い Compose を使ったポケモン図鑑をみたい人は,zsoltk/compose-pokedex (GitHub) が参考になると思います。