第12篇:综合实战——制作鸿蒙应用

本课目标:综合运用所学知识,完成一个完整的鸿蒙应用
**作者:**中文编程倡导者—— 李金雨
预计课时:6课时(270分钟)
难度等级:⭐⭐⭐⭐⭐(专家级)


一、开篇引入

1.1 回顾所学知识

经过前面11篇的学习,我们已经掌握了:

篇目 核心技能
第1篇 搭建开发环境,创建第一个应用
第2篇 数据类型,变量和常量
第3篇 界面组件,布局排版
第4篇 状态管理,事件处理
第5篇 列表渲染,数据展示
第6篇 条件渲染,动态界面
第7篇 功能定义,代码封装
第8篇 组件化开发,复用组件
第9篇 页面跳转,多页面应用
第10篇 网络请求,获取数据
第11篇 本地存储,数据持久化

1.2 综合实战目标

今天我们要用所有学到的知识,完成一个完整的应用!

项目选择(三选一):

  1. 待办事项管理应用 - 任务管理
  2. 天气预报应用 - 信息展示
  3. 简易社交应用 - 内容发布

1.3 开发流程

1. 需求分析
   ↓
2. 界面设计(画草图)
   ↓
3. 数据结构定义
   ↓
4. 组件拆分
   ↓
5. 逐个实现功能
   ↓
6. 联调测试
   ↓
7. 优化完善

二、项目A:待办事项管理应用

2.1 需求分析

功能需求:

  • ✅ 添加待办事项
  • ✅ 标记完成/未完成
  • ✅ 删除事项
  • ✅ 本地存储(重启不丢失)
  • ✅ 分类筛选(全部/未完成/已完成)
  • ✅ 优先级设置
  • ✅ 截止日期

页面设计:

首页:
┌─────────────────────────┐
│  📋 待办事项      [+]   │
├─────────────────────────┤
│  [全部] [未完成] [已完成] │
├─────────────────────────┤
│ ☑️ 完成的事项            │
│ ☐ 未完成的事项  🗑️      │
│ ☐ 高优先级事项  📅明天   │
│ ...                     │
│                         │
│ 剩余3项待完成            │
└─────────────────────────┘

添加/编辑页:
┌─────────────────────────┐
│  < 返回      保存       │
├─────────────────────────┤
│ 标题:_______________   │
│                         │
│ 优先级:[高] [中] [低]   │
│                         │
│ 截止日期:2024-01-20    │
│                         │
│ 备注:                 │
│ ┌─────────────────┐    │
│ │                 │    │
│ │                 │    │
│ └─────────────────┘    │
└─────────────────────────┘

2.2 完整代码

import dataPreferences from '@ohos.data.preferences'
import router from '@ohos.router'

// ========== 数据类型定义 ==========

interface 待办事项 {
  id: string
  标题: string
  是否完成: boolean
  优先级: string    // 高、中、低
  截止日期: string
  备注: string
  创建时间: string
}

// ========== 首页组件 ==========

// 完整可运行代码,复制到 Index.ets 即可运行
@Entry
@Component
struct Index {
  @State 待办列表: 待办事项[] = []
  @State 当前筛选: string = "全部"    // 全部、未完成、已完成
  @State 加载中: boolean = true
  
  private 首选项: dataPreferences.Preferences = null
  private readonly 存储键: string = 'todos'

  async aboutToAppear() {
    this.首选项 = await dataPreferences.getPreferences(getContext(this), 'todoApp')
    await this.加载数据()
  }
  
  async 加载数据() {
    this.加载中 = true
    let 数据 = await this.首选项.get(this.存储键, '[]') as string
    this.待办列表 = JSON.parse(数据)
    this.加载中 = false
  }
  
  async 保存数据() {
    await this.首选项.put(this.存储键, JSON.stringify(this.待办列表))
    await this.首选项.flush()
  }

  build() {
    Column() {
      // 标题栏
      this.标题栏()
      
      // 筛选标签
      this.筛选栏()
      
      // 待办列表
      this.待办列表区域()
      
      // 统计信息
      this.统计栏()
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F5F5F5')
  }
  
  // 标题栏
  @Builder
  标题栏() {
    Row() {
      Column({ space: 5 }) {
        Text('📋 待办事项')
          .fontSize(24)
          .fontWeight(FontWeight.Bold)
        
        Text(`${this.未完成数量}项待完成`)
          .fontSize(12)
          .fontColor('#999999')
      }
      .alignItems(HorizontalAlign.Start)
      
      Button('+', { type: ButtonType.Circle })
        .width(50)
        .height(50)
        .fontSize(30)
        .backgroundColor('#4CAF50')
        .onClick(() => {
          router.pushUrl({
            url: 'pages/编辑页',
            params: { 模式: '新建' }
          })
        })
    }
    .width('100%')
    .justifyContent(FlexAlign.SpaceBetween)
    .padding(20)
    .backgroundColor('#FFFFFF')
  }
  
  // 筛选栏
  @Builder
  筛选栏() {
    Row({ space: 15 }) {
      ForEach(['全部', '未完成', '已完成'], (筛选: string) => {
        Text(筛选)
          .fontSize(14)
          .fontColor(this.当前筛选 == 筛选 ? '#FFFFFF' : '#666666')
          .padding({ left: 15, right: 15, top: 6, bottom: 6 })
          .backgroundColor(this.当前筛选 == 筛选 ? '#2196F3' : '#F5F5F5')
          .borderRadius(15)
          .onClick(() => {
            this.当前筛选 = 筛选
          })
      })
    }
    .width('100%')
    .padding(15)
    .backgroundColor('#FFFFFF')
  }
  
  // 待办列表区域
  @Builder
  待办列表区域() {
    if (this.加载中) {
      Column({ space: 10 }) {
        LoadingProgress()
          .width(40)
          .height(40)
        Text('加载中...')
          .fontColor('#999999')
      }
      .layoutWeight(1)
      .justifyContent(FlexAlign.Center)
    } else if (this.过滤后列表.length == 0) {
      Column({ space: 15 }) {
        Text('📭')
          .fontSize(80)
        Text(this.当前筛选 == '全部' ? '还没有待办事项' : `没有${this.当前筛选}的事项`)
          .fontSize(16)
          .fontColor('#999999')
        Text('点击右上角+添加')
          .fontSize(14)
          .fontColor('#CCCCCC')
      }
      .layoutWeight(1)
      .justifyContent(FlexAlign.Center)
    } else {
      List({ space: 10 }) {
        ForEach(this.过滤后列表, (事项: 待办事项) => {
          ListItem() {
            this.待办卡片(事项)
          }
          .swipeAction({
            end: {
              Button('删除')
                .width(80)
                .height('100%')
                .backgroundColor('#F44336')
                .fontColor('#FFFFFF')
                .onClick(() => this.删除事项(事项.id))
            }
          })
        })
      }
      .padding(15)
      .layoutWeight(1)
    }
  }
  
  // 待办卡片
  @Builder
  待办卡片(事项: 待办事项) {
    Row({ space: 12 }) {
      // 完成状态复选框
      Text(事项.是否完成 ? '☑️' : '⬜')
        .fontSize(24)
        .onClick(() => this.切换完成状态(事项.id))
      
      // 内容区域
      Column({ space: 5 }) {
        Row({ space: 8 }) {
          Text(事项.标题 || '无标题')
            .fontSize(16)
            .fontWeight(FontWeight.Medium)
            .fontColor(事项.是否完成 ? '#999999' : '#333333')
            .decoration({
              type: 事项.是否完成 ? TextDecorationType.LineThrough : TextDecorationType.None
            })
            .layoutWeight(1)
          
          // 优先级标签
          Text(事项.优先级)
            .fontSize(10)
            .fontColor('#FFFFFF')
            .padding({ left: 6, right: 6, top: 2, bottom: 2 })
            .backgroundColor(this.获取优先级颜色(事项.优先级))
            .borderRadius(4)
        }
        .width('100%')
        
        // 截止日期
        if (事项.截止日期) {
          Row({ space: 5 }) {
            Text('📅')
              .fontSize(12)
            Text(事项.截止日期)
              .fontSize(12)
              .fontColor(this.是否过期(事项.截止日期) ? '#F44336' : '#999999')
            
            if (this.是否过期(事项.截止日期) && !事项.是否完成) {
              Text('已过期')
                .fontSize(10)
                .fontColor('#F44336')
            }
          }
        }
      }
      .layoutWeight(1)
      .alignItems(HorizontalAlign.Start)
      
      // 编辑按钮
      Text('✏️')
        .fontSize(18)
        .onClick(() => {
          router.pushUrl({
            url: 'pages/编辑页',
            params: { 模式: '编辑', 事项: JSON.stringify(事项) }
          })
        })
    }
    .width('100%')
    .padding(15)
    .backgroundColor('#FFFFFF')
    .borderRadius(12)
    .shadow({ radius: 4, color: '#10000000' })
  }
  
  // 统计栏
  @Builder
  统计栏() {
    Row({ space: 20 }) {
      this.统计项('总任务', this.待办列表.length)
      this.统计项('待完成', this.未完成数量)
      this.统计项('已完成', this.已完成数量)
    }
    .width('100%')
    .justifyContent(FlexAlign.Center)
    .padding(15)
    .backgroundColor('#FFFFFF')
    .border({ width: { top: 1 }, color: '#EEEEEE' })
  }
  
  @Builder
  统计项(标签: string, 数值: number) {
    Column({ space: 5 }) {
      Text(`${数值}`)
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .fontColor('#2196F3')
      
      Text(标签)
        .fontSize(12)
        .fontColor('#999999')
    }
  }
  
  // 计算属性
  get 过滤后列表(): 待办事项[] {
    switch (this.当前筛选) {
      case '未完成':
        return this.待办列表.filter(事项 => !事项.是否完成)
      case '已完成':
        return this.待办列表.filter(事项 => 事项.是否完成)
      default:
        return this.待办列表
    }
  }
  
  get 未完成数量(): number {
    return this.待办列表.filter(事项 => !事项.是否完成).length
  }
  
  get 已完成数量(): number {
    return this.待办列表.filter(事项 => 事项.是否完成).length
  }
  
  // 方法
  async 切换完成状态(id: string) {
    let 索引 = this.待办列表.findIndex(事项 => 事项.id == id)
    if (索引 != -1) {
      this.待办列表[索引].是否完成 = !this.待办列表[索引].是否完成
      this.待办列表 = [...this.待办列表]
      await this.保存数据()
    }
  }
  
  async 删除事项(id: string) {
    this.待办列表 = this.待办列表.filter(事项 => 事项.id != id)
    await this.保存数据()
  }
  
  获取优先级颜色(优先级: string): string {
    switch (优先级) {
      case '高': return '#F44336'
      case '中': return '#FF9800'
      case '低': return '#4CAF50'
      default: return '#9E9E9E'
    }
  }
  
  是否过期(日期字符串: string): boolean {
    let 截止日期 = new Date(日期字符串)
    let 今天 = new Date()
    今天.setHours(0, 0, 0, 0)
    return 截止日期 < 今天
  }
  
  onPageShow() {
    // 从编辑页返回时刷新数据
    this.加载数据()
  }
}

// ========== 编辑页组件 ==========

// 完整可运行代码,复制到 Index.ets 即可运行
@Entry
@Component
struct Index {
  @State 模式: string = '新建'
  @State 标题: string = ''
  @State 优先级: string = '中'
  @State 截止日期: string = ''
  @State 备注: string = ''
  @State 编辑ID: string = ''
  
  private 首选项: dataPreferences.Preferences = null

  async aboutToAppear() {
    this.首选项 = await dataPreferences.getPreferences(getContext(this), 'todoApp')
    
    // 获取传递的参数
    let 参数 = router.getParams()
    if (参数) {
      this.模式 = 参数['模式'] as string
      
      if (this.模式 == '编辑' && 参数['事项']) {
        let 事项 = JSON.parse(参数['事项'] as string)
        this.编辑ID = 事项.id
        this.标题 = 事项.标题
        this.优先级 = 事项.优先级
        this.截止日期 = 事项.截止日期
        this.备注 = 事项.备注
      }
    }
  }

  build() {
    Column({ space: 20 }) {
      // 标题栏
      Row() {
        Text('< 返回')
          .fontSize(16)
          .fontColor('#666666')
          .onClick(() => router.back())
        
        Text(this.模式 == '新建' ? '新建待办' : '编辑待办')
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
        
        Text('保存')
          .fontSize(16)
          .fontColor(this.可以保存 ? '#4CAF50' : '#CCCCCC')
          .onClick(() => {
            if (this.可以保存) {
              this.保存()
            }
          })
      }
      .width('100%')
      .justifyContent(FlexAlign.SpaceBetween)
      .padding(20)
      .backgroundColor('#FFFFFF')
      
      // 表单内容
      Scroll() {
        Column({ space: 20 }) {
          // 标题输入
          this.表单项('标题') {
            TextInput({ placeholder: '请输入标题', text: this.标题 })
              .width('100%')
              .height(50)
              .backgroundColor('#F5F5F5')
              .borderRadius(8)
              .onChange(() => this.标题 =)
          }
          
          // 优先级选择
          this.表单项('优先级') {
            Row({ space: 10 }) {
              ForEach(['高', '中', '低'], (优先级: string) => {
                Text(优先级)
                  .fontSize(14)
                  .fontColor(this.优先级 == 优先级 ? '#FFFFFF' : '#666666')
                  .padding({ left: 20, right: 20, top: 8, bottom: 8 })
                  .backgroundColor(
                    this.优先级 == 优先级 
                      ? this.获取优先级颜色(优先级) 
                      : '#F5F5F5'
                  )
                  .borderRadius(20)
                  .onClick(() => this.优先级 = 优先级)
              })
            }
          }
          
          // 截止日期
          this.表单项('截止日期') {
            TextInput({ placeholder: '选择日期,如:2024-01-20', text: this.截止日期 })
              .width('100%')
              .height(50)
              .backgroundColor('#F5F5F5')
              .borderRadius(8)
              .onChange(() => this.截止日期 =)
          }
          
          // 备注
          this.表单项('备注') {
            TextArea({ placeholder: '添加备注...', text: this.备注 })
              .width('100%')
              .height(150)
              .backgroundColor('#F5F5F5')
              .borderRadius(8)
              .onChange(() => this.备注 =)
          }
        }
        .width('100%')
        .padding(20)
      }
      .layoutWeight(1)
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#FFFFFF')
  }
  
  @Builder
  表单项(标签: string, 内容: () => void) {
    Column({ space: 8 }) {
      Text(标签)
        .fontSize(14)
        .fontColor('#666666')
        .alignSelf(ItemAlign.Start)
      
      content()
    }
    .width('100%')
  }
  
  get 可以保存(): boolean {
    return this.标题.trim() != ''
  }
  
  获取优先级颜色(优先级: string): string {
    switch (优先级) {
      case '高': return '#F44336'
      case '中': return '#FF9800'
      case '低': return '#4CAF50'
      default: return '#9E9E9E'
    }
  }
  
  async 保存() {
    let 列表数据 = await this.首选项.get('todos', '[]') as string
    let 列表: 待办事项[] = JSON.parse(列表数据)
    
    if (this.模式 == '新建') {
      // 添加新事项
      let 新事项: 待办事项 = {
        id: Date.now().toString(),
        标题: this.标题.trim(),
        是否完成: false,
        优先级: this.优先级,
        截止日期: this.截止日期,
        备注: this.备注,
        创建时间: new Date().toISOString()
      }
      列表.unshift(新事项)
    } else {
      // 更新现有事项
      let 索引 = 列表.findIndex(事项 => 事项.id == this.编辑ID)
      if (索引 != -1) {
        列表[索引].标题 = this.标题.trim()
        列表[索引].优先级 = this.优先级
        列表[索引].截止日期 = this.截止日期
        列表[索引].备注 = this.备注
      }
    }
    
    await this.首选项.put('todos', JSON.stringify(列表))
    await this.首选项.flush()
    
    router.back()
  }
}

三、项目总结

3.1 用到的知识点

知识点 应用场景
@State 管理待办列表、筛选状态
@Component 封装待办卡片、统计项
List/ForEach 展示待办列表
if-else 根据筛选条件显示不同内容
本地存储 保存待办数据
页面跳转 首页和编辑页之间跳转
计算属性 统计待完成/已完成数量
滑动删除 左滑删除待办

3.2 项目亮点

  1. 完整的数据流:添加→存储→读取→展示→编辑→更新
  2. 友好的交互:滑动删除、优先级标签、过期提醒
  3. 数据持久化:重启应用数据不丢失
  4. 筛选功能:快速查看不同状态的事项
  5. 统计信息:一目了然了解任务完成情况

3.3 扩展建议

可以尝试添加以下功能:

  • 🔍 搜索功能
  • 🏷️ 标签分类
  • 🔔 提醒通知
  • 📊 完成统计图表
  • ☁️ 云端同步

四、课程总结

4.1 学习路径回顾

入门阶段(1-3篇)
├── 搭建环境
├── 学习基础语法
└── 掌握界面组件

进阶阶段(4-7篇)
├── 状态管理
├── 列表渲染
├── 条件渲染
└── 功能封装

高级阶段(8-11篇)
├── 组件化开发
├── 页面路由
├── 网络请求
└── 本地存储

实战阶段(12篇)
└── 综合项目

4.2 核心技能清单

基础技能:

  • ✅ 搭建鸿蒙开发环境
  • ✅ 使用ArkTS编写代码
  • ✅ 使用中文命名变量、组件
  • ✅ 理解数据类型和变量

界面开发:

  • ✅ 使用基础组件(Text、Button、Image等)
  • ✅ 使用布局容器(Column、Row、List等)
  • ✅ 设置样式(颜色、大小、间距等)
  • ✅ 响应式界面(状态管理)

数据处理:

  • ✅ 定义和使用数据类型
  • ✅ 列表渲染和条件渲染
  • ✅ 封装功能函数
  • ✅ 创建可复用组件

高级功能:

  • ✅ 多页面应用开发
  • ✅ 页面间跳转和传参
  • ✅ 网络请求获取数据
  • ✅ 本地数据存储

4.3 继续学习建议

深入学习:

  1. 鸿蒙官方文档:https://developer.harmonyos.com/
  2. ArkTS高级特性
  3. 动画和过渡效果
  4. 性能优化技巧

实践项目:

  1. 个人博客应用
  2. 在线商城应用
  3. 社交聊天应用
  4. 游戏应用

参与社区:

  1. 加入鸿蒙开发者社区
  2. 参与开源项目
  3. 分享学习心得

五、写在最后

恭喜你完成了全部12篇教程的学习!🎉

从搭建环境到完成实战项目,你已经掌握了鸿蒙应用开发的核心技能。

记住:

  • 编程是一门实践性很强的技能,多写代码才能提高
  • 遇到问题不要怕,学会查阅文档和搜索解决方案
  • 保持好奇心,不断探索新技术

未来可期:

  • 你可以开发自己的鸿蒙应用
  • 你可以参加鸿蒙开发者大赛
  • 你可以成为鸿蒙生态的贡献者

让我们开始你的鸿蒙开发之旅吧! 🚀


本教程由李金雨编写
联系邮箱:wbtm2718@qq.com

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐