在上一篇文章中,我們完成了UI界面的編寫
接下來我們就要把搜索的結果,顯示在界面上。
在Android開發中有很多種方式訪問網絡,本次視頻將向大家介紹Retrofit,
Retrofit由Square開發的,它構建在OkHttp之上。它是一個流行的庫,可以輕松地進行異步網絡調用并將JSON數據處理為模型對象。
在Android開發中有很多種方式訪問網絡,本次視頻將向大家介紹Retrofit,Retrofit由Square開發的,它構建在OkHttp之上。它是一個流行的庫,可以輕松地進行異步網絡調用并將JSON數據處理為模型對象。本次視頻您將了解Retrofit庫的簡單實用,Moshi解析Json等知識。
在使用Retrofit之前,我們需要先在項目中添加Retrofit庫的依賴。編輯app/build.gradle文件,在dependencies閉包中添加如下內容:
//retrofitimplementation"com.squareup.retrofit2:retrofit:2.9.0"//moshiimplementation("com.squareup.moshi:moshi-kotlin:1.12.0")//retrofitwithmoshiimplementation"com.squareup.retrofit2:converter-moshi:2.9.0"//coilimplementation("io.coil-kt:coil-compose:1.3.2")//kotlincoroutineimplementation"org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3"implementation"org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3"//viewmodeldeflifecycle_version="2.3.1"implementation("androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version")implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version")retrofit庫的依賴,主要負責網絡請求,允許我們發送GET,POST請求
moshi庫的依賴,moshi庫將幫助我們將json數據轉換為kotlin對象
converter-moshi庫,增加了對retrofit使用moshi進行JSON解析的支持
coil庫,允許我們使用url加載網絡圖片,我們可以使用少量的代碼,完成圖片的加載,coil庫也是采用kotlin編寫
kotlincoroutine,我們使用kotlincoroutine的Flow處理網路的異步請求
ViewModel依賴,使用ViewModel使視圖和數據能夠分離開
完成之后我們重新編譯一下項目
Postman是查看API接口返回結果非常優秀的程序,我們啟動Postman。
使用默認的GET***,輸入下面的URL地址https://api.map.baidu.com/weather/v1/?district_id=110100&data_type=all&ak=m5ABoErD6VuCKdyGfqoEjflYvSmn1XqR,然后單擊Send。如下圖:
在搜索結果中,將輸出類型設置為JSON。您將看到格式良好的JSON顯示:如下圖:
接下來,我們將創建天氣信息的數據類
數據類的創建,我們借助Kotlin插件,將Json字符串快速轉換為Kotlin數據類代碼
在model包下右鍵;如下圖:
點擊Advanced,這個支持(幾乎)各種JSON庫注釋(Gson、Jackson、Fastjson、MoShi和LoganSquare)這個我們選擇MoShi
點擊生成,如下圖:
這樣我們的數據類型就很方便的創建了。如下圖:
接下來,編寫我們的網絡服務
objectWeatherApiClient{privatevalBASE_URL="https://api.map.baidu.com"privatevalmoshi=Moshi.Builder().add(KotlinJsonAdapterFactory()).build()privatevalretrofit:Retrofitbylazy{Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(MoshiConverterFactory.create(moshi)).build()}valweatherApiService:WeatherApiServicebylazy{retrofit.create(WeatherApiService::class.java)}}interfaceWeatherApiService{@GET("/weather/v1/")suspendfungetWeatherData(@Query("district_id")district_id:String,@Query("data_type")data_type:String,@Query("ak")ak:String):WeatherModel}在上面的代碼中,我們創建一個私有的BASE_URL變量,我們需要為moshi構造器創建一個變量,添加KotlinJson的適配器工廠(KotlinJsonAdapterFactory),創建Retrofit,這里使用bylazy關鍵字創建Retrofit實例,這樣僅在需要時進行初始化,傳入BASE_URL,添加MoshiConverterFactory轉換器工廠,然后構建.接下來,創建一個接口,獲取api接口數據,這里我們創建一個函數,設置了查詢參數,調用這個***就會返回查詢的數據。接下來,創建api接口的實例這里也是使用bylazy關鍵字創建延遲加載的實例,通過創建好的Retrofit來創建api接口服務。
下面創建Repository(數據倉庫)
classWeatherRepository{companionobject{fungetWeather(district_id:String,data_type:String,ak:String):Flow<WeatherModel>=flow{varweather=WeatherApiClient.weatherApiService.getWeatherData(district_id,data_type,ak)emit(weather)}.flowOn(Dispatchers.IO)}}下面創建ViewModel
classWeatherViewModel:ViewModel(){valweatherData:MutableState<WeatherState>=mutableStateOf(WeatherState.Empty)init{getWeatherData("110100","all","m5ABoErD6VuCKdyGfqoEjflYvSmn1XqR");}fungetWeatherData(district_id:String,data_type:String,ak:String){viewModelScope.launch{WeatherRepository.getWeather(district_id,data_type,ak).onStart{weatherData.value=WeatherState.Loading}.catch{e->weatherData.value=WeatherState.Failure(e)}.collect{response->weatherData.value=WeatherState.Success(response)}}}}在創建ViewModel之前,需要創建Model的狀態類
sealedclassWeatherState{classSuccess(valweather:WeatherModel):WeatherState()classFailure(valerror:Throwable):WeatherState()objectLoading:WeatherState()objectEmpty:WeatherState()}創建好之后,我們在Activity中使用ViewModel
classMainActivity:ComponentActivity(){privatevalweatherViewModel:WeatherViewModelbyviewModels()@ExperimentalMaterialApioverridefunonCreate(savedInstanceState:Bundle?){super.onCreate(savedInstanceState)setContent{WeatherAppTheme{Surface(color=MaterialTheme.colors.background){setWeather(weatherViewModel=weatherViewModel)}}}}}添加數據請求的狀態,如下圖:
@ExperimentalMaterialApi@ComposablefunsetWeather(weatherViewModel:WeatherViewModel){when(valresult=weatherViewModel.weatherData.value){isWeatherState.Success->{Log.d("--result--",result.weather.toString())}isWeatherState.Failure->{Text(text="${result.error}")}isWeatherState.Loading->{Column(horizontalAlignment=Alignment.CenterHorizontally,modifier=Modifier.fillMaxSize().background(brush=Brush.verticalGradient(colors=listOf(color1,color2)))){Surface(onClick={},modifier=Modifier.fillMaxWidth(0.6f),color=Color.Transparent){Row(modifier=Modifier.padding(12.dp),verticalAlignment=Alignment.CenterVertically,horizontalArrangement=Arrangement.Center){CircularProgressIndicator(modifier=Modifier.height(16.dp).width(16.dp),strokeWidth=2.dp,color=Color.White)Spacer(modifier=Modifier.width(10.dp))ComposeText(text="加載天氣數據中...",textColor=Color.White,fontSize=16.sp)}}}}WeatherState.Empty->{}}}在上面的代碼中,我們添加Loading,狀態的UI。
下面在WeatherState.Success狀態下,對UI界面賦值。
Column(horizontalAlignment=Alignment.CenterHorizontally,modifier=Modifier.fillMaxSize().background(brush=Brush.verticalGradient(colors=listOf(color1,color2)))){Spacer(modifier=Modifier.height(20.dp))LocationScreen(result.weather.result.location)Spacer(modifier=Modifier.height(40.dp))NowScreen(result.weather.result.now)Spacer(modifier=Modifier.height(20.dp))WeatherDaysScreen(result.weather.result.forecasts)}設置定位信息@ComposablefunLocationScreen(location:WeatherModel.Result.Location){ComposeText(text="${location.city},${location.name}",fontSize=30.sp)}2.設置當天天氣信息
@ComposablefunNowScreen(now:WeatherModel.Result.Now){Column(horizontalAlignment=Alignment.CenterHorizontally){valimg_url=when(now.text){"陰"->"http://www.moji.com/templets/mojichina/images/weather/weather/w2.png""雷陣雨"->"http://www.moji.com/templets/mojichina/images/weather/weather/w4.png""多云"->"http://www.moji.com/templets/mojichina/images/weather/weather/w1.png"else->"http://www.moji.com/templets/mojichina/images/weather/weather/w1.png"}Image(painter=rememberImagePainter(img_url),contentDescription="",modifier=Modifier.size(100.dp))ComposeText(text="${now.temp}°",fontSize=48.sp)}Surface(modifier=Modifier.fillMaxWidth(0.5f),color=color3,shape=RoundedCornerShape(48)){Row(horizontalArrangement=Arrangement.SpaceBetween,verticalAlignment=Alignment.CenterVertically,modifier=Modifier.fillMaxWidth().padding(8.dp)){ComposeText(text="${now.windDir}",textColor=colortext,fontSize=12.sp)ComposeText(text="${now.windClass}",textColor=colortext,fontSize=12.sp)ComposeText(text="濕度",textColor=colortext,fontSize=12.sp)ComposeText(text="${now.rh}%",textColor=colortext,fontSize=12.sp)}}}3.設置未來5天的天氣信息
@ComposablefunWeatherDaysScreen(forecasts:List<WeatherModel.Result.Forecast>){LazyRow{items(forecasts){forecast->WeatherItems(forecast)}}}@ComposablefunWeatherItems(forecast:WeatherModel.Result.Forecast){Column(horizontalAlignment=Alignment.CenterHorizontally,modifier=Modifier.padding(start=8.dp,end=10.dp)){ComposeText(text="${forecast.week}",fontSize=16.sp)Spacer(modifier=Modifier.height(18.dp))valimg_url=when(forecast.textDay){"陰"->"http://www.moji.com/templets/mojichina/images/weather/weather/w2.png""雷陣雨"->"http://www.moji.com/templets/mojichina/images/weather/weather/w4.png""多云"->"http://www.moji.com/templets/mojichina/images/weather/weather/w1.png"else->"http://www.moji.com/templets/mojichina/images/weather/weather/w1.png"}Image(painter=rememberImagePainter(img_url),contentDescription="",modifier=Modifier.size(50.dp))Spacer(modifier=Modifier.height(2.dp))ComposeText(text="${forecast.high}°/${forecast.low}°",fontSize=22.sp)}}