前言学鸿蒙也有一段时间了之前做了个掷骰子的小项目这次想挑战点更有难度的。想了想天气App挺合适的——UI复杂、数据多、交互丰富正好练手。这篇文章记录了整个开发过程有思路、有代码、有踩坑希望能帮到同样在学习鸿蒙的小伙伴。为什么选天气App理由有几个功能全面- 数据展示、列表、弹窗该有的都有UI有挑战- 渐变背景、卡片布局、温度条够折腾实用性强- 做完真能用接个API就行能发挥创意- 主题切换、动画效果随你玩功能规划开干之前先想清楚要做什么功能说明天气展示温度、天气状况、最高最低温生活指数空气质量、紫外线、湿度、风速逐小时预报8小时天气趋势7天预报一周天气概况城市切换支持8个城市动态主题根据天气换背景颜色项目创建打开DevEco Studio选Empty Ability模板。填信息项目名WeatherApp包名com.example.weatherapp位置E:\HMproject\Project\WeatherApp点Finish等项目初始化。主要代码在entry/src/main/ets/pages/Index.ets。数据设计数据结构先定义数据结构这是整个App的基础// 天气信息interfaceWeatherInfo{city:string// 城市名temp:number// 当前温度cond:string// 天气状况晴/多云/雨...humid:number// 湿度wind:string// 风力uv:string// 紫外线high:number// 最高温low:number// 最低温aqi:number// 空气质量指数aqiDesc:string// 空气质量描述}// 小时预报interfaceHourlyItem{time:stringtemp:numbericon:string}// 日预报interfaceDailyItem{day:stringdate:stringicon:stringhigh:numberlow:numberdesc:string}模拟数据这个项目先用模拟数据以后再接真实API。我准备了8个城市的数据privatereadonlyWEATHER_DATA:WeatherInfo[][{city:北京市,temp:26,cond:晴,humid:45,wind:3级,uv:中等,high:30,low:18,aqi:72,aqiDesc:良},{city:上海市,temp:24,cond:多云,humid:62,wind:4级,uv:中等,high:27,low:20,aqi:55,aqiDesc:良},{city:广州市,temp:31,cond:雷阵雨,humid:78,wind:3级,uv:强,high:33,low:25,aqi:38,aqiDesc:优},// ... 其他城市]状态管理ArkUI用State管状态状态变了UI自动更新。我定义了一堆状态Statelocation:string北京市// 当前城市StatecurrentTemp:number26// 当前温度StatecurrentCondition:string晴// 天气状况StatecurrentHumidity:number45// 湿度StatecurrentWind:string3级// 风力StatecurrentUV:string中等// 紫外线StatecurrentHigh:number30// 最高温StatecurrentLow:number18// 最低温StatecurrentAQI:number72// AQIStatecurrentAQIDesc:string良// AQI描述StateshowCityPicker:booleanfalse// 弹窗开关StatehourlyData:HourlyItem[][]// 小时预报StatedailyData:DailyItem[][]// 日预报看着多其实每个都有用。核心功能动态主题切换这是我做这个App最想实现的功能——根据天气自动换背景晴天用橙色雨天用蓝色阴天用灰色privategetBgGradient(cond:string):string{if(cond晴)return#FF9F0A// 橙色if(cond多云||cond阴)return#8E8E93// 灰色if(cond.includes(雨))return#5AC8FA// 蓝色return#4A90D9}privategetBgEnd(cond:string):string{if(cond晴)return#FFD60Aif(cond多云||cond阴)return#636366if(cond.includes(雨))return#007AFFreturn#87CEEB}然后在Column上加渐变Column(){// 内容...}.linearGradient({direction:GradientDirection.Bottom,colors:[[this.getBgGradient(this.currentCondition),0],[this.getBgEnd(this.currentCondition),1]]})效果超棒切到北京就是橙色的晴天切到广州就是蓝色的雨天。温度条可视化7天预报里有温度条我觉得这个挺酷的BuilderdailyRow(item:DailyItem){Row(){// 左边星期、图标、描述Text(item.day).width(48)Text(item.icon).width(30)Text(item.desc).width(36)Blank()// 最低温Text(String(item.low)°).width(32)// 温度条Column(){Column().width(this.tempBarWidth(item.low,item.high)).height(6).borderRadius(3).backgroundColor(this.tempBarColor(item.low,item.high))}.width(60).height(6).backgroundColor(#333333).borderRadius(3)// 最高温Text(String(item.high)°).width(32)}}温度条的宽度和颜色是动态计算的// 温差越大条越宽privatetempBarWidth(low:number,high:number):string{returnMath.floor(((high-low)/20)*10020)%}// 温度越高颜色越红privatetempBarColor(low:number,high:number):string{constavg(lowhigh)/2if(avg28)return#FF3B30// 红色if(avg20)return#FF9F0A// 橙色return#34C759// 绿色}城市切换点城市名弹出选择器选了就切换所有数据privateswitchCity(city:string):void{constdatathis.getWeatherByCity(city)// 更新所有状态this.locationcitythis.currentTempdata.tempthis.currentConditiondata.cond// ... 其他状态// 重新生成预报this.hourlyDatathis.generateHourlyData(data.cond,data.temp)this.dailyDatathis.generateDailyData(data.cond,data.high,data.low)// 关弹窗this.showCityPickerfalse}组件封装ArkUI有个Builder装饰器可以把UI封装成可复用的组件。信息卡片BuildercompactCard(icon:string,value:string,desc:string,color:string){Column(){Text(icon).fontSize(20)Text(value).fontSize(16).fontWeight(FontWeight.Bold).fontColor(Color.White).margin({top:6})if(desc)Text(desc).fontSize(12).fontColor(color).margin({top:2})}.layoutWeight(1).alignItems(HorizontalAlign.Center)}用起来很方便Row(){this.compactCard(,72,良,#34C759)this.compactCard(☀️,中等,,#FF9F0A)this.compactCard(,45%,,#5AC8FA)this.compactCard(️,3级,,#8E8E93)}城市按钮BuildercityButton(city:string){Button(city).height(44).borderRadius(22).backgroundColor(this.locationcity?#FF9F0A:#2C2C2E).fontColor(this.locationcity?Color.White:#8E8E93).layoutWeight(1).onClick(()this.switchCity(city))}选中的城市高亮显示。布局实现整体结构build(){Stack(){// 主内容Scroll(){Column(){// 1. 天气展示区渐变背景// 2. 信息卡片// 3. 小时预报// 4. 7天预报}}// 城市选择弹窗if(this.showCityPicker){// 弹窗内容}}}用Stack是为了叠加弹窗。城市选择弹窗if(this.showCityPicker){Column(){Column(){Text(选择城市).margin({top:20,bottom:16})Row(){this.cityButton(北京市)this.cityButton(上海市)this.cityButton(广州市)this.cityButton(深圳市)}Row(){this.cityButton(杭州市)this.cityButton(成都市)this.cityButton(武汉市)this.cityButton(南京市)}Button(取消).onClick(()this.showCityPickerfalse)}.width(85%).backgroundColor(#1C1C1E).borderRadius(20)}.width(100%).height(100%).backgroundColor(#80000000)// 半透明遮罩.justifyContent(FlexAlign.Center)}踩坑记录坑1渐变背景不生效一开始忘了加linearGradient背景就是纯色。检查了好几遍才发现…解决在Column上正确添加linearGradient属性。坑2弹窗点内部也会关遮罩层的onClick写在最外层结果点弹窗内容也会触发。解决内部弹窗容器不要加onClick只在外层遮罩加。坑3温度条宽度一样一开始用固定宽度看起来没差别。解决改成根据温差计算宽度。坑4切换城市背景没变忘了currentCondition也要更新。解决在switchCity里更新所有相关状态。运行效果在DevEco Studio里运行效果如下点击城市名不同天气的背景学到了啥状态联动- 一个操作更新多个状态渐变背景-linearGradient的用法条件渲染-if控制弹窗显示组件封装-Builder复用UI叠加布局-Stack实现弹窗动态样式- 方法返回颜色和宽度后续计划这个App还能继续完善接真实API- 和风天气、心知天气都行加定位- 自动获取当前城市天气动画- 下雨效果、飘雪效果下拉刷新- 更新天气数据多日预报- 15天天气生活指数- 穿衣、洗车、运动建议总结这个天气App比之前的掷骰子复杂多了但也更有成就感。动态主题切换是我最满意的功能切城市的时候背景跟着变感觉很高级。ArkUI写起来确实舒服声明式UI真是前端开发的大趋势。有React或Flutter经验的话上手很快。有问题欢迎留言交流~