易得天气
开始做天气预报主界面,目前完成了天气预报的头部UI。
等天气预报完成后会将代码进行开源
效果图
天气预报数据源
kotlin
// 生成天气背景
this.weatherBg = WeatherDataUtils.generateWeatherBg(weatherData)
// 根据天气背景计算天气头部是否是dark模式
this.isWeatherHeaderDark = WeatherDataUtils.isWeatherHeaderDark(this.weatherBg)
// 根据天气背景计算天气内容是否是dark模式
this.isDark = WeatherDataUtils.isDark(this.weatherBg)
// 根据天气背景计算天气面板的透明度
this.panelOpacity = WeatherDataUtils.calPanelOpacity(this.weatherBg)
// 生成天气items数据
this.weatherItems = WeatherDataUtils.generateWeatherItems(AppRuntimeData.getInstance().currentWeatherCardSort,
AppRuntimeData.getInstance().currentWeatherObservesCardSort, weatherData)
页面布局
scss
Stack({ alignContent: Alignment.Top }) {
List({ scroller: this.weatherMainVM.listScroller }) {
ForEach(this.weatherMainVM.weatherItemsFilter, (item: WeatherItemData) => {
ListItem() {
Text(item.itemType.toString())
.width("100%")
.height(800)
.fontSize(20)
.textAlign(TextAlign.Center)
.backgroundColor(Color.Gray)
}
}, (item: WeatherItemData) => item.itemType.toString())
}
.width('100%')
.height('100%')
.sticky(StickyStyle.Header)
.scrollBar(BarState.Off)
.edgeEffect(EdgeEffect.Spring, { alwaysEnabled: true })
.margin({ top: px2vp(AppUtil.getStatusBarHeight()) + Constants.WEATHER_HEADER_MIN_HEIGHT })
.contentStartOffset(Constants.WEATHER_HEADER_MAX_HEIGHT - Constants.WEATHER_HEADER_MIN_HEIGHT)
.onDidScroll(() => {
const yOffset = this.weatherMainVM.listScroller.currentOffset().yOffset
this.weatherMainVM.setWeatherHeaderOffset(yOffset + (Constants.WEATHER_HEADER_MAX_HEIGHT - Constants.WEATHER_HEADER_MIN_HEIGHT))
})
WeatherHeaderWidget({
isWeatherHeaderDark: this.weatherMainVM.isWeatherHeaderDark,
weatherItemData: this.weatherMainVM.weatherHeaderItemData,
weatherHeaderOffset: this.weatherMainVM.weatherHeaderOffset
})
}
监听weatherHeaderOffset变化从而改变页面高度以及透明度
ini
@Monitor('weatherHeaderOffset')
onOffsetChanged(monitor: IMonitor) {
const offset = monitor.value<number>()?.now ?? 0
const percent = fixPercent(offset / (Constants.WEATHER_HEADER_MAX_HEIGHT - Constants.WEATHER_HEADER_MIN_HEIGHT))
const currentHeight = Constants.WEATHER_HEADER_MAX_HEIGHT - offset;
const minHeight = Constants.WEATHER_HEADER_MIN_HEIGHT
this.currentHeight = currentHeight < minHeight ? minHeight : currentHeight
const marginTop = this.minMarginTop + (this.maxMarginTop - this.minMarginTop) * (1 - percent)
this.marginTop = marginTop < this.minMarginTop ? this.minMarginTop : marginTop
const opacity1 = 1 - (percent - 0.2) / (0.3 - 0.2)
this.opacity1 = opacity1 > 1 ? 1 : (opacity1 < 0 ? 0 : opacity1)
const opacity2 = 1 - (percent - 0.4) / (0.5 - 0.4)
this.opacity2 = opacity2 > 1 ? 1 : (opacity2 < 0 ? 0 : opacity2)
const opacity3 = 1 - (percent - 0.7) / (0.8 - 0.7)
this.opacity3 = opacity3 > 1 ? 1 : (opacity3 < 0 ? 0 : opacity3)
const opacity4 = 1 - (percent - 0.9) / (0.9 - 1.0)
this.opacity4 = opacity4 > 1 ? 1 : (opacity4 < 0 ? 0 : opacity4)
}
item布局
scss
build() {
Column() {
Text(this.title)
.textStyle(28, FontWeight.Normal, this.isWeatherHeaderDark)
.margin({ top: this.marginTop })
Stack({ alignContent: Alignment.Top }) {
Column({ space: 5 }) {
Row() {
Blank()
.layoutWeight(1)
Text(this.temp)
.textStyle(92, FontWeight.Lighter, this.isWeatherHeaderDark)
Text('°')
.textStyle(86, FontWeight.Lighter, this.isWeatherHeaderDark)
.layoutWeight(1)
}
.width('100%')
.opacity(this.opacity3)
Row() {
Text('最\n高')
.textStyle(15, FontWeight.Normal, this.isWeatherHeaderDark)
Blank().width(3)
Text(getTemp(this.currentWeatherDetailData?.high))
.textStyle(34, FontWeight.Lighter, this.isWeatherHeaderDark)
Blank().width(12)
Text('最\n低')
.textStyle(15, FontWeight.Normal, this.isWeatherHeaderDark)
Blank().width(3)
Text(getTemp(this.currentWeatherDetailData?.low))
.textStyle(34, FontWeight.Lighter, this.isWeatherHeaderDark)
}
.opacity(this.opacity2)
Text(this.weatherDesc)
.textStyle(20, FontWeight.Normal, this.isWeatherHeaderDark)
.opacity(this.opacity1)
}
Text(`${getTemp(this.weatherItemData?.weatherData?.observe?.temp)} | ${this.weatherDesc}`)
.textStyle(20, FontWeight.Normal, this.isWeatherHeaderDark)
.opacity(this.opacity4)
}
.margin({ top: 5 })
}
.width('100%')
.height(this.currentHeight)
.margin({ top: px2vp(AppUtil.getStatusBarHeight()) })
.hitTestBehavior(HitTestMode.Transparent)
.clip(true)
}