嚴華 发表于 2025-3-15 06:20:14

【中工开发者】——气候查询,鸿蒙OS

引言

伴随智能手机的大规模普及,气候查询已然成为人们一样平常生存里不可或缺的关键构成部分。而鸿蒙操作体系(HarmonyOS)作为华为精心打造并推出的新一代智能终端操作体系,凭借其独特的技术架构与创新特性,为广大开发者们开启了一扇全新的开发大门,带来前所未有的开发体验。本文将会全方位、细致入微地先容基于鸿蒙 OS 开发一款气候查询应用的详细步骤与方法,其内容将完整覆盖从与数据接口进行对接获取景象信息,到实现都会信息管理中都会删除功能的一整套流畅流程,旨在为开发者们提供全面且极具价值的开发指引与参考范例
1. 数据接口获取

在开发气候查询应用之前,我们需要一个可靠的气候数据接口。这里我们可以选择公开的气候API,如高德地图、和风气候等。以高德地图为例,我们需要注册账号并获取一个API密钥(API Key),用于后续的数据请求。
1.1 获取API Key
访问OpenWeatherMap官网气候查询-基础 API 文档-开发指南-Web服务 API | 高德地图API气候查询-基础 API 文档-开发指南-Web服务 API | 高德地图API气候查询-基础 API 文档-开发指南-Web服务 API | 高德地图APIOpenWeatherMap官网,注册账号并订阅相应的服务,获取API Key。
https://i-blog.csdnimg.cn/direct/12d9f46f3a1f423d88d707790d41109a.png

2. 对象创建

在鸿蒙OS中,我们需要创建几个关键的对象来管理气候数据和用户界面。
2.1 气候数据模型

创建一个Casts类来存储气候数据,包罗白天 温度、夜晚温度、湿度、风向、等数据信息。
export class casts{

date:string=''
dayweather:string=''
nightweather:string=''
daytemp:number=0
nighttemp:number=0
daywind:string=''
daypower:string=''
daytemp_float:number=0
nighttemp_float:number=0

} 2.2 都会管理类

创建一个forecasts类来存储都会名称,代码,气候信息:
export class forecasts{
city:string=''
adcode:number=0
casts:Array<casts>=[]
}
2.3 城市管理类 2.3 都会管理类

创建一个WeatherModel类来存储访问状态信息,都会信息等
export class WeatherModel{
status:number=0
count:number=0
infocode:number=0
forecasts:Array<forecasts> = []
} 3. 获取数据

通过高德地图API获取指定都会的气候数据,并提供了并发请求多个都会气候的功能。代码中界说了一个getWeatherUtil类,其中包罗两个主要方法:
getWeather(cityCode):这个方法继承一个都会代码作为参数,使用高德地图API的URL模板构建请求URL,并发送HTTP GET请求。假如请求乐成(响应码200),它将解析响应结果并使用Promise的resolve函数返回解析后的气候数据对象WeatherModel。
getWeathers(cityCodes):这是一个异步方法,继承一个都会代码数组,使用getWeather方法为每个都会代码创建一个Promise,并使用Promise.all来并发实行这些Promise。当全部请求都完成后,它会遍历结果,打印每个都会的名字,并返回包罗全部气候数据的WeatherModel数组。
import {casts} from "../viewmodel/casts"


@Component
export struct cityView{


@Builder weatherImage(dayweather: string) {
    if (dayweather === "晴") {
      Image($r('app.media.sun')).width(30)
    }
    if (dayweather === "多云") {
      Image($r("app.media.cloud")).width(30)
    }
    if (dayweather === "阴") {
      Image($r("app.media.cloud")).width(30)
    }
    if (dayweather.includes("雨")) {
      Image($r("app.media.rain")).width(30)
    }
}
//天气数据
casts:Array<casts>=[]


build() {
    Column(){
      ForEach(this.casts,(cast:casts)=>{
      if (this.casts===cast){
          //上半部分 图片+当天天气
          //图片
          Row() {
            if (cast.dayweather === "晴") {
            Image($r('app.media.sun')).width(260)
            }
            if (cast.dayweather === "多云") {
            Image($r("app.media.cloud")).width(260)
            }
            if (cast.dayweather === "阴") {
            Image($r("app.media.cloud")).width(260)
            }
            if (cast.dayweather.includes("雨")) {
            Image($r("app.media.rain")).width(260)
            }
          }.height("30%")
          Column(){
            //温度天气
            Row() {
            Text(cast.dayweather).fontSize(30).fontColor(Color.White).fontWeight(FontWeight.Bold)
            Text("" + cast.daytemp + "°~" + cast.nighttemp + "°")
                .fontSize(30)
                .fontColor(Color.White)
                .fontWeight(FontWeight.Bold)
            }
            //风力
            Row() {
            Text(cast.daywind + "风").fontSize(30).fontColor(Color.White).fontWeight(FontWeight.Bold)
            Text(cast.daypower + "级").fontSize(30).fontColor(Color.White).fontWeight(FontWeight.Bold)
            }

          }.margin({ top: 10 })

      }
      })
      //近期天气列表
      Column() {
      Text("查看近期天气").fontSize(26).margin({ top: 30 })
      //天气列表
      Row() {
          ForEach(this.casts, (cast: casts) => {
            Column() {
            Text(cast.date.substring(5))
            this.weatherImage(cast.dayweather)
            Blank()
            Text(cast.daytemp.toString())
            Line()
                .width(20).height(80).startPoint()
                .endPoint().stroke(Color.Black)
                .strokeWidth(3).strokeDashArray()
            Text(cast.nighttemp.toString())
            Blank()
            this.weatherImage(cast.dayweather)
            }.height("90%").width("20%")
          })
      }
      .width("80%")
      .height("60%")
      .backgroundColor("#ffbab8b8")
      .opacity(0.5)
      .justifyContent(FlexAlign.SpaceAround)

      }.height("45%").width("100%")

    }.width("100%").height("100%")





}

}

4. 主页气候数据展示 

4.1 主页结构

该项目主页用于展示一个都会气候信息的界面。界面答应用户在差别都会的气候信息之间切换。组件在即将表现时会异步获取气候数据,并更新界面上表现的都会列表和气候信息。用户可以通过点击添加按钮跳转到添加都会的页面,并通过Tab组件检察各个都会的气候详情。以下是主页的结构代码:
import { cityView } from '../view/cityView'
import getWeatherUtil from "../viewmodel/getWeatherUtil"
import {WeatherModel} from "../viewmodel/WeatherModel"
import { router } from '@kit.ArkUI'

@Entry
@Component
struct Index {
//城市代码集合
@State cityCodeList :number[]=
//城市名字集合
@State cityNameList :string[]=[]
//城市信息集合
@State cityWeatherList :Array<WeatherModel>=[]
//当前tab组件索引
@State cityIndex: number = 0
//tab控制器
tabcontroller: TabsController = new TabsController()

//tarBar 自定义函数
@Builder tabBuild(index: number) {
    Circle({ width: 10, height: 10 }).fill(this.cityIndex === index ? Color.White : Color.Gray).opacity(0.2)
}



aboutToAppear(){
    this.initDate()

}
asyncinitDate(){
    // let result :Array<WeatherModel> = getWeatherUtil.getWeathers(this.cityCodeList)
let result :Array<WeatherModel>=await getWeatherUtil.getWeathers(this.cityCodeList)
   for (let i = 0; i < result.length; i++) {
   let ACityWeather = new WeatherModel()
   ACityWeather = result
   this.cityWeatherList.push(ACityWeather)
   let cityName = result.forecasts.city
   this.cityNameList.push(cityName)
   }
}



build() {
    Column(){
      Row(){
      Button("添加")
      .fontSize(25)
      .fontColor(Color.Gray)
      .opacity(0.7)
      .backgroundColor("#87CEEB")
      .margin({ bottom: 15 })
      .onClick(() => {
          router.pushUrl({
            url: "pages/AddCity",
            params: {
            Codes : this.cityCodeList,
            Names : this.cityNameList
            }
          })
      })



      Text(this.cityNameList).fontSize(40).fontColor(Color.Orange)



      Button("删除")
      .fontSize(25)
      .fontColor(Color.Gray)
      .opacity(0.7)
      .backgroundColor("#87CEEB")
      .margin({ bottom: 15 })



      }.height("10%")
      .width("100%")
      .justifyContent(FlexAlign.SpaceBetween)




      //信息布局

      Tabs({ barPosition: BarPosition.Start, controller: this.tabcontroller }) {
      ForEach(this.cityWeatherList,(cityWeather:WeatherModel)=>{
          TabContent(){
            cityView({casts:cityWeather.forecasts.casts})
          }.tabBar(this.tabBuild(this.cityWeatherList.findIndex(obj=>obj===cityWeather)))
      })
      }.barWidth(20)
       .barHeight(40)
      .onChange((index:number)=>{
      this.cityIndex = index
      })




    }.backgroundColor("#87CEEB")
    .width("100%").height("100%")
}
}
[*] 4.2 气候信息展示
在差别的气候信息的展示中用ForEach循环来遍历气候数据数组casts,并为每条气候数据生成对应的视图。界面中包罗了气候图标、温度表现、风力描述以及日期标记,使用差别的结构和样式来优化信息的展示。
import {casts} from "../viewmodel/casts"


@Component
export struct cityView{


@Builder weatherImage(dayweather: string) {
    if (dayweather === "晴") {
      Image($r('app.media.sun')).width(30)
    }
    if (dayweather === "多云") {
      Image($r("app.media.cloud")).width(30)
    }
    if (dayweather === "阴") {
      Image($r("app.media.cloud")).width(30)
    }
    if (dayweather.includes("雨")) {
      Image($r("app.media.rain")).width(30)
    }
}
//天气数据
casts:Array<casts>=[]


build() {
    Column(){
      ForEach(this.casts,(cast:casts)=>{
      if (this.casts===cast){
          //上半部分 图片+当天天气
          //图片
          Row() {
            if (cast.dayweather === "晴") {
            Image($r('app.media.sun')).width(260)
            }
            if (cast.dayweather === "多云") {
            Image($r("app.media.cloud")).width(260)
            }
            if (cast.dayweather === "阴") {
            Image($r("app.media.cloud")).width(260)
            }
            if (cast.dayweather.includes("雨")) {
            Image($r("app.media.rain")).width(260)
            }
          }.height("30%")
          Column(){
            //温度天气
            Row() {
            Text(cast.dayweather).fontSize(30).fontColor(Color.White).fontWeight(FontWeight.Bold)
            Text("" + cast.daytemp + "°~" + cast.nighttemp + "°")
                .fontSize(30)
                .fontColor(Color.White)
                .fontWeight(FontWeight.Bold)
            }
            //风力
            Row() {
            Text(cast.daywind + "风").fontSize(30).fontColor(Color.White).fontWeight(FontWeight.Bold)
            Text(cast.daypower + "级").fontSize(30).fontColor(Color.White).fontWeight(FontWeight.Bold)
            }

          }.margin({ top: 10 })

      }
      })
      //近期天气列表
      Column() {
      Text("查看近期天气").fontSize(26).margin({ top: 30 })
      //天气列表
      Row() {
          ForEach(this.casts, (cast: casts) => {
            Column() {
            Text(cast.date.substring(5))
            this.weatherImage(cast.dayweather)
            Blank()
            Text(cast.daytemp.toString())
            Line()
                .width(20).height(80).startPoint()
                .endPoint().stroke(Color.Black)
                .strokeWidth(3).strokeDashArray()
            Text(cast.nighttemp.toString())
            Blank()
            this.weatherImage(cast.dayweather)
            }.height("90%").width("20%")
          })
      }
      .width("80%")
      .height("60%")
      .backgroundColor("#ffbab8b8")
      .opacity(0.5)
      .justifyContent(FlexAlign.SpaceAround)

      }.height("45%").width("100%")

    }.width("100%").height("100%")





}

}


5. 都会添加与删除

提供用户界面让用户可以或许添加新的都会到都会列表,同样,提供用户界面让用户可以或许从都会列表中删除都会。
import router from '@ohos.router'

interface GeneratedTypeLiteralInterface_1 {
Codes: number[];
}

interface GeneratedTypeLiteralInterface_2 {
Names: string[];
}

@Entry
@Component
struct AddCity {

@State AllCityCodeList :Array<number> =
@State AllCityNameList :Array<string> = ["北京市","天津市","河北省","山西省","辽宁省","吉林省","上海市"]

//当前城市代码列表 接收传入数据的载体
@State cityCodeList :Array<number> = []
@State cityNameList :Array<string> = []


onPageShow(){
    let params = router.getParams()
    //(user as { name: string }).name
    this.cityCodeList = (params as GeneratedTypeLiteralInterface_1).Codes
    this.cityNameList = (params as GeneratedTypeLiteralInterface_2).Names
}



build() {
    //加入
    Column(){
      Row(){
      Text("添加城市列表").fontSize(35).fontColor(Color.White)
      Blank()
      Button("完成").backgroundColor("").fontSize(26)
          .onClick(()=>{
            router.back({
            url:"pages/Index",
            params:{
                Codes:this.cityCodeList
            }
            })
          })
      }.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({ right: 5 })
                      .opacity(0.8)
                  }.width("100%")

                  Blank()
                  Divider().strokeWidth(5)
                }.height(90).width("100%").margin({ top: 20 })
                .backgroundColor("#4682B4")
            } else {
                Column() {
                  Row() {
                  Text(name).fontSize(35).fontColor(Color.White).width("60%")
                      .margin({ top: 20, left: 30 })
                  Blank()
                  Button("添加").backgroundColor("").fontSize(18)
                      .margin({ right: 5 })
                      .onClick(() => {
                        //根据name 获取所在索引
                        let index = this.AllCityNameList.findIndex(obj => obj === name)
                        console.log("index:"+index)
                        //根据索引获得 城市对应的编码
                        let cityCode: number = this.AllCityCodeList
                        console.log("cityCode= "+cityCode)
                        //将编码加入列表
                        this.cityCodeList.push(cityCode)
                        this.cityNameList.push(name)
                        console.log(this.cityCodeList.toString())
                      })
                  }.width("100%")

                  Blank()
                  Divider().strokeWidth(5)
                }.height(90).width("100%").margin({ top: 20 })
                .backgroundColor("#87CEEB")
            }


            }
          })
      }
      }.width("100%").height("90%")
    }.width("100%").height("100%").backgroundColor("#87CEFA")
}
} 6.功能展示


 https://i-blog.csdnimg.cn/direct/5b6b0bfccbe440b5b761b42763196be3.png
https://i-blog.csdnimg.cn/direct/1569e12724ac475cb6027314f20e839e.png
7.项目不足与经验总结
 

7.1项目计划不足

错误处理机制不统一:差别的请求有差别的错误处理方式,缺少统一的管理。
数据缓存策略不美满:缓存过期时间与革新机制不敷灵活,可能导致数据滞后。
网络请求与组件耦合:网络请求和 UI 组件耦合过深,应该解耦。
过度依赖外部 API:对第三方 API 依赖过高,缺少容错机制和备用方案。
UI 计划与用户体验欠缺:UI 交互和细节优化不足,加载提示和数据展示有待改进。
气候数据展示条理不清晰:展示的气候数据缺乏优先级和条理感。
缺少单元测试与主动化测试:缺少美满的单元测试和主动化测试,难以及时发现问题。
7.2经验总结

模块化计划:功能模块化进步代码可维护性和可扩展性。
公道使用数据缓存:优化缓存机制,减少网络请求,进步用户体验。
UI 交互优化:通过动态效果和反馈提拔用户体验。
提前规划错误处理:统一的错误处理机制提拔代码可靠性。
第三方 API 备选方案:避免过度依赖单一 API,增长容错机制。
清晰的数据结构界说:明确数据格式和结构,包管一致性。
可扩展性:计划时考虑将来功能扩展,保持灵活性。
参考与主要借鉴网址:GitHub - YeMengLiChou/LuckWeather-HarmonyApp: 鸿蒙课设开发——好气候App

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 【中工开发者】——气候查询,鸿蒙OS