易得天气
1、极端天气详情天气弹窗
2、空气质量详情天气弹窗
3、空气质量指数信息弹窗
使用到第三方库:
perl
"@pura/harmony-dialog": "^1.1.4",
效果图
极端天气详情天气弹窗
kotlin
.onClick(() => {
if (this.showHideWeatherContent) {
this.showHideWeatherContent(false)
}
DialogHelper.showCustomDialog(wrapBuilder(WeatherAlarmsDetailPopupBuilder), {
dialogId: 'weather_alarms_detail_popup',
alarms: this.weatherItemData?.weatherData?.alarms,
isDark: this.isDark,
panelOpacity: this.panelOpacity,
ref: this.alarmsDetailPopupRef,
backCancel: false,
maskColor: $r('app.color.transparent'),
transition: AnimationHelper.transitionInDown(0),
onWillDismiss: () => {
this.alarmsDetailPopupRef.exit()
},
onDidDisappear: () => {
if (this.showHideWeatherContent) {
this.showHideWeatherContent(true)
}
}
} as WeatherAlarmsDetailPopupOptions)
})
less
@Builder
export function WeatherAlarmsDetailPopupBuilder(options: WeatherAlarmsDetailPopupOptions) {
WeatherAlarmsDetailPopup({ options: options })
}
@ComponentV2
export struct WeatherAlarmsDetailPopup {
@Require @Param options: WeatherAlarmsDetailPopupOptions
@Local animHeight: number = Constants.WEATHER_ALARM_PANEL_HEIGHT
@Local contentOpacity: number = 0
@Local marginTop: number = 0
@Local alignment: FlexAlign = FlexAlign.Start
private heights: Array<number> = []
aboutToAppear(): void {
this.options.ref.exit = this.exit
const weatherAlarmsPanelRectangle = componentUtils.getRectangleById('weather_alarms_panel')
this.marginTop = px2vp(weatherAlarmsPanelRectangle.windowOffset.y)
setTimeout(() => {
this.contentOpacity = 1
this.marginTop = 0
this.alignment = FlexAlign.Center
}, 16)
}
exit = () => {
const weatherAlarmsPanelRectangle = componentUtils.getRectangleById('weather_alarms_panel')
this.alignment = FlexAlign.Start
this.marginTop = px2vp(weatherAlarmsPanelRectangle.windowOffset.y)
this.contentOpacity = 0
setTimeout(() => {
DialogHelper.closeDialog('weather_alarms_detail_popup')
}, 200)
}
build() {
Column() {
Stack() {
Swiper() {
ForEach(this.options.alarms, (item: WeatherAlarmsData, index: number) => {
this.alarmItem(item, index)
}, (item: WeatherAlarmsData) => item.short_title)
}
.width('100%')
.height(this.animHeight)
.animation({ duration: 200, curve: Curve.Linear })
.loop(false)
.indicator(
(this.options.alarms?.length ?? 0) <= 1 ? false :
Indicator.dot()
.itemWidth(3)
.itemHeight(2)
.selectedItemWidth(6)
.selectedItemHeight(2)
.color(ColorUtils.alpha($r('app.color.special_white'), 0.5))
.selectedColor($r('app.color.special_white'))
)
.onChange((index) => {
this.animHeight = this.heights[index]
})
}
.width(`calc(100% - ${2 * 16}vp)`)
.opacity(this.contentOpacity)
.margin({ top: this.marginTop })
.animation({ curve: Curve.Ease, duration: 200 })
.backgroundColor(ColorUtils.alpha(this.options.isDark ? $r('app.color.special_white') :
$r('app.color.special_black'),
this.options.panelOpacity))
.borderRadius(Constants.ITEM_PANEL_RADIUS)
}
.width('100%')
.height('100%')
.justifyContent(this.alignment)
.animation({ curve: Curve.Ease, duration: 200 })
.onClick(() => {
this.exit()
})
}
@Builder
alarmItem(item: WeatherAlarmsData, index: number) {
Column() {
Blank().height(12)
Text(item.short_title)
.fontSize(16)
.fontColor($r('app.color.special_white'))
.fontWeight(FontWeight.Bold)
Blank().height(6)
Text(this.pubTimeDesc(item))
.fontSize(13)
.fontColor($r('app.color.special_white'))
Blank().height(8)
Text(item.desc)
.fontSize(14)
.fontColor($r('app.color.special_white'))
.textOverflow({ overflow: TextOverflow.Ellipsis })
.onSizeChange((_, newValue) => {
const height = newValue.height
if (height) {
const heightValue = height as number
if (!this.heights[index]) {
this.heights.splice(index, 0, heightValue + 53 + 42)
if (this.animHeight == Constants.WEATHER_ALARM_PANEL_HEIGHT) {
this.animHeight = this.heights[0]
}
}
}
})
Blank().height(28)
}
.width('100%')
.alignItems(HorizontalAlign.Start)
.padding({ left: 16, right: 16 })
}
pubTimeDesc(item: WeatherAlarmsData): string {
let pubTimeDesc = ''
const date = DateUtil.getFormatDate(item.pub_time)
const diff = DateUtil.getToday().getTime() - date.getTime()
const fewHours = Math.floor(diff / 1000 / 60 / 60)
pubTimeDesc = `${fewHours}小时前更新`
if (fewHours <= 0) {
const fewMinutes = Math.floor(diff / 1000 / 60)
pubTimeDesc = `${fewMinutes}分钟前更新`
if (fewMinutes <= 0) {
const fewMills = Math.floor(diff / 1000)
pubTimeDesc = `${fewMills}秒前更新`
}
}
return pubTimeDesc
}
}
空气质量详情天气弹窗
kotlin
.onClick(() => {
if (this.showHideWeatherContent) {
this.showHideWeatherContent(false)
}
DialogHelper.showCustomDialog(wrapBuilder(AirQualityDetailPopupBuilder), {
dialogId: 'air_quality_detail_popup',
evn: this.weatherItemData?.weatherData?.evn,
isDark: this.isDark,
panelOpacity: this.panelOpacity,
ref: this.popupExitRef,
backCancel: false,
maskColor: $r('app.color.transparent'),
transition: AnimationHelper.transitionInDown(0),
onWillDismiss: () => {
this.popupExitRef.exit()
},
onDidDisappear: () => {
if (this.showHideWeatherContent) {
this.showHideWeatherContent(true)
}
}
} as AirQualityDetailPopupOptions)
})
less
@Builder
export function AirQualityDetailPopupBuilder(options: AirQualityDetailPopupOptions) {
AirQualityDetailPopup({ options: options })
}
@ComponentV2
export struct AirQualityDetailPopup {
@Require @Param options: AirQualityDetailPopupOptions
@Local panelOpacity: number = 0
@Local contentOpacity: number = 0
@Local marginTop: number = 0
@Local alignment: FlexAlign = FlexAlign.Start
aboutToAppear(): void {
this.options.ref.exit = this.exit
const airQualityPanelRectangle = componentUtils.getRectangleById('weather_air_quality_panel')
this.marginTop = px2vp(airQualityPanelRectangle.windowOffset.y)
setTimeout(() => {
this.panelOpacity = 1
this.marginTop = 0
this.alignment = FlexAlign.Center
setTimeout(() => {
this.contentOpacity = 1
}, 200)
}, 16)
}
exit = () => {
const airQualityPanelRectangle = componentUtils.getRectangleById('weather_air_quality_panel')
this.alignment = FlexAlign.Start
this.marginTop = px2vp(airQualityPanelRectangle.windowOffset.y)
this.contentOpacity = 0
setTimeout(() => {
this.panelOpacity = 0
setTimeout(() => {
DialogHelper.closeDialog('air_quality_detail_popup')
}, 200)
}, 200)
}
build() {
Column() {
Column() {
this.airQualityPanel()
Blank().height(12)
Grid() {
this.airQualityItem('PM2.5', this.options.evn?.pm25 ?? 0)
this.airQualityItem('PM10', this.options.evn?.pm10 ?? 0)
this.airQualityItem('NO', this.options.evn?.no2 ?? 0, '2')
this.airQualityItem('SO', this.options.evn?.so2 ?? 0, '2')
this.airQualityItem('O', this.options.evn?.o3 ?? 0, '3')
this.airQualityItem('CO', this.options.evn?.co ?? 0)
}
.width('100%')
.columnsTemplate('1fr 1fr 1fr')
.columnsGap(12)
.rowsGap(12)
.maxCount(3)
.layoutDirection(GridDirection.Row)
.opacity(this.contentOpacity)
.animation({ curve: Curve.Ease, duration: 200 })
}
.width(`calc(100% - ${2 * 16}vp)`)
.opacity(this.panelOpacity)
.margin({ top: this.marginTop })
.animation({ curve: Curve.Ease, duration: 200 })
}
.width('100%')
.height('100%')
.justifyContent(this.alignment)
.animation({ curve: Curve.Ease, duration: 200 })
.onClick(() => {
this.exit()
})
}
@Builder
airQualityPanel() {
Column() {
Blank().height(12)
Row() {
Text(this.title)
.fontSize(16)
.fontColor($r('app.color.special_white'))
.fontWeight(FontWeight.Bold)
OpacityLayout({
child: () => {
this.queryIcon()
},
onTap: () => {
DialogHelper.showBindSheet(wrapBuilder(AirQualityQueryDialogBuilder), {
dialogId: 'air_quality_query_dialog',
})
}
})
}
Blank().height(12)
AirQualityBar({ barWidth: '100%', barHeight: 4, aqi: this.options.evn?.aqi ?? 0 })
Blank().height(10)
Text(this.desc)
.fontSize(13)
.fontColor($r('app.color.special_white'))
}
.width('100%')
.height(Constants.WEATHER_AIR_QUALITY_PANEL_HEIGHT)
.alignItems(HorizontalAlign.Start)
.padding({ left: 16, right: 16 })
.backgroundColor(ColorUtils.alpha(this.options.isDark ? $r('app.color.special_white') :
$r('app.color.special_black'),
this.options.panelOpacity))
.borderRadius(Constants.ITEM_PANEL_RADIUS)
}
@Builder
airQualityItem(title: string, value: number, subscript?: string) {
GridItem() {
Column() {
Row() {
Text(title)
.fontSize(16)
.fontColor($r('app.color.special_white'))
.fontWeight(FontWeight.Bold)
Text(subscript ?? '')
.fontSize(10)
.fontColor($r('app.color.special_white'))
.fontWeight(FontWeight.Bold)
.visibility(StrUtil.isEmpty(subscript) ? Visibility.Hidden : Visibility.Visible)
Blank().width(2)
Text('ug/m³')
.fontSize(12)
.fontColor(ColorUtils.alpha($r('app.color.special_white'), 0.6))
}
Blank().height(12)
Text(value.toFixed(1))
.fontSize(18)
.fontColor($r('app.color.special_white'))
.fontWeight(FontWeight.Bold)
}
.width('100%')
.height(72)
.padding({ left: 12, right: 12 })
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Start)
.backgroundColor(ColorUtils.alpha(this.options.isDark ? $r('app.color.special_white') :
$r('app.color.special_black'),
this.options.panelOpacity))
.borderRadius(Constants.ITEM_PANEL_RADIUS)
}
}
@Builder
queryIcon() {
Stack() {
Image($r('app.media.ic_query_icon'))
.width(14)
.height(14)
.draggable(false)
.colorFilter(ColorUtils.translateColor($r('app.color.special_white')))
}
.padding({
left: 12,
top: 4,
right: 12,
bottom: 4
})
}
get title() {
return `${this.options.evn?.aqi} - ${this.options.evn?.aqi_level_name}`
}
get desc() {
return `当前AQI为${this.options.evn?.aqi}`
}
}
空气质量指数信息弹窗
scss
OpacityLayout({
child: () => {
this.queryIcon()
},
onTap: () => {
DialogHelper.showBindSheet(wrapBuilder(AirQualityQueryDialogBuilder), {
dialogId: 'air_quality_query_dialog',
})
}
})
less
@Builder
export function AirQualityQueryDialogBuilder(options: BaseSheetOptions) {
AirQualityQueryDialog({ options: options })
}
@ComponentV2
export struct AirQualityQueryDialog {
@Require @Param options: BaseSheetOptions
build() {
Column() {
Stack({ alignContent: Alignment.TopEnd }) {
Text('中国国家环境保护部\n空气质量指数及相关信息')
.width('100%')
.textAlign(TextAlign.Center)
.fontSize(20)
.fontColor($r('app.color.black'))
.fontWeight(FontWeight.Bold)
.margin({ top: 12 })
OpacityLayout({
child: () => {
this.closeIcon()
},
onTap: () => {
DialogHelper.closeBindSheet('air_quality_query_dialog')
}
})
}
.width('100%')
Row() {
Stack()
.width(16)
.height('100%')
.linearGradient({
direction: GradientDirection.Bottom,
colors: [[$r('app.color.color_00e301'), 0.0], [$r('app.color.color_fdfd01'), 0.2],
[$r('app.color.color_fd7e01'), 0.4], [$r('app.color.color_f70001'), 0.6],
[$r('app.color.color_98004c'), 0.8],
[$r('app.color.color_7d0023'), 1.0]]
})
.borderRadius(100)
Blank().width(12)
Column() {
this.content('优:0-50', '空气质量令人满意,基本无空气污染,各类人群可正常活动')
this.content('良:51-100', '空气质量可接受,但某些污染物可能对极少数异常敏感人群健康有较弱影响')
this.content('轻度污染:101-150', '儿童、老年人及心脏病、呼吸系统疾病患者应减少长时间、高强度的户外锻炼')
this.content('中度污染:151-200',
'儿童、老年人及心脏病、呼吸系统疾病患者应减少长时间、高强度的户外锻炼,一般人群适量减少户外运动')
this.content('重度污染:201-300',
'儿童、老年人和心脏病、肺病患者应停留在室内,停止户外运动,一般人群减少户外运动', FlexAlign.Center)
this.content('严重污染:大于300', '儿童、老年人和病人应当留在室内,避免体力消耗,一般人群应避免户外活动',
FlexAlign.End)
}
.layoutWeight(1)
}
.width('100%')
.padding({ left: 20, right: 20 })
.layoutWeight(1)
.margin({ top: 24, bottom: px2vp(AppUtil.getNavigationIndicatorHeight()) + 64 })
}
.width('100%')
.backgroundColor($r('app.color.bg_color'))
.borderRadius({ topLeft: 24, topRight: 24 })
}
@Builder
closeIcon() {
Stack() {
Image($r('app.media.ic_close_icon1'))
.width(22)
.height(22)
.colorFilter(ColorUtils.translateColor($r('app.color.black')))
.draggable(false)
}
.padding(16)
}
@Builder
content(title: string, content: string, justifyContent: FlexAlign = FlexAlign.Start) {
Column() {
Text(title)
.fontSize(18)
.fontColor($r('app.color.black'))
.fontWeight(FontWeight.Bold)
Blank().height(2)
Text(content)
.fontSize(15)
.fontColor($r('app.color.text_color_01'))
.fontWeight(FontWeight.Bold)
}
.width('100%')
.alignItems(HorizontalAlign.Start)
.justifyContent(justifyContent)
.layoutWeight(1)
}
}