【华为HarmonyOS开发】使用和风天气api开发天气预报项目之主页面设计

前言:本笔记记录于学习HarmonyOS移动应用开发一书中天气预报项目的制作,项目内容因软件更新导致的兼容性问题进行相应的修改,并在修改试错中尝试了与书中不同的方法并在此期间获得收获,故记录此笔记。

目录

书中内容兼容性调整

和风天气数据的获取

关于和风天气api:

先分后合——制作主页面各部分的天气小组件

利用LIst组件实现滑动显示未来天气

天气图标与实时天气的匹配

获取实时时间

组件整体构建

Gauge组件画图

使用Canvas组件来绘制多日天气气温曲线

Scroll容器组件实现小组件的拼接展示


书中内容兼容性调整

书中代码是在FA模型下编写的,而现在模型为Stage,需要做修改:

  • config.json配置文件,stage模型中拆分为了app.json和module.json

  • FA模型中deviceConfig配置字段近似于Stage模型的app.json文件

  • Stage模型无需network申请允许网络明文访问,本身就是允许的

和风天气数据的获取

HarmonyOS中实现天气服务功能使用了HTTP数据请求的方式调用天气预报的API。所以可以选择Web API类型的Key,并在鸿蒙应用中使用HTTP客户端库来发起请求

调取数据方法代码:

在调取数据之前需要把存放数据的类写好(相当于准备一个分好格子的容器,将获取到的数据依次存放进去)。

例如

建议把每个小部分分开写,最后再整合到一个大类里,一次调取多项数据需要定义为数组类型

分类好就是这个样子

数据获取方法

//获取实时天气
  getTimelyWeather(locationId: string)
  {
    return new Promise<TimelyWeather>((resolve, reject) => {

      let request = http.createHttp()

      request.request(`https://devapi.qweather.com/v7/weather/now?location=${locationId}&key=16d8d9c11a9848d1be949b4f440c3611&gzip=n`,
        {
          method: http.RequestMethod.GET
        }
      )
        .then(res => {
          if (res.responseCode === 200) {
            console.log('testTag1', res.result)
            resolve(JSON.parse(res.result.toString()))
          } else {
            console.log('查询失败1,', JSON.stringify(res.result))
            reject('失败1')
          }
        })
        .catch((err: Error) => {
          console.log('查询失败2,', JSON.stringify(err))
          reject('失败2')
        })
    })
  }
  
   //获取单个城市实时天气(打包为TimelyWeather类型)
  async getTimelyWeather_1(locationId: string): Promise<TimelyWeather>{

    const weatherModel = await this.getTimelyWeather(locationId)
    console.log(weatherModel.code)
    return weatherModel

  }

其他数据的获取大体类似,只需要修改网址或者简单修改部分内容即可即可,这里不做过多展示。

学会看日志!!

日志能快速帮助锁定问题的所在,如上述方法中console语句,在日志界面中检索关键字段(如testtag),即可查看接收到数据的情况

这边日志显示成功返回相应数据

关于和风天气api:

WebAPI传回数据均为经过Gzip压缩后的压缩文件,无法使用

解决办法:在网址后加 &gzip=n,然后正常调用即可

DevEco编译器中,在预览器中预览无法显示数据,而且日志显示也是传回压缩文件,但是在虚拟机中进行运行则能成功显示数据,而且日志也显示是传回JSON格式的数据。虽然不清楚是什么原因但总归是能正常使用了(虽然一直启动虚拟机有点麻烦就是了)

先分后合——制作主页面各部分的天气小组件

主页面整体设计思路是将页面划分为多个组件,后期也方便整理调整组件位置。

其中AllComponent.ets为所有组件的整合。

利用LIst组件实现滑动显示未来天气

(这个其实可以滑动显示未来多小时的天气,可参考华为内置天气)

天气图标与实时天气的匹配

因为该项目要求实时调用数据,一个一个往里面塞天气图标是不现实的,所以这里使用了函数进行筛选检索。

代码如下:

 @Builder
  weatherImage(weather:string){

    if (weather === '晴') {
      Image($r("app.media.tianqi_qing"))
        .width(30)
    }

    if (weather === '阴') {
      Image($r("app.media.tianqi_yintian"))
        .width(30)
    }

    if (weather === '多云') {
      Image($r("app.media.tianqi_duoyun"))
        .width(30)
    }

    if (weather.includes("雨")) {
      Image($r("app.media.tianqi_zhongyu"))
        .width(30)


    }
  }
  //根据接受数据的关键信息就可以调取库中的天气图标了
获取实时时间

实时天气的数据中包含了时间,但是格式不相符。为了转化为上图的日期格式,我编写了一个函数。

formatTime(date: Date): string {
    // 获取小时数(24小时制)
    let hours = date.getHours();

    // 转换为12小时制
    let period = hours >= 12 ? "下午" : "上午";
    hours = hours % 12;
    // 如果小时数为0,表示中午12点
    hours = hours ? hours : 12;

    // 格式化分钟数,始终保留两位数字(例如:"05")
    let minutes = date.getMinutes().toString().padStart(2, '0');

    // 返回格式化后的时间字符串
    return `${period}${hours}:${minutes}`;
  }

  timeChange(time:string):string{
    // 假设的时间字符串
    let timeString: string = time

    // 使用Date对象解析时间字符串
    let date = new Date(timeString);

    // 创建一个格式化日期时间的函数


    // 调用函数并打印结果
    let formattedTime:string = this.formatTime(date);
    return formattedTime  // 输出: 下午10点00分
  }

这样就能把奇奇怪怪的 UTC时间转化为上/下午XX:XX了

组件整体构建

我使用的方法是组件显示的时候开始调取数据,然后将获取数据整合,再在组件中应用。

先上代码吧

import {HourlyWeather} from '../bean/HourlyWeather/HourlyWeather'
import getweatherUtil from '../Util/getWeatherUtil'
import {hourly} from '../bean/HourlyWeather/hourly'
import { EditableTitleBar } from '@ohos.arkui.advanced.EditableTitleBar'

@Component
export struct HourlyWeatherComponent {

  private locationId : string = '101010100'    //这里预设了一个城市id,后续可以在外部修改

  @State hourlyWeather: HourlyWeather = new HourlyWeather()

  @State hourly_weather: Array<hourly> = []


  aboutToAppear(): void
  {
    this.initDate()
  }
  //初始化方法
  async  initDate(){

    let result1: HourlyWeather = await getweatherUtil.getHourlyWeather_1(this.locationId)
    this.hourlyWeather = result1
    this.hourly_weather = result1.hourly

  }

//天气图标函数
  @Builder
  weatherImage(weather:string,size:number){

    if (weather === '晴') {
      Image($r("app.media.tianqi_qing"))
        .width(size)
    }

    if (weather === '阴') {
      Image($r("app.media.tianqi_yintian"))
        .width(size)
    }

    if (weather === '多云') {
      Image($r("app.media.tianqi_duoyun"))
        .width(size)
    }

    if (weather.includes("雨")) {
      Image($r("app.media.tianqi_zhongyu"))
        .width(size)


    }
  }

//日期转化函数
  formatTime(date: Date): string {
    // 获取小时数(24小时制)
    let hours = date.getHours();

    // 转换为12小时制
    let period = hours >= 12 ? "下午" : "上午";
    hours = hours % 12;
    // 如果小时数为0,表示中午12点
    hours = hours ? hours : 12;

    // 格式化分钟数,始终保留两位数字(例如:"05")
    let minutes = date.getMinutes().toString().padStart(2, '0');

    // 返回格式化后的时间字符串
    return `${period}${hours}:${minutes}`;
  }

  timeChange(time:string):string{
    // 假设的时间字符串
    let timeString: string = time

    // 使用Date对象解析时间字符串
    let date = new Date(timeString);

    // 创建一个格式化日期时间的函数


    // 调用函数并打印结果
    let formattedTime:string = this.formatTime(date);
    return formattedTime  // 输出: 下午10点00分
  }

  build() {
  Column(){
    List({space:3}){   //使用foreach遍历数据数组
      ForEach(this.hourly_weather,(hour:hourly)=>{
        ListItem(){
          Column({space:10}){


            Text(this.timeChange(hour.fxTime))
              .fontSize(13)
              .fontColor(Color.White)
              .fontWeight(FontWeight.Normal)

            this.weatherImage(hour.text,30)

            Text(hour.temp+"°C")
              .fontColor(Color.White)

          }
          .justifyContent(FlexAlign.Center)
          .alignItems(HorizontalAlign.Center)
          .padding({top:15,bottom:15,left:15,right:15})
        }

      })
    }
    .listDirection(Axis.Horizontal)
    .width("92%")


  }.width("95%")
  .height("15%")
  .backgroundColor('rgba(255, 255, 255, 0.1)')
  .borderRadius(20)
  .margin({top:10})
  }
}

然后个人喜好对间隔大小透明度进行微调就可以了

透明组件

.opacity()只能整体调节组件的透明度,无法调整单个部分的透明度!!

想要只调整背景颜色,可以在.background()颜色设置里选择rgba格式颜色设置,即.background(rgba(255,255,255,0.1)),最后一位数就是透明度,越低越透明

效果如图所示(还是这张图)

(其实这个组件是半透明背景)

Gauge组件画图

用这个来画空气质量aqi环形视图

直接上代码吧,其中Gauge组件的相关配置使用详情请见华为官方文档。

import {TimelyAir} from  '../bean/TimelyAir/TimelyAir'
import getweatherUtil from '../Util/getWeatherUtil'

@Component
export struct AqiCircleComponent {

  private locationId: string = '101010100'

  @State timelyAir: TimelyAir = new TimelyAir()

  @State aqi: number = Number(this.timelyAir.now.aqi)

  aboutToAppear(): void {
    this.initDate()
  }

  //初始化方法
  async initDate() {

    let result1: TimelyAir = await getweatherUtil.getTimelyAir_1(this.locationId)
    this.timelyAir = result1
    this.aqi = Number(this.timelyAir.now.aqi)
    console.log("aqi_test"+ this.aqi);

  }

  build() {

    Column(){
      Stack({alignContent:Alignment.Center}){
        Gauge({value:this.aqi,min:0,max:500})
          .startAngle(210)
          .endAngle(150)
          .colors([[0X00FF00, 0.1],[0xFFFF00,0.1],[0xFF6100,0.1],[0xFF0000,0.1],[0xA020F0,0.2],[0x8B0000,0.4]])
          .strokeWidth(5)
          .description(null)
          .trackShadow(null)

        Column(){
          Text(this.timelyAir.now.category)
            .fontSize(35)
            .fontColor(Color.White)
            .fontWeight(FontWeight.Medium)


          Text(this.timelyAir.now.aqi)
            .fontSize(20)
            .fontColor(Color.White)
        }
      }
    }

  }
}

这段代码仅呈现为图片左边圆形组件,其余部分在另一组件内完成。

使用Canvas组件来绘制多日天气气温曲线

之前发过一次,这里就不放了(见上一篇)

Scroll容器组件实现小组件的拼接展示

以以上组件为参考,制作其他组件

这个就简单用Text组件排版就行

List组件,每小时天气预报

Canvas画图实现未来每日天气曲线

 

Gauge组件实现,剩余Text排版,实现空气质量实时监测

List组件实现各项指数

制作完全部组件后使用Scroll组件进行整合,一个简易的主页面就做好了。

Scroll 作为可滚动的容器类组件,它最多包含一个子组件,当子组件的布局尺寸在指定的滚动方向上超过 Scroll 的视图窗口时,子组件可以滚动, Scroll 滚动方向只支持水平滚动和竖直滚动。 Scroller 作为滚动组件的控制器,它可以控制滚动组件的一些行为,比如滚动到特定位置,滚动到边界等。

注意!Scroll下的子组件不能设置宽度和高度,不然滑动不了

在该项目中使用Scroll组件实现各天气小组件的整合,在主页面及未来可能需要的页面中实现可滑动的天气信息查看。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值