鸿蒙-从 0 到 1 开发一个天气 APP

项目名称

浅草天气是一款个人独立开发,设计的 鸿蒙 APP,开发过程中主要参考了 HUAWEI 天气,使用的开发工具有 DevEco studio 和 Figma,在开发的过程中经历过多次改版优化,最终实现了如下的 Feature

  • 定位
  • 当前天气
  • 天气指数
  • 每小时天气
  • 多日天气预报

运行条件

列出运行该项目所必须的条件和相关依赖

  • API 11
  • deploy retrofit

技术使用

  • 使用装饰器来简化权限请求
  • 网络请求
  • list 列表渲染,grid 列表渲染
  • state,provide-consume 使用
  • 组件的提取
  • 自定义view 绘制
  • 贝塞尔曲线
  • 迁移 ts-retrofit 到 Harmony OS

技术讲解

定位权限管理

考虑到天气对地理位置要求不高,本 App 默认是不需要请求定位权限的,目前的技术方案是从高德地图的 Web Api 里面通过IP来获取地址。当然如果需要精确的位置可以先获取 GPS 权限,然后进行请求。 详见本人前段时间的文章

网络请求

目前使用鸿蒙自带的 http + Promise 来实现的,通过 gpt 去生成对应的 class,使用 JSON 来解析对象,具体实现可以参考以下代码

getHourWeather(): Promise<DayForecast> {
  return new Promise(async (resolve: Function, reject: Function) => {
    let url = `${WeatherConstants.SERVER}/${WeatherConstants.HOUR_FORECAST}`;
    if (this.address.infocode == null) {
      this.address = await this.getCityFromIp();
    }
    let location = this.address
    const result = calculateAverageCoordinates(location.rectangle);

    url += '?location=' + result.averageLongitude.toFixed(2) + "," + result.averageLatitude.toFixed(2) + '&key=' + WeatherConstants.KEY;
    let httpRequest = http.createHttp();
    httpRequest.request(url, (err, data) => {
      if (!err) {
        let day : DayForecast = JSON.parse(data.result as string)
        resolve(day)
      }
      else {
        reject(err)
        console.log("getHourWeather:", err.message);
      }
    });
  });
}

天气的 Api 是来自 和风天气 ,可以去上面申请免费的 Api 来使用,免费的额度有限制,也可以花个一杯奶茶钱用付费版的,次数管饱。

页面布局

图片

首先展示的是当前的天气,看天气自然最关系当天的天气咯,其次展示当天的天气指数,天气指数里面我选取了几个比较重要的指数,当然大家也可以选择自己感兴趣的指数。后面展示的是逐小时天气预报,这个是用一个横向的 list 实现的。最后就是一个最重要的多日预报,是一个自定义绘制的 View,也是本篇要重点讲的内容。

多日预报实现

如何绘制这样一个多日天气布局呢,首先我们了解一下 鸿蒙绘制的 Api ,在这里我们需要使用三个 Api。

  1. fillText(text: string, x: number, y: number, maxWidth?: number): void; 用来在指定位置绘制文字
  2. drawImage(image: ImageBitmap | PixelMap, dx: number, dy: number, dw: number, dh: number): void; 在指定位置绘制图像
  3. bezierCurveTo(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number): void; 绘三阶制贝塞尔曲线
fillText

参数比较简单,相信大家一眼就懂了,但是这里面有一点比较重要,就是我们在绘制文字包括图片的时候要跟当前天气坐标对齐,也就是我们习惯上看起来的上下居中对齐,为了达到这一点我们一般需要测量一下文字的宽度,然后沿着 X 轴平移一半的宽度。下面是示例代码

const w = this.context.measureText(temp).width
this.context.fillText(temp, lowPs[i][0] - (w >> 1), lowPs[i][1] + 20, 40)
drawImage

这个 Api 对我来说有点奇怪,因为我没找到可以对图片进行 tint 的参数,感觉还是挺麻烦的,项目里面图标颜色都是我在 Figma 上面自己改的,如果有好的方案希望有知道的同学告知一下。

bezierCurveTo

绘制三阶贝塞尔曲线,里面的参数我需要介绍一下,首先呢 cp 就是 control point 翻译过来就是控制点,主要是用来决定曲线走向的,最后一个点是 end point,一般是我们实际用到的数据。在很多图像曲线展示中为了使得展示效果比较自然平滑,我们都会采用贝塞尔曲线绘制。

在绘制过程中我们会使用一些算法来生成贝塞尔曲线上面的点,这样我们就可以按照 Api 进行绘制曲线。在这里面随便多嘴一句,写代码是一项具有挑战的工作,但是我们不能等所有的工作都准备好采取行动,很多时候我们需要边学边用。

好了,回归正题,项目里面的贝塞尔曲线的点就是用 github 上搜出来的算法计算出来的,亲测还挺好用。需要注意的是计算出来的点是不包括初始点的,很好理解第一个点不是任何点的 end point,而且他是一个始发点,所以在实现过程中我们需要 moveTo 第一点,至于剩下的坐标我们只需要调用bezierCurveTo 即可。下面是实现的示例代码

const x = highPs[0][0];
const y = highPs[0][1];

this.context.moveTo(x, y)
let points = curveToBezier(highPs)

for (let i = 1; i < points.length - 2; i += 3) {
  const controlPoint1 = points[i];
  const controlPoint2 = points[i + 1];
  const endPoint = points[i + 2];
  this.context.bezierCurveTo(
    controlPoint1[0], controlPoint1[1],
    controlPoint2[0], controlPoint2[1],
    endPoint[0], endPoint[1]
  );
}
如何计算贝塞尔曲线的坐标点?

一般来说我们绘制的时候一个 view 的高度是固定的,上面绘制什么,下面绘制什么都已经确定了。那么中间的坐标位置就是 marginTop+otherViewsHeight(minY) 到 marginTop+otherViewsHeight+curveHeight(maxY) 至于 x 轴的位置,则没有影响跟每日坐标保持一致即可,至于每日的坐标,大家可以按照自己的要求或者其他考虑,有一个合理的 x 轴的 interval 即可。

那么我们知道 y 轴上的 max 以及 min 之后,如何确定每个点在 y 轴上距离呢,比如有 10 个点 [10,11,13,20,30,32,23,32,23,20], 这些点要能全部展示在 view 里面不会跑出来,或者显示的不会留下很多空白,那么我们首先计算 max 和 min ,用这两个点标定最低位和最高位,这样的话温度曲线展示的就会比较自然。下面是示意代码:

for (let index = 0; index < highs.length; index++) {
  const element = highs[index];
  let y = this.calY(max - parseFloat(element), unit, remainSpace) + curTop // margin top
  let x = index * perGap + curLeft // margin left
  highPs.push([x, y])
}

结尾

由于项目里面用到的是免费的 Api 可能不稳定,如果你遇到请求错误的情况可以自己花个十块钱买一个付费 key ,由于本人精力,技术有限代码中难免存在一些问题,如果有问题可以留言或者提 pr。最后附上代码传送门 周末愉快。

鸿蒙-从 0 到 1 开发一个 APP

作者:CaptainZ
链接:https://juejin.cn/post/7401060368087777318
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值