首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【HarmonyOS NEXT星河版开发实战】天气查询APP

【HarmonyOS NEXT星河版开发实战】天气查询APP

作者头像
@VON
发布2025-12-21 09:30:22
发布2025-12-21 09:30:22
1220
举报

https://gitee.com/wang-xin-jie234/harmony-os

前言

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

界面效果展示

首页

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

添加和删除

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

界面构建讲解

1. 获取所需数据

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

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

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

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

2. 在编译器中准备数据

3. index页面代码讲解

3.1 导入模块
代码语言:javascript
复制
import getweatherutil from '../util/getWeather'
import { cityView } from '../view/cityView'
import {WeatherModel} from '../view/WeatherModel'
import router from '@ohos.router'
3.2 定义组件
代码语言:javascript
复制
@Entry
@Component
struct Index {
3.3 定义状态变量:
代码语言:javascript
复制
// 城市代码集合
@State cityCoeList: number[] = [110000, 120000]
// 城市名字集合
@State cityNameList: string[] = []
// 城市信息集合
@State cityWeatherList: Array<WeatherModel> = []
// 当前城市索引
@State cityIndex: number = 0
3.4 定义Tabs控制器:
代码语言:javascript
复制
tabController: TabsController = new TabsController()
3.5 定义按钮样式
代码语言:javascript
复制
@Builder tabBuild(index: number) {
  Circle({ width: 10, height: 10 })
    .fill(this.cityIndex === index ? Color.White : Color.Gray).opacity(0.4)
}
3.6 页面显示时触发的方法
代码语言:javascript
复制
onPageShow() {
  let params = router.getParams()
  if (params !== null) {
    this.cityCoeList = params['codes']
    // 清空所有数据
    this.cityWeatherList = []
    this.cityNameList = []
    this.initDate()
  }
}
3.7 获取数据的方法
代码语言:javascript
复制
aboutToAppear() {
  this.initDate()
}
3.8 初始化数据的方法
代码语言:javascript
复制
async initDate() {
  let result: Array<WeatherModel> = 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
代码语言:javascript
复制
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 导入模块
代码语言:javascript
复制
import router from '@ohos.router'
4.2 定义组件
代码语言:javascript
复制
@Entry
@Component
struct AddCity {
4.3 定义状态变量
代码语言:javascript
复制
// 所有城市的代码列表
@State AllCityCodeList: Array<number> = [
  410100, 410200, 410300, 410400, 410500, 410600, 410700, 410800, 410900, 411000, 411200, 411300, 411400, 411500, 411600, 411700
]
// 所有城市的名称列表
@State AllCityNameList: Array<string> = [
  '郑州市', '开封市', '洛阳市', '平顶山市', '安阳市', '鹤壁市', '新乡市', '焦作市', '濮阳市', '许昌市', '三门峡市', '南阳市', '商丘市', '信阳市', '周口市', '驻马店市'
]
4.4 接受数据的载体
代码语言:javascript
复制
@State cityCodeList: number[] = []
@State cityNameList: string[] = []
4.5 页面显示时触发的方法
代码语言:javascript
复制
onPageShow() {
  let param = router.getParams()
  this.cityCodeList = param['codes']
  this.cityNameList = param['names']
}
4.6 构建UI
代码语言:javascript
复制
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 导入模块
代码语言:javascript
复制
import { WeatherModel } from '../view/WeatherModel'
import http from '@ohos.net.http'
5.2 定义类
代码语言:javascript
复制
class getWeatherUtil {
5.3 定义方法 getWeather
代码语言:javascript
复制
// 发送一个url,返回对应的数据
getWeather(cityCode: number): Promise<WeatherModel> {
  return new Promise<WeatherModel>((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
代码语言:javascript
复制
// 直接发送多个url结果一并返回
async getWeathers(cityCodes: Array<number>): Promise<Array<WeatherModel>> {
  let promises: Array<Promise<WeatherModel>> = []
  let weatherModels: Array<WeatherModel> = []
  for (let i = 0; i < cityCodes.length; i++) {
    promises.push(this.getWeather(cityCodes[i]))
  }

  await Promise.all(promises).then(result => {
    for (const element of result) {
      console.log(element.forecasts[0].city)
    }
    weatherModels = result
  }).catch((err) => {
    console.log(err)
  })
  return weatherModels
}
5.5 实例化并导出类
代码语言:javascript
复制
let getweatherutil = new getWeatherUtil()
export default getweatherutil as getWeatherUtil

getWeather 方法

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

getWeat hers 方法

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

全套源代码展示

AddCity

代码语言:javascript
复制
import router from '@ohos.router'

@Entry
@Component
struct AddCity {

  @State AllCityCodeList:Array<number>=
    [410100,410200,410300,410400,410500,410600,410700,410800,410900,411000,411200,411300,411400,411500,411600,411700]
  @State AllCityNameList:Array<string>=
    ['郑州市','开封市','洛阳市','平顶山市','安阳市','鹤壁市','新乡市','焦作市','濮阳市','许昌市','三门峡市','南阳市','商丘市','信阳市','周口市','驻马店市']

  // 接受数据的载体
  @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

代码语言:javascript
复制
import getweatherutil from '../util/getWeather'
import { cityView } from '../view/cityView'
import {WeatherModel} from '../view/WeatherModel'
import router from '@ohos.router'

@Entry
@Component
struct Index {
  // 城市代码集合
  @State cityCoeList:number[] =[110000,120000]
  // 城市名字集合
  @State cityNameList:string[]=[]
  // 城市信息集合
  @State cityWeatherList:Array<WeatherModel>=[]
  // 当前城市索引
  @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<WeatherModel> =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)
    }
  }

  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})

  }
}

getWeather

代码语言:javascript
复制
import {WeatherModel} from '../view/WeatherModel'
import http from '@ohos.net.http'

class getWeatherUtil{
  // 发送一个url,返回对应的数据
  getWeather(cityCode:number){
    return new Promise<WeatherModel>((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<number>){
    let promises:Array<Promise<WeatherModel>>=[]
    let weatherModels:Array<WeatherModel>=[]
    for (let i = 0; i < cityCodes.length; i++) {
      promises.push(this.getWeather(cityCodes[i]))
    }

    await Promise.all(promises).then(result=>{
      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

代码语言:javascript
复制
export class casts{
  date:string
  dayweather:string
  nightweather:string
  daytemp:number
  nighttemp:number
  daywind:string
  daypower:string
  daytemp_float:number
  nighttemp_float:number
}

cityView

代码语言:javascript
复制
import {casts} from '../view/casts'

@Component
export struct cityView {
  // 获取数据

  // 城市天气数据
  casts:Array<casts>=[]

  @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

代码语言:javascript
复制
import {casts} from '../view/casts'

export class forecasts{
  city:string
  adcode:number
  casts:Array<casts>
}

WeatherModel

代码语言:javascript
复制
import {forecasts} from './forecasts'

export class WeatherModel{
  status:number
  count:number
  infocode:number
  forecasts:Array<forecasts>=[]
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-10-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 界面效果展示
    • 首页
    • 添加和删除
  • 界面构建讲解
    • 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
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档