6
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

簡単な天気アプリをKotlin初心者が実装した話

こんにちはmotochikaです。
1ヶ月ほど前から趣味でKotlinの勉強をしています。
最近、HTTP通信を利用した簡単な天気アプリを便利なライブラリを使いながら作ったので記事にしてみました。

APIはLiveDoor天気情報のWeather Hacksです。
http://weather.livedoor.com/weather_hacks/webservice
*WeatherHacksは2020/7/31を以って終了してしまったそうです。

デモ画面

picture2.jpg

例えば明日の天気のボタンを押すと
picture3.jpg

画面に表示された地域はほんの一例で、お天気APIに対応している地域ならどこでも表示できます。

使用したライブラリ

Retrofit2

APIをJava/Kotlinインターフェースに変換するライブリです。
公式
retrofit2とmoshiのコンバータを導入します。

build.gradle
implementation 'com.squareup.retrofit2:retrofit:retrofit2の現バージョン'
implementation 'com.squareup.retrofit2:converter-moshi:retrofit2の現バージョン'

Moshi

Jsonのパースを行うライブラリです。
kotlinクラスへのサポートを提供しています。
公式

build.grade
implementation "com.squareup.moshi:moshi:moshiの現バージョン"
implementation "com.squareup.moshi:moshi-adapters:moshiの現バージョン"
implementation "com.squareup.moshi:moshi-kotlin:moshiの現バージョン"
kapt "com.squareup.moshi:moshi-kotlin-codegen:moshiの現バージョン"

最後のkaptですがkotlin-annotation-processing toolsといって、アノテーションを使いコードを生成できるツールです。retrofit2を使う際に必要です。

build.gradle
apply plugin: 'kotlin-kapt'

最初にこう書いてあげることも忘れずに。

内容

はじめにインターフェースを作成します。

WeatherApi.kt

import com.example.motochika.getweather.api.WeatherInfo
import retrofit2.http.GET
import retrofit2.http.Query

interface WeatherApi {
    //"forecast/webservice/json/v1"にGETリクエストをする関数
    @GET("/forecast/webservice/json/v1")
    suspend fun getWeatherInfo( 
        @Query("city")
        city: String
    ): WeatherInfo
}

Retrofit2では$@$GET(規定URL以降の部分)でGETリクエスト先のAPIを指定できます。
http://weather.livedoor.com/forecast/webservice/json/v1?city=400040

関数getWeatherInfoでは引数をリクエストに含めるパラメータとし、戻り値はJSONデータを受け取るデータクラスWeatherInfoとしています。

次のクラスApiFactoryでは上記のインターフェースをインスタンス化して返す関数weatherApiを定義しています。

ApiFactory.kt
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import retrofit2.create

class ApiFactory {

    fun weatherApi(): WeatherApi {

        val BASE_URL = "http://weather.livedoor.com"

        val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()

        val retrofit = Retrofit.Builder().baseUrl(BASE_URL)
            .addConverterFactory(MoshiConverterFactory.create(moshi))
            .build()

        return retrofit.create(WeatherApi::class.java)

    }

}

関数weatherApiでは生成したMoshiのインスタンスと規定URLを元にしてretrofitインスタンスを生成します。こうしてあるプログラムでApiFactoryのインスタンスを生成した際に関数weatherApiを中継して先ほどの関数getWeatherInfoを呼び出すことができます。

次のコードのwhen文の内部をみてください。

Result.kt

    suspend fun getWeather(id: String, day: String) = withContext(Dispatchers.IO) {
        val http = ApiFactory()

        when (day) {
            "今日" -> http.weatherApi().getWeaterInfo(id).forecasts.find {
                it.dateLabel == "今日"
            }?.telop
            "明日" -> http.weatherApi().getWeaterInfo(id).forecasts.find {
                it.dateLabel == "明日"
            }?.telop
            "明後日" -> http.weatherApi().getWeaterInfo(id).forecasts.find {
                it.dateLabel == "明後日"
            }?.telop

            else -> "日なし"
        }

    }

    suspend fun setWeatherText(day: String) {

        weather_result.setText(
            "札幌の${Day}の天気:" + setWeather("016010", Day) + "\n" +
                    "旭川の${day}の天気:" + getWeather("012010", day) + "\n" +
                    "函館の${day}の天気:" + getWeather("017010", day) + "\n" +
                    "仙台の${day}の天気:" + getWeather("040010", day) + "\n" +
                    "米沢の${day}の天気:" + getWeather("060020", day) + "\n" +
                    "新潟の${day}の天気:" + getWeather("150010", day) + "\n" +
                    "会津若松の${day}の天気:" + getWeather("070030", day) + "\n" +
                    "東京の${day}の天気:" + getWeather("130010", day) + "\n" +
                    "横浜の${day}の天気:" + getWeather("140010", day) + "\n" +
                    "名古屋の${day}の天気:" + getWeather("230010", day) + "\n" +
                    "大阪の${day}の天気:" + getWeather("270000", day) + "\n" +
                    "京都の${day}の天気:" + getWeather("260010", day) + "\n" +
                    "広島の${day}の天気:" + getWeather("340010", day) + "\n" +
                    "熊本の${day}の天気:" + getWeather("430010", day) + "\n"
        )

    }
}

関数getWeatherInfoに地域idを投げるだけで、戻り値のデータクラスについて様々な処理ができます。そして関数getWeatherでは地域idと今日〜明後日というキーワードを元に天気予報を返しています。

余談ですが weather_result.setTextについて、weather_resultはTextViewのid名です。
kotlin-android-extensionsを使用すると、findViewById()を使わなくても直接id名が使えます。小さいことですがとても感動しました。

最後に

こうして記事にすると自分で書いたコードでもまだ理解できていない場所が洗い出されるので、Qiitaに投稿するのはとても良いことだなぁと思いました。

サポートしてくれてありがとうございます。@yt8492

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
6
Help us understand the problem. What are the problem?