基于 OpenHarmony 开发的玻璃拟态天气应用(3)构建天气组件并实现毛玻璃效果
项目介绍
项目描述
本项目使用了 API9 作为开发版本,该版本是一个较新的版本,在这个项目中我会展示该项目的主要功能构成,主要是展示玻璃拟态的具体开发代码和开发思路,本项目偏向前端开发,后端使用的是和风天气的免费天气 API.
开源地址
整个项目也会同步开源到 Gitee 和 GItHub 仓库中
Gitee:GlassWerther (gitee.com)
GItHub:GlassWeather (github.com)
项目展示
项目设计思路
本项目的设计风格主要学习了 IOS 自带的天气软件,IOS 自带天气的风格就是这种玻璃拟态的风格,个人认为这种风格还是非常好看的,所以本项目大部分代码都是为了实现与其相近的效果
项目主要组件
当前天气组件 NowTemp
该组件显示的内容有以下几部分:当前城市,当前天气,天气描述,今日最高温和最低温
今日空气质量组件 AirQuality
该组件展示了今日的空气质量,主要以展示 AQI 的方式来展示空气质量
卡片组件
该组件包含了数个卡片,卡片中的信息是今日天气指数的一些相关信息,如钓鱼指数等,目的是直观的展示今日天气适合做什么.
搭建项目
构建空气质量组件
创建空气质量组件的文件
在目录E:\Works\DevoEcoProjects\GlassWeather\entry\src\main\ets\weatherComponent
下创建新的 ArkTS File,命名为 AirQuality,如图:
按照之前写当前天气组件的方式来构建这个组件
先添加修饰符@Component
并在下面创建对应的结构体组件,代码如下
@Component
export struct AirQuality {
build() {
Column() {
}
}
}
复制代码
添加修饰符@Preview
即可在预览窗口中查看,但不建议这样做,还是建议直接预览主页面而不是预览自定义组件
为主页添加 Scroll 容器
将该组件导入主页面中即可预览
因为在当前天气的下方是一个可以滑动的模块,所以我们需要添加一个 Scroll 容器来放之后的组件,关于这个容器的具体用法,可以参考官方的文档
首先,我们需要创建一个滚动控制器,在主界面添加一个变量
scroller: Scroller = new Scroller(); // 滚动器
复制代码
然后再添加 Scroll 容器才能生效,并修改样式
主页面代码修改为:
import { AirQuality } from '../weatherComponent/AirQuality'
import { CurrentWeather } from '../weatherComponent/CurrentWeather'
@Entry
@Component
struct Index {
@State message: string = 'Hello World'
scroller: Scroller = new Scroller(); // 滚动器
build() {
Column() {
CurrentWeather()
Scroll(this.scroller) {
AirQuality()
}
.padding({
left: 20,
right: 20,
bottom: 300
})
.scrollable(ScrollDirection.Vertical) // 滚动方向纵向
.scrollBar(BarState.Off) // 滚动条常驻显示
.edgeEffect(EdgeEffect.Spring) // 滚动到边沿后回弹
Text('卡片组件')
.fontSize(50)
.fontWeight(FontWeight.Bold)
}
.width('100%')
.justifyContent(FlexAlign.Start)
}
}
复制代码
添加了之后主页面并没有改变,因为我们现在还没有在自定义组件中写东西,接下来我们继续写自定义组件
添加颜色
在这之前,先添加一下之后会用到的颜色到color.json
中
修改后的代码如下
{
"color": [
{
"name": "start_window_background",
"value": "#FFFFFF"
},
{
"name": "baseFontColor",
"value": "#000000"
},
{
"name": "weatherCardTitle",
"value": "#60FFFFFF"
},
{
"name": "weatherCardBack",
"value": "#205392c8"
},
{
"name": "weatherCardShadow",
"value": "#30010b30"
}
]
}
复制代码
构建空气质量组件(重点)
按照图中所示添加对应的文字,并修改格式
代码修改后如下:
@Component
export struct AirQuality {
@State aqi: string = 'aqi'
@State category: string = 'category'
aboutToAppear() {
}
build() {
Column() {
Text('空气质量').fontColor($r('app.color.weatherCardTitle')).fontSize(11)
.margin({
bottom: 10
})
Column() {
}
.border({ width: { left: 0, right: 0, top: 0, bottom: 0.4 }, color: $r('app.color.weatherCardTitle'), })
.width('100%')
Text(this.aqi + ' - ' + this.category)
.fontColor($r('app.color.baseFontColor'))
.fontSize(20)
.fontWeight(700)
.margin({
top: 10,
})
Text('当前AQI(CN)为' + this.aqi)
.fontColor($r('app.color.baseFontColor')).fontSize(16).fontWeight(300)
.margin({
top: 10,
bottom: 5
})
}
.border({ width: 0.4, color: $r('app.color.weatherCardTitle'), })
.backgroundColor($r('app.color.weatherCardBack'))
.alignItems(HorizontalAlign.Start)
.width('100%')
.padding(15)
.borderRadius(10)
.margin({ top: 20 })
.shadow({
radius:20,
color:$r('app.color.weatherCardShadow')
})
.backdropBlur(70)
.animation({
duration: 120,
curve: Curve.EaseInOut,
})
}
}
复制代码
这个地方可以重点解析一下前端的代码,这一部分就是实现毛玻璃效果的关键
分隔线
首先,组件中间有一个分隔线,我是用的是以下代码来实现的
Column() {
}
.border({ width: { left: 0, right: 0, top: 0, bottom: 0.4 }, color: $r('app.color.weatherCardTitle'), })
.width('100%')
复制代码
这部分代码就是一个 column 容器,但是里面不放任何东西,但是在样式上给一个底部的边框,这样的话就可以实现基本的分隔线效果了
分隔线主要是起到视觉上的平衡效果,所以对应的颜色不用太深,可以使用带有透明度的颜色,比如这里app.color.weatherCardTitle
对应的的#60FFFFFF
这样效果既不会太喧宾夺主又非常好看
毛玻璃效果
.border({ width: 0.4, color: $r('app.color.weatherCardTitle'), })
.backgroundColor($r('app.color.weatherCardBack'))
.borderRadius(10)
.shadow({
radius:20,
color:$r('app.color.weatherCardShadow')
})
.backdropBlur(70)
复制代码
毛玻璃效果的关键就是以上的样式
最关键的是.backdropBlur(70)
这一行,这行代码控制着背景的模糊量,数值越高,模糊的程度越大
背景颜色需要使用半透明的颜色,不然是没有效果的,比如这里的app.color.weatherCardBack
就对应的是#205392c8
这个颜色
shadow
这个属性我认为也是必不可少的一部分,添加淡淡的阴影可以让整个组件显得更加突出
border
这个属性为整个组件添加了一圈淡淡的描边,这里我使用的颜色是和分隔线一样的半透明的颜色,这样可以让描边更好地融入整个组件之中
目前还没有添加网络请求,但是大概的样子已经可以看出来了
效果:
添加背景
因为没有背景图片,所以毛玻璃的效果不是很好,我们照一张图片来充当背景图,比如下面这张图
将这张图放在E:\Works\DevoEcoProjects\GlassWeather\entry\src\main\resources\base\media
目录下,
通过
.backgroundImage($r('app.media.img_1'), ImageRepeat.NoRepeat)
.backgroundImageSize(ImageSize.Cover)
复制代码
就可以修改背景了
修改之后主界面的完整代码如下:
import { AirQuality } from '../weatherComponent/AirQuality'
import { CurrentWeather } from '../weatherComponent/CurrentWeather'
@Entry
@Component
struct Index {
@State message: string = 'Hello World'
scroller: Scroller = new Scroller(); // 滚动器
build() {
Column() {
CurrentWeather()
Scroll(this.scroller) {
AirQuality()
}
.padding({
left: 20,
right: 20,
bottom: 300
})
.scrollable(ScrollDirection.Vertical) // 滚动方向纵向
.scrollBar(BarState.Off) // 滚动条常驻显示
.edgeEffect(EdgeEffect.Spring) // 滚动到边沿后回弹
Text('卡片组件')
.fontSize(50)
.fontWeight(FontWeight.Bold)
}
.height('100%')
.width('100%')
.justifyContent(FlexAlign.Start)
.backgroundImage($r('app.media.img_1'), ImageRepeat.NoRepeat)
.backgroundImageSize(ImageSize.Cover)
}
}
复制代码
再修改一下字体的颜色
修改后的 color 文件如下
{
"color": [
{
"name": "start_window_background",
"value": "#FFFFFF"
},
{
"name": "baseFontColor",
"value": "#FFFFFF"
},
{
"name": "weatherCardTitle",
"value": "#60FFFFFF"
},
{
"name": "weatherCardBack",
"value": "#205392c8"
},
{
"name": "weatherCardShadow",
"value": "#30010b30"
}
]
}
复制代码
效果如下:
添加空气质量请求
构建对应的模型
在E:\Works\DevoEcoProjects\GlassWeather\entry\src\main\ets\model\WeatherModel.ets
中添加对应的模型,以便将获得的 json 文本转换为对象
export class AirQualityData{
aqi:string
category:string
}
复制代码
完整代码如下:
export class CurrentWeatherModel {
obsTime?: string // 数据观测时间
temp?: string // 温度,默认单位:摄氏度
feelsLike?: string // 体感温度,默认单位:摄氏度
icon?: string // 天气状况和图标的代码,图标可通过天气状况和图标下载
text?: string // 天气状况的文字描述,包括阴晴雨雪等天气状态的描述
wind360?: string // 风向360角度
windDir?: string // 风向
windScale?: string // 风力等级
temperature?: string // 温度
direct?: string // 风向
windSpeed?: string // 风速,公里/小时
humidity?: string // 相对湿度,百分比数值
precip?: string // 当前小时累计降水量,默认单位:毫米
pressure?: string //大气压强,默认单位:百帕
vis?: string // 能见度,默认单位:公里
cloud?: string // 云量,百分比数值。可能为空
dew?: string // 露点温度。可能为空
}
export class AirQualityData{
aqi:string
category:string
}
复制代码
构建 axios 请求
构建 axios 请求
GetAirQuality() {
// 构建天气URL
let weatherUrl = 'https://devapi.qweather.com/v7/air/now?location=116,40&key=9243123252294a3e93ae6ccab5ec5da7'
// 发送GET请求获取天气数据
axios.get(weatherUrl)
.then(res => {
// 将返回的天气数据转换为字符串
let data = JSON.stringify(res.data.now);
// 将字符串转换为JSON对象并赋值给airWeather变量
this.airQualityData = JSON.parse(data.toString());
this.aqi = this.airQualityData.aqi
this.category = this.airQualityData.category
});
}
复制代码
将构建好的函数放入aboutToAppear()
中,这样组件加载时就会调用这个函数来获取信息
完整的代码如下:
import axios from '@ohos/axios'
import { AirQualityData } from '../model/WeatherModel'
@Component
export struct AirQuality {
@State aqi: string = 'aqi'
@State category: string = 'category'
@State airQualityData: AirQualityData = new AirQualityData()
aboutToAppear() {
this.GetAirQuality()
}
GetAirQuality() {
// 构建天气URL
let weatherUrl = 'https://devapi.qweather.com/v7/air/now?location=116,40&key=9243123252294a3e93ae6ccab5ec5da7'
// 发送GET请求获取天气数据
axios.get(weatherUrl)
.then(res => {
// 将返回的天气数据转换为字符串
let data = JSON.stringify(res.data.now);
// 将字符串转换为JSON对象并赋值给airWeather变量
this.airQualityData = JSON.parse(data.toString());
this.aqi = this.airQualityData.aqi
this.category = this.airQualityData.category
});
}
build() {
Column() {
Text('空气质量').fontColor($r('app.color.weatherCardTitle')).fontSize(11)
.margin({
bottom: 10
})
Column() {
}
.border({ width: { left: 0, right: 0, top: 0, bottom: 0.4 }, color: $r('app.color.weatherCardTitle'), })
.width('100%')
Text(this.aqi + ' - ' + this.category)
.fontColor($r('app.color.baseFontColor'))
.fontSize(20)
.fontWeight(700)
.margin({
top: 10,
})
Text('当前AQI(CN)为' + this.aqi)
.fontColor($r('app.color.baseFontColor')).fontSize(16).fontWeight(300)
.margin({
top: 10,
bottom: 5
})
}
.border({ width: 0.4, color: $r('app.color.weatherCardTitle'), })
.backgroundColor($r('app.color.weatherCardBack'))
.alignItems(HorizontalAlign.Start)
.width('100%')
.padding(15)
.borderRadius(10)
.margin({ top: 20 })
.shadow({
radius: 20,
color: $r('app.color.weatherCardShadow')
})
.backdropBlur(70)
.animation({
duration: 120,
curve: Curve.EaseInOut,
})
}
}
复制代码
这样就能获取实时的天气质量信息了
总结
在搭建项目的过程中,我们首先创建了空气质量组件的文件,并添加了修饰符和构建函数。然后,在主页中添加了一个 Scroll 容器来放置空气质量组件,并对 Scroll 容器进行了样式修改。接下来,我们为空气质量组件添加了对应的文字和样式,包括分隔线和毛玻璃效果。然后,我们添加了背景图片,并修改了字体颜色。最后,我们构建了空气质量请求,通过 axios 发送 GET 请求获取天气数据,并将返回的数据转换为字符串并赋值给组件的状态变量。通过这样的操作,我们成功地搭建了一个可以获取实时空气质量信息的组件
评论