> 技术文档 > 【HarmonyOS NEXT星河版开发实战】天气查询APP_鸿蒙next开发中查询天气

【HarmonyOS NEXT星河版开发实战】天气查询APP_鸿蒙next开发中查询天气

目录

前言

界面效果展示

首页

添加和删除 

界面构建讲解

1. 获取所需数据

 2. 在编译器中准备数据

 3. index页面代码讲解

  3.1 导入模块:

 3.2 定义组件:

3.3 定义状态变量:

3.4  定义Tabs控制器:

3.5 定义按钮样式:

 3.6 页面显示时触发的方法:

 3.7 获取数据的方法:

3.8  初始化数据的方法:

 3.9 构建UI:

4. AddCity页面代码讲解

   4.1 导入模块:

   4.2 定义组件:

   4.3 定义状态变量:

   4.4 接受数据的载体:

   4.5 页面显示时触发的方法:

   4.6 构建UI:

 5. getWeather页面代码讲解

  5.1 导入模块:

  5.2 定义类:

  5.3 定义方法 getWeather:

  5.4 定义方法 getWeathers:

  5.5 实例化并导出类:

全套源代码展示

AddCity 

Index

getWeather

casts

cityView

forecasts

WeatherModel


 

个人主页→VON

收录专栏→鸿蒙综合案例开发​​​​​

代码及其图片资源会发布于gitee上面(已发布)

gitee地址【HarmonyOS NEXT星河版开发实战】天气查询APP_鸿蒙next开发中查询天气https://gitee.com/wang-xin-jie234/harmony-os

 

​ 

前言

基础部分如今已经学完,可能有些东西并不是太了解,在练习实战项目的过程中也是一个不断学习的过程,因为实战项目复杂代码量较大,所以写的时候要及其认真,一个小的错误可能会花费几小时的时间去寻找bug。所以在学习的过程中要十分严谨,希望大家可以跟着我的思路独自完成天气查询app这一项目。

界面效果展示

首页

 首页包括添加删除天气的功能,还有近五天天气的展示,以及温度的展示。

添加和删除 

 

添加城市列表是本地进行导入的,如果有需要可以将想要的城市自行导入进去,添加完城市点击完成会跳转到首页进行渲染展示。删除功能,点击确定的时候改城市的信息会被删除。 

界面构建讲解

1. 获取所需数据

因为天气的数据需要我们联网去获取,所以要去调用天气的API。

我这里用的是高德开放平台,没有注册过的要先进行账号的注册。

注:必须要进行账号注册,后面要用到我们个人的Key值,每个用户都不一样。 

拿到key之后在浏览器上输入服务示例+key+extensions=all 

 2. 在编译器中准备数据

​ 

 3. index页面代码讲解

  3.1 导入模块

import getweatherutil from \'../util/getWeather\'import { cityView } from \'../view/cityView\'import {WeatherModel} from \'../view/WeatherModel\'import router from \'@ohos.router\'

 3.2 定义组件

@Entry@Componentstruct Index {

3.3 定义状态变量:

// 城市代码集合@State cityCoeList: number[] = [110000, 120000]// 城市名字集合@State cityNameList: string[] = []// 城市信息集合@State cityWeatherList: Array = []// 当前城市索引@State cityIndex: number = 0

3.4  定义Tabs控制器:

tabController: TabsController = new TabsController()

3.5 定义按钮样式

@Builder tabBuild(index: number) { Circle({ width: 10, height: 10 }) .fill(this.cityIndex === index ? Color.White : Color.Gray).opacity(0.4)}

 3.6 页面显示时触发的方法

onPageShow() { let params = router.getParams() if (params !== null) { this.cityCoeList = params[\'codes\'] // 清空所有数据 this.cityWeatherList = [] this.cityNameList = [] this.initDate() }}

 3.7 获取数据的方法

aboutToAppear() { this.initDate()}

3.8  初始化数据的方法

async initDate() { let result: Array = await getweatherutil.getWeathers(this.cityCoeList) for (let i = 0; i < result.length; i++) { // 城市天气 let ACityWeather = new WeatherModel() ACityWeather = result[i] this.cityWeatherList.push(ACityWeather) // 城市名称 let cityname = result[i].forecasts[0].city this.cityNameList.push(cityname) }}

 3.9 构建UI

build() { Column() { Row() { Button(\'添加\') .fontSize(25) .fontColor(Color.Gray) .backgroundColor(\'#00ffffff\') .onClick(() => { router.pushUrl({ url: \"pages/AddCity\", params: {  codes: this.cityCoeList,  names: this.cityNameList } }) }) Text(this.cityNameList[this.cityIndex]).fontSize(40).fontColor(Color.Orange) Button(\'删除\') .fontSize(25) .fontColor(Color.Gray) .backgroundColor(\'#00ffffff\') .onClick(() => { AlertDialog.show({ title: \'删除\', message: `你确定要删除${this.cityNameList[this.cityIndex]}吗?`, confirm: {  value: \'确定\',  action: () => { this.cityNameList = this.cityNameList.filter(item => item !== this.cityNameList[this.cityIndex]) this.cityCoeList = this.cityCoeList.filter(item => item !== this.cityCoeList[this.cityIndex]) this.cityWeatherList = this.cityWeatherList.filter(item => item !== this.cityWeatherList[this.cityIndex])  } } }) }) } .width(\'100%\') .height(\'10%\') .justifyContent(FlexAlign.SpaceBetween) Tabs({ barPosition: BarPosition.Start, controller: this.tabController }) { ForEach(this.cityWeatherList, (cityWeather: WeatherModel) => { TabContent() { cityView({ casts: cityWeather.forecasts[0].casts }) } .tabBar(this.tabBuild(this.cityWeatherList.findIndex((obj => obj === cityWeather)))) }) } .barWidth(40) .barHeight(40) .onChange((index: number) => { this.cityIndex = index }) } .width(\'100%\') .height(\'100%\') .backgroundImage($r(\'app.media.weather_background\')) .backgroundImageSize({ width: \'100%\', height: \'100%\' }) .backgroundImagePosition({ x: 0, y: 0 })}

4. AddCity页面代码讲解

   4.1 导入模块

import router from \'@ohos.router\'

   4.2 定义组件

@Entry@Componentstruct AddCity {

   4.3 定义状态变量

// 所有城市的代码列表@State AllCityCodeList: Array = [ 410100, 410200, 410300, 410400, 410500, 410600, 410700, 410800, 410900, 411000, 411200, 411300, 411400, 411500, 411600, 411700]// 所有城市的名称列表@State AllCityNameList: Array = [ \'郑州市\', \'开封市\', \'洛阳市\', \'平顶山市\', \'安阳市\', \'鹤壁市\', \'新乡市\', \'焦作市\', \'濮阳市\', \'许昌市\', \'三门峡市\', \'南阳市\', \'商丘市\', \'信阳市\', \'周口市\', \'驻马店市\']

   4.4 接受数据的载体

@State cityCodeList: number[] = []@State cityNameList: string[] = []

   4.5 页面显示时触发的方法

onPageShow() { let param = router.getParams() this.cityCodeList = param[\'codes\'] this.cityNameList = param[\'names\']}

   4.6 构建UI

build() { Column() { Row() { Text(\'添加城市列表\') .fontSize(35) .fontColor(Color.White) Blank() Button(\'完成\') .fontSize(26) .backgroundColor(\"\") .onClick(() => { router.back({ url: \'pages/Index\', params: {  codes: this.cityCodeList,  names: this.AllCityNameList } }) }) } .height(\'10%\') .width(\'95%\') Column() { List() { ForEach(this.AllCityNameList, (name: string) => { ListItem() { if (this.cityNameList.includes(name)) {  Column() { Row() {  Text(name)  .fontSize(35)  .fontColor(Color.White)  .width(\'60%\')  .margin({ top: 20, left: 30 })  Blank()  Text(\'已添加\')  .backgroundColor(\'\')  .fontSize(18)  .margin({ top: 5 }).opacity(0.8) }.width(\'100%\') Blank() Divider().strokeWidth(5)  }  .height(90)  .width(\'100%\')  .margin({ top: 20 })  .backgroundColor(\'#ff4a93c6\') } else {  Column() { Row() {  Text(name)  .fontSize(35)  .fontColor(Color.White)  .width(\'60%\')  .margin({ top: 20, left: 30 })  Blank()  Button(\'添加\')  .margin({ right: 5 })  .backgroundColor(\"\")  .onClick(() => {// 根据name来获取索引let index = this.AllCityNameList.findIndex(obj => obj === name)// 根据索引获取城市编码let cityCode: number = this.AllCityCodeList[index]// 将城市编码加入列表this.cityCodeList.push(cityCode)this.cityNameList.push(name)  }) }.width(\'100%\') Blank() Divider().strokeWidth(5)  }  .height(90)  .width(\'100%\')  .margin({ top: 20 }) } } }) } } } .width(\'100%\') .height(\'100%\') .backgroundImage($r(\'app.media.weather_background\')) .backgroundImageSize({ width: \'100%\', height: \'100%\' }) .backgroundImagePosition({ x: 0, y: 0 })}

 5. getWeather页面代码讲解

  5.1 导入模块

import { WeatherModel } from \'../view/WeatherModel\'import http from \'@ohos.net.http\'

  5.2 定义类

class getWeatherUtil {

  5.3 定义方法 getWeather

// 发送一个url,返回对应的数据getWeather(cityCode: number): Promise { return new Promise((resolve, reject) => { let request = http.createHttp() let url = `https://restapi.amap.com/v3/weather/weatherInfo?city=${cityCode}&key=57b5118aed53d7a188874fc44256a0b8&extensions=all` let result = request.request(url) result.then((res) => { if (res.responseCode === 200) { console.log(res.result.toString()); resolve(JSON.parse(res.result.toString())) } }).catch((err) => { console.log(err) reject(err) }) })}

  5.4 定义方法 getWeathers

// 直接发送多个url结果一并返回async getWeathers(cityCodes: Array): Promise<Array> { let promises: Array<Promise> = [] let weatherModels: Array = [] for (let i = 0; i  { for (const element of result) { console.log(element.forecasts[0].city) } weatherModels = result }).catch((err) => { console.log(err) }) return weatherModels}

  5.5 实例化并导出类

let getweatherutil = new getWeatherUtil()export default getweatherutil as getWeatherUtil

 getWeather  方法

  • 该方法接受一个城市代码 cityCode,并返回一个 Promise
  • 创建一个 HTTP 请求对象 request
  • 构建请求 URL,包含城市代码和 API 密钥。
  • 发送 HTTP 请求并处理响应。
  • 如果响应码为 200,解析响应结果并返回 WeatherModel 对象。
  • 如果发生错误,捕获并记录错误。

 getWeat hers 方法

  • 该方法接受一个城市代码数组 cityCodes,并返回一个 Promise<Array>
  • 创建一个空数组 promises 用于存储每个城市天气请求的 Promise。
  • 遍历 cityCodes,为每个城市代码调用 getWeather 方法,并将返回的 Promise 添加到 promises 数组中。
  • 使用 Promise.all 等待所有请求完成,并处理结果。
  • 遍历结果数组,记录每个城市的名称。
  • 将结果赋值给 weatherModels 数组并返回。
  • 如果发生错误,捕获并记录错误。

全套源代码展示

AddCity 

import router from \'@ohos.router\'@Entry@Componentstruct AddCity { @State AllCityCodeList:Array= [410100,410200,410300,410400,410500,410600,410700,410800,410900,411000,411200,411300,411400,411500,411600,411700] @State AllCityNameList:Array= [\'郑州市\',\'开封市\',\'洛阳市\',\'平顶山市\',\'安阳市\',\'鹤壁市\',\'新乡市\',\'焦作市\',\'濮阳市\',\'许昌市\',\'三门峡市\',\'南阳市\',\'商丘市\',\'信阳市\',\'周口市\',\'驻马店市\'] // 接受数据的载体 @State cityCodeList:number[]=[] @State cityNameList:string[]=[] onPageShow(){ let param=router.getParams() this.cityCodeList=param[\'codes\'] this.cityNameList=param[\'names\'] } build() { Column(){ Row(){ Text(\'添加城市列表\') .fontSize(35) .fontColor(Color.White) Blank() Button(\'完成\') .fontSize(26) .backgroundColor(\"\") .onClick(()=>{ router.back({  url:\'pages/Index\',  params:{ codes:this.cityCodeList, names:this.AllCityNameList  } }) }) } .height(\'10%\') .width(\'95%\') Column(){ List(){ ForEach(this.AllCityNameList,(name:string)=>{ ListItem(){  if(this.cityNameList.includes(name)){ Column(){  Row(){  Text(name).fontSize(35).fontColor(Color.White).width(\'60%\').margin({top:20,left:30})  Blank()  Text(\'已添加\').backgroundColor(\'\').fontSize(18).margin({top:5}).opacity(0.8)  }.width(\'100%\')  Blank()  Divider().strokeWidth(5) } .height(90) .width(\'100%\') .margin({top:20}) .backgroundColor(\'#ff4a93c6\')  }else{ Column(){  Row(){  Text(name).fontSize(35).fontColor(Color.White).width(\'60%\').margin({top:20,left:30})  Blank()  Button(\'添加\').margin({right:5}).backgroundColor(\"\").onClick(()=>{ // 根据name来获取索引 let index=this.AllCityNameList.findIndex(obj=>obj===name) // 根据索引获取城市编码 let cityCode:number=this.AllCityCodeList[index] // 将城市编码加入列表 this.cityCodeList.push(cityCode) this.cityNameList.push(name)})  }.width(\'100%\')  Blank()  Divider().strokeWidth(5) } .height(90) .width(\'100%\') .margin({top:20})  } } }) } } } .width(\'100%\') .height(\'100%\') .backgroundImage($r(\'app.media.weather_background\')) .backgroundImageSize({width:\'100%\',height:\'100%\'}) .backgroundImagePosition({x:0,y:0}) }}

Index

import getweatherutil from \'../util/getWeather\'import { cityView } from \'../view/cityView\'import {WeatherModel} from \'../view/WeatherModel\'import router from \'@ohos.router\'@Entry@Componentstruct Index { // 城市代码集合 @State cityCoeList:number[] =[110000,120000] // 城市名字集合 @State cityNameList:string[]=[] // 城市信息集合 @State cityWeatherList:Array=[] // 当前城市索引 @State cityIndex:number=0 tabController:TabsController=new TabsController() // 按钮样式 @Builder tabBuild(index:number){ Circle({width:10,height:10}) .fill(this.cityIndex===index?Color.White:Color.Gray).opacity(0.4) } onPageShow(){ let params=router.getParams() if(params!==null){ this.cityCoeList=params[\'codes\'] // 清空所有数据 this.cityWeatherList=[] this.cityNameList=[] this.initDate() } } // 获取数据 aboutToAppear(){ this.initDate() } // 初始化方法 async initDate(){ let result:Array =await getweatherutil.getWeathers(this.cityCoeList) for (let i = 0; i { router.pushUrl({  url:\"pages/AddCity\",  params:{ codes:this.cityCoeList, names:this.cityNameList  } }) }) Text(this.cityNameList[this.cityIndex]).fontSize(40).fontColor(Color.Orange) Button(\'删除\') .fontSize(25) .fontColor(Color.Gray) .backgroundColor(\'#00ffffff\') .onClick(()=>{ AlertDialog.show({  title:\'删除\',  message:`你确定要删除${this.cityNameList[this.cityIndex]}吗?`,  confirm:{ value:\'确定\', action:()=>{  this.cityNameList=this.cityNameList.filter(item=>item!==this.cityNameList[this.cityIndex])  this.cityCoeList=this.cityCoeList.filter(item=>item!==this.cityCoeList[this.cityIndex])  this.cityWeatherList=this.cityWeatherList.filter(item=>item!==this.cityWeatherList[this.cityIndex]) }  } }) }) } .width(\'100%\') .height(\'10%\') .justifyContent(FlexAlign.SpaceBetween) Tabs({barPosition:BarPosition.Start,controller:this.tabController}){ ForEach(this.cityWeatherList,(cityWeather:WeatherModel)=>{ TabContent(){ cityView({casts:cityWeather.forecasts[0].casts}) } .tabBar(this.tabBuild(this.cityWeatherList.findIndex((obj=>obj===cityWeather)))) }) } .barWidth(40) .barHeight(40) .onChange((index:number)=>{ this.cityIndex=index }) } .width(\'100%\') .height(\'100%\') .backgroundImage($r(\'app.media.weather_background\')) .backgroundImageSize({width:\'100%\',height:\'100%\'}) .backgroundImagePosition({x:0,y:0}) }}

getWeather

import {WeatherModel} from \'../view/WeatherModel\'import http from \'@ohos.net.http\'class getWeatherUtil{ // 发送一个url,返回对应的数据 getWeather(cityCode:number){ return new Promise((resolve,reject)=>{ let request=http.createHttp() let url=`https://restapi.amap.com/v3/weather/weatherInfo?city=${cityCode}&key=57b5118aed53d7a188874fc44256a0b8&extensions=all` let result=request.request(url) result.then((res)=>{ if(res.responseCode===200){ console.log(res.result.toString()); resolve(JSON.parse(res.result.toString())) } }).catch((err)=>{ console.log(err) }) }) } // 直接发送多个url结果一并返回 async getWeathers(cityCodes:Array){ let promises:Array<Promise>=[] let weatherModels:Array=[] for (let i = 0; i { for(const element of result){ console.log(element.forecasts[0].city) } weatherModels=result }) return weatherModels }}let getweatherutil=new getWeatherUtil()export default getweatherutil as getWeatherUtil

casts

export class casts{ date:string dayweather:string nightweather:string daytemp:number nighttemp:number daywind:string daypower:string daytemp_float:number nighttemp_float:number}

cityView

import {casts} from \'../view/casts\'@Componentexport struct cityView { // 获取数据 // 城市天气数据 casts:Array=[] @Builder weartherImage(weather:string){ if(weather===\'晴\'){ Image($r(\'app.media.sun\')) .width(23) } if(weather===\'阴\'){ Image($r(\'app.media.cloudy\')) .width(30) } if(weather===\'多云\'){ Image($r(\'app.media.cloudy\')) .width(30) } if(weather.includes(\'雨\')){ Image($r(\'app.media.rain\')) .width(30) } } // 展示数据 build() { Column(){ // 当天天气数据 ForEach(this.casts,(cast:casts)=>{ if(this.casts[0]===cast){ // 展示天气所对应图片 Row(){ if(cast.dayweather===\'晴\'){  Image($r(\'app.media.sun\')) .width(250) } if(cast.dayweather===\'阴\'){  Image($r(\'app.media.cloudy\')) .width(250) } if(cast.dayweather===\'多云\'){  Image($r(\'app.media.cloudy\')) .width(250) } if(cast.dayweather.includes(\'雨\')){  Image($r(\'app.media.rain\')) .width(300) } } Column(){ // 温度天气 Row(){  Text(cast.dayweather) .fontSize(30) .fontColor(Color.White) .fontWeight(700)  Text(\" \"+cast.nighttemp+\"°~\"+cast.daytemp+\"°\") .fontSize(30) .fontColor(Color.White) .fontWeight(700) } Row(){  Text(cast.daywind+\"风\") .fontSize(30) .fontColor(Color.White) .fontWeight(700)  Text(cast.daypower+\"级\") .fontSize(30) .fontColor(Color.White) .fontWeight(700) } } } }) // 近期天气数据 Column(){ Text(\'近期天气查询\') .fontSize(26) .margin({top:30,bottom:15}) // 天气列表 Row(){ ForEach(this.casts,(cast:casts)=>{ Column(){  Text(cast.date.substring(5))  this.weartherImage(cast.dayweather)  Text(cast.daytemp.toString())  Line() .width(20) .height(80) .startPoint([10,0]) .endPoint([10,70]) .stroke(Color.Black) .strokeWidth(3) .strokeDashArray([10,3])  this.weartherImage(cast.nightweather)  Text(cast.nighttemp.toString()) } .margin(5) }) } } } .width(\'100%\') .height(\'100%\') }}

forecasts

import {casts} from \'../view/casts\'export class forecasts{ city:string adcode:number casts:Array}

WeatherModel

import {forecasts} from \'./forecasts\'export class WeatherModel{ status:number count:number infocode:number forecasts:Array=[]}