鸿蒙5.0项目开发——鸿蒙天气项目的实现(主页2)

【高心星出品】

4.主界面

在这里插入图片描述

1. 顶部搜索区域
  • 左侧菜单按钮

  • 中间搜索输入框

  • 右侧搜索按钮

  • 整体采用白色背景,圆角设计

2. 天气信息卡片
  • 城市名称(大号字体)

  • 当前温度(特大号字体)

  • 天气图标(大尺寸)

  • 天气描述

  • 详细信息网格:

  • 湿度

  • 风速

  • PM2.5

  • 空气质量

  • 卡片采用白色背景,带阴影效果

3. 天气预报区域
  • 标题:“未来天气预报”

  • 三个天气卡片:

  • 显示星期

  • 天气图标

  • 温度范围

  • 采用白色背景,带阴影效果

4. 左侧抽屉菜单
  • 顶部标题栏:

  • "搜索历史"标题

  • 编辑/完成按钮

  • 历史记录列表:

  • 城市名称

  • 搜索时间

  • 删除按钮(编辑模式)

  • 空状态显示:

  • 空历史图标

  • "暂无搜索历史"提示

  • 底部清空按钮(编辑模式)

  • 支持滑动关闭

  build() {
    Stack() {
      Column() {
        // 搜索框布局
        Row() {
          Image($r('app.media.menu'))
            .width(30)
            .onClick(() => {
              this.isshow = !this.isshow
            })
          TextInput({ placeholder: '输入城市名称' })
            .width('60%')
            .height(40)
            .backgroundColor(Color.White)
            .borderRadius(8)
            .onChange((value: string) => {
              this.searchText = value
            })

          Button('搜索')
            .width('25%')
            .height(40)
            .backgroundColor('#0083b0')
            .onClick(() => {
              if (this.searchText.trim()) {
                this.loadweather(this.searchText.trim(), true)
              } else {
                promptAction.showToast({ message: '请输入城市名称' })
              }
            })
        }
        .width('100%')
        .justifyContent(FlexAlign.SpaceBetween)
        .margin({ bottom: 20 })

        // 天气信息卡片布局
        Column() {
          // 城市名称
          Text(this.cityName)
            .fontSize(24)
            .fontWeight(FontWeight.Bold)
            .margin({ bottom: 10 })

          // 温度显示
          Text(this.temperature)
            .fontSize(48)
            .fontWeight(FontWeight.Bold)
            .margin({ bottom: 10 })

          // 天气图标
          Image(getweathericon(this.weatherDesc))
            .width(100)
            .height(100)
            .margin({ bottom: 10 })

          // 天气描述
          Text(this.weatherDesc)
            .fontSize(18)
            .margin({ bottom: 20 })

          // 详细信息网格布局
          Grid() {
            // 湿度信息
            GridItem() {
              Column() {
                Text('湿度')
                  .fontSize(14)
                  .fontColor('#666666')
                Text(this.humidity)
                  .fontSize(16)
                  .fontWeight(FontWeight.Bold)
                  .margin({ top: 5 })
              }
            }

            // 风速信息
            GridItem() {
              Column() {
                Text('风速')
                  .fontSize(14)
                  .fontColor('#666666')
                Text(this.windSpeed)
                  .fontSize(16)
                  .fontWeight(FontWeight.Bold)
                  .margin({ top: 5 })
              }
            }

            // PM2.5信息
            GridItem() {
              Column() {
                Text('PM2.5')
                  .fontSize(14)
                  .fontColor('#666666')
                Text(this.pm25)
                  .fontSize(16)
                  .fontWeight(FontWeight.Bold)
                  .margin({ top: 5 })
              }
            }

            // 空气质量信息
            GridItem() {
              Column() {
                Text('空气质量')
                  .fontSize(14)
                  .fontColor('#666666')
                Text(this.quality)
                  .fontSize(16)
                  .fontWeight(FontWeight.Bold)
                  .margin({ top: 5 })
              }
            }
          }
          .columnsTemplate('1fr 1fr')
          .rowsTemplate('1fr 1fr')
          .width('100%')
          .height(160)
          .margin({ top: 20 })
        }
        .width('100%')
        .padding(20)
        .backgroundColor('#FFFFFF')
        .borderRadius(16)
        .shadow({ radius: 6, color: '#1A000000', offsetY: 2 })

        // 天气预报布局
        Column() {
          Text('未来天气预报')
            .fontSize(20)
            .fontWeight(FontWeight.Bold)
            .margin({ bottom: 15 })

          // 未来三天天气预报
          Row() {
            // 按照引用传值才会引起ui刷新
            this.ForecastItem({ day: this.day1week, tempe: this.day1wendu, type: this.day1type })
            this.ForecastItem({ day: this.day2week, tempe: this.day2wendu, type: this.day2type })
            this.ForecastItem({ day: this.day3week, tempe: this.day3wendu, type: this.day3type })
          }
          .width('100%')
          .justifyContent(FlexAlign.SpaceBetween)
        }
        .width('100%')
        .padding(20)
        .backgroundColor('#FFFFFF')
        .borderRadius(16)
        .margin({ top: 20 })
        .shadow({ radius: 6, color: '#1A000000', offsetY: 2 })
      }
      .width('100%')
      .height('100%')
      .padding(20)
      .backgroundColor('#F5F5F5')
      .onClick(() => {
        this.isshow = false
      })

      // 左侧抽屉布局
      if (this.isshow) {
        Column() {
          // 顶部标题栏布局
          Row() {
            if (this.searchHistory.length > 0) {
              Text('搜索历史')
                .fontSize(20)
                .fontWeight(FontWeight.Bold)
            }
            Blank()

            if (this.searchHistory.length > 0) {
              Text(this.isEditing ? '完成' : '编辑')
                .fontSize(16)
                .fontColor('#0083b0')
                .onClick(() => {
                  this.isEditing = !this.isEditing
                  // 编辑完成后清除要删除的id
                  if (!this.isEditing) {
                    this.delids.clear()
                  }
                })
            }
          }
          .width('100%')
          .height(50)
          .padding(16)
          .margin({ top: 20 })
          .backgroundColor('#FFFFFF')

          // 搜索历史为空时的显示
          if (this.searchHistory.length === 0) {
            Column() {
              Image($r('app.media.empty_history'))
                .width(80)
                .height(80)
                .margin({ bottom: 16 })

              Text('暂无搜索历史')
                .fontSize(16)
                .fontColor('#999999')
            }
            .width('100%')
            .height('100%')
            .justifyContent(FlexAlign.Center)
          } else {
            // 搜索历史列表布局
            List({ space: 10 }) {
              ForEach(this.searchHistory, (item: SearchHistoryItem) => {
                ListItem() {
                  Row() {
                    Row({ space: 20 }) {
                      Text(item.keyword)
                        .fontSize(16)
                        .fontColor(Color.Black)

                      Text(item.timestamp)
                        .fontSize(14)
                        .fontColor('#555555')
                        .margin({ top: 4 })
                    }.onClick(async () => {
                      await this.loadweather(item.keyword, false)
                      this.isshow = false
                    })

                    .layoutWeight(1)

                    // 编辑模式下显示删除按钮
                    if (this.isEditing) {
                      Image($r('app.media.delete'))
                        .width(20)
                        .height(20)
                        .margin({ left: 16 })
                        .onClick(() => {
                          this.deleteHistoryItem(item.id as number)
                        })
                        .transition(this.effect1)
                    }
                  }
                  .width('100%')
                  .padding(15)
                }
                .translate({ x: this.delids.has(item.id) ? '-100%' : '0%' })
              })
            }
            .width('100%')
            .layoutWeight(1)
            .margin({ top: 10 })
            .divider({ strokeWidth: 2, color: 'rgba(0,0,0,0.4)' })
          }

          // 编辑模式下显示清空按钮
          if (this.searchHistory.length > 0 && this.isEditing) {
            Button('清空历史记录')
              .width('90%')
              .height(40)
              .margin({ bottom: 16 })
              .backgroundColor('#FF4D4F')
              .onClick(() => {
                this.clearHistory()
              })
              .transition(this.effect1)
          }
        }
        .width('60%')
        .height('100%')
        .backgroundColor(Color.White)
        .transition(this.effect)
        .borderRadius({ topRight: 20, bottomRight: 20 })
        .border({ width: 1, color: 'rgba(0,0,100,0.5)' })
        // 抽屉出现时加载搜索历史
        .onAppear(() => {
          let sd = new searchdao(this.context)
          sd.queryall().then((value) => {
            this.searchHistory = value
          }).catch((e: Error) => {
            console.error('gxxt 查询搜索历史失败: ', e.message)
          })
        })
        // 抽屉滑动关闭处理
        .onTouch((event) => {
          if (event.type == TouchType.Down) {
            this.downxy = { x: event.touches[0].windowX, y: event.touches[0].windowY }
          } else if (event.type == TouchType.Move) {
            if (event.touches[0].windowX - (this.downxy.x as number) < 0) {
              if (event.touches[0].windowX - (this.downxy.x as number) <= -60) {
                this.isshow = false
              }
            }
          }
        })
      }

    }
    .width('100%')
    .height('100%')
    .alignContent(Alignment.Start)

  }

5. 用户交互阶段

  1. 搜索功能:

    • 用户输入城市名称

    • 点击搜索按钮

    • 加载对应城市天气

    • 保存搜索记录

     Button('搜索')
                .width('25%')
                .height(40)
                .backgroundColor('#0083b0')
                .onClick(() => {
                  if (this.searchText.trim()) {
                    this.loadweather(this.searchText.trim(), true)
                  } else {
                    promptAction.showToast({ message: '请输入城市名称' })
                  }
                })
    
  2. 历史记录管理:

    • 点击菜单按钮打开侧边栏

    • 显示搜索历史列表

    • 支持编辑模式切换

    • 可以删除单条记录或清空所有记录

 // 左侧抽屉布局
      if (this.isshow) {
        Column() {
          // 顶部标题栏布局
          Row() {
            if (this.searchHistory.length > 0) {
              Text('搜索历史')
                .fontSize(20)
                .fontWeight(FontWeight.Bold)
            }
            Blank()

            if (this.searchHistory.length > 0) {
              Text(this.isEditing ? '完成' : '编辑')
                .fontSize(16)
                .fontColor('#0083b0')
                .onClick(() => {
                  this.isEditing = !this.isEditing
                  // 编辑完成后清除要删除的id
                  if (!this.isEditing) {
                    this.delids.clear()
                  }
                })
            }
          }
          .width('100%')
          .height(50)
          .padding(16)
          .margin({ top: 20 })
          .backgroundColor('#FFFFFF')

          // 搜索历史为空时的显示
          if (this.searchHistory.length === 0) {
            Column() {
              Image($r('app.media.empty_history'))
                .width(80)
                .height(80)
                .margin({ bottom: 16 })

              Text('暂无搜索历史')
                .fontSize(16)
                .fontColor('#999999')
            }
            .width('100%')
            .height('100%')
            .justifyContent(FlexAlign.Center)
          } else {
            // 搜索历史列表布局
            List({ space: 10 }) {
              ForEach(this.searchHistory, (item: SearchHistoryItem) => {
                ListItem() {
                  Row() {
                    Row({ space: 20 }) {
                      Text(item.keyword)
                        .fontSize(16)
                        .fontColor(Color.Black)

                      Text(item.timestamp)
                        .fontSize(14)
                        .fontColor('#555555')
                        .margin({ top: 4 })
                    }.onClick(async () => {
                      await this.loadweather(item.keyword, false)
                      this.isshow = false
                    })

                    .layoutWeight(1)

                    // 编辑模式下显示删除按钮
                    if (this.isEditing) {
                      Image($r('app.media.delete'))
                        .width(20)
                        .height(20)
                        .margin({ left: 16 })
                        .onClick(() => {
                          this.deleteHistoryItem(item.id as number)
                        })
                        .transition(this.effect1)
                    }
                  }
                  .width('100%')
                  .padding(15)
                }
                .translate({ x: this.delids.has(item.id) ? '-100%' : '0%' })
              })
            }
            .width('100%')
            .layoutWeight(1)
            .margin({ top: 10 })
            .divider({ strokeWidth: 2, color: 'rgba(0,0,0,0.4)' })
          }

          // 编辑模式下显示清空按钮
          if (this.searchHistory.length > 0 && this.isEditing) {
            Button('清空历史记录')
              .width('90%')
              .height(40)
              .margin({ bottom: 16 })
              .backgroundColor('#FF4D4F')
              .onClick(() => {
                this.clearHistory()
              })
              .transition(this.effect1)
          }
        }
        .width('60%')
        .height('100%')
        .backgroundColor(Color.White)
        .transition(this.effect)
        .borderRadius({ topRight: 20, bottomRight: 20 })
        .border({ width: 1, color: 'rgba(0,0,100,0.5)' })
        // 抽屉出现时加载搜索历史
        .onAppear(() => {
          let sd = new searchdao(this.context)
          sd.queryall().then((value) => {
            this.searchHistory = value
          }).catch((e: Error) => {
            console.error('gxxt 查询搜索历史失败: ', e.message)
          })
        })
        // 抽屉滑动关闭处理
        .onTouch((event) => {
          if (event.type == TouchType.Down) {
            this.downxy = { x: event.touches[0].windowX, y: event.touches[0].windowY }
          } else if (event.type == TouchType.Move) {
            if (event.touches[0].windowX - (this.downxy.x as number) < 0) {
              if (event.touches[0].windowX - (this.downxy.x as number) <= -60) {
                this.isshow = false
              }
            }
          }
        })
      }

    }
    .width('100%')
    .height('100%')
    .alignContent(Alignment.Start)

5. 数据持久化阶段

  1. 搜索历史保存:

    • 每次搜索后自动保存

    • 记录包含关键词和时间戳

    // 抽屉出现时加载搜索历史
            .onAppear(() => {
              let sd = new searchdao(this.context)
              sd.queryall().then((value) => {
                this.searchHistory = value
              }).catch((e: Error) => {
                console.error('gxxt 查询搜索历史失败: ', e.message)
              })
            })
    ................................
    /**
       * 查询所有搜索历史记录
       * @returns 返回搜索历史记录数组
       */
      async queryall() {
        // 定义存储搜索历史的数组
        let datas: SearchHistoryItem[] = []
        try {
          // 获取数据库实例
          let rdb = await getrdb(this.context)
          // 创建查询条件
          let predicates = new relationalStore.RdbPredicates(TABLENAME1)
          // 执行查询
          let rs = await rdb.query(predicates)
          // 遍历结果集
          while (rs.goToNextRow()) {
            // 将每条记录添加到数组中
            datas.push({
              id: rs.getLong(rs.getColumnIndex('id')),
              keyword: rs.getString(rs.getColumnIndex('keyword')),
              timestamp: rs.getString(rs.getColumnIndex('timestamp')),
            })
          }
        } catch (e) {
          console.error('gxxt searchdao 查询失败: ', JSON.stringify(e))
        }
    
        return datas;
      }
    
  2. 数据更新:

    • 实时更新天气信息

    • 保存用户操作记录

6. 错误处理阶段

  1. 网络错误:

    • 显示错误提示

    • 使用默认数据

  2. 定位失败:

    • 提示用户检查定位设置

    • 使用默认城市

  3. 数据加载失败:

    • 显示错误对话框

    • 提供重试选项

7. 界面更新阶段

  1. 状态变化触发UI更新:

    • 天气数据更新

    • 历史记录变化

    • 编辑模式切换

  2. 动画效果:

    • 侧边栏滑动

    • 加载动画

    • 删除动画

8. 资源释放阶段

  1. 页面关闭时:

    • 清理定时器

    • 保存未保存的数据

    • 释放系统资源

最后:

完整项目:https://download.csdn.net/download/gao_xin_xing/90848357

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值