HarmonyOS Next 项目完整学习指南

HarmonyOS Next 项目完整学习指南

基于 HealthyMate(泰科健康)项目的实战学习文档

适合人群: 完成 HarmonyOS Next 基础学习的初学者

学习目标: 通过真实项目巩固基础知识,掌握实际开发技能


📚 目录


第一章:项目概览

1.1 项目简介

**HealthyMate(泰科健康)**是一款基于 HarmonyOS Next 开发的综合性健康管理应用。

核心功能模块:

  • 🏠 首页模块: 健康检测、医院查询、热点资讯
  • 🔍 健康发现: 健康知识、视频播放、管理工具
  • 📊 健康数据: 数据展示、趋势分析、图表可视化
  • 👤 个人中心: 用户信息、设置管理

技术栈:

复制代码
开发框架: HarmonyOS Next
开发语言: ArkTS (TypeScript 扩展)
UI框架: ArkUI (声明式UI)
开发工具: DevEco Studio
包管理: ohpm

1.2 学习路线图

复制代码
启动流程 → UI组件 → 状态管理 → 路由导航 → 数据交互 → 高级特性
   ↓         ↓         ↓          ↓          ↓         ↓
 Entry    @Component  @State    Navigation   Model    第三方库
 Ability   @Builder   AppStorage  router     Class    mpchart

第二章:核心概念速览

2.1 ArkTS 基础概念

2.1.1 装饰器系统

ArkTS 使用装饰器来增强类、组件和变量的功能:

装饰器 作用 使用场景 示例
@Entry 标记页面入口 可独立运行的页面 启动页、登录页
@Component 定义自定义组件 可复用的UI组件 卡片、按钮
@State 状态变量 组件内部状态 计数器、开关
@Prop 单向数据传递 父组件→子组件 配置参数
@Link 双向数据绑定 父子组件共享 表单数据
@Builder 自定义构建函数 动态UI内容 插槽内容
@BuilderParam 插槽参数 接收UI内容 卡片插槽
@Extend 扩展组件样式 统一样式 文本样式
@Preview 预览组件 开发调试 组件预览
2.1.2 核心组件

容器组件:

typescript 复制代码
Column()    // 垂直布局
Row()       // 水平布局
Stack()     // 堆叠布局
Flex()      // 弹性布局
Grid()      // 网格布局
List()      // 列表布局
Swiper()    // 轮播容器
Tabs()      // 标签页容器

基础组件:

typescript 复制代码
Text()      // 文本
Image()     // 图片
Button()    // 按钮
TextInput() // 文本输入

2.2 项目架构概念

2.2.1 应用生命周期
复制代码
应用启动
    ↓
EntryAbility.onCreate()     // Ability 创建
    ↓
onWindowStageCreate()       // 窗口创建
    ↓
loadContent('pages/SplashPage')  // 加载启动页
    ↓
页面显示
    ↓
aboutToAppear()            // 页面即将显示
    ↓
build()                    // 构建UI
    ↓
onPageShow()              // 页面已显示
2.2.2 组件生命周期
typescript 复制代码
@Component
struct MyComponent {
  // 1. 组件即将创建
  aboutToAppear() {
    console.log('组件即将出现')
    // 初始化数据、启动定时器
  }
  
  // 2. 构建UI(每次状态变化都会触发)
  build() {
    Column() {
      Text('Hello')
    }
  }
  
  // 3. 组件即将销毁
  aboutToDisappear() {
    console.log('组件即将消失')
    // 清理定时器、释放资源
  }
}

第三章:项目结构详解

3.1 目录树结构

复制代码
HealthyMate/
├── AppScope/                    # 应用全局配置
│   ├── app.json5               # 应用配置文件
│   └── resources/              # 全局资源
│
├── entry/                      # 主模块
│   ├── src/main/
│   │   ├── ets/               # ArkTS 源代码
│   │   │   ├── entryability/  # 应用入口
│   │   │   │   └── EntryAbility.ets
│   │   │   │
│   │   │   ├── pages/         # 页面文件
│   │   │   │   ├── Index.ets          # 主页面
│   │   │   │   ├── SplashPage.ets     # 启动页
│   │   │   │   ├── GuidePage.ets      # 引导页
│   │   │   │   ├── LoginPage.ets      # 登录页
│   │   │   │   │
│   │   │   │   ├── comp/              # 公共组件
│   │   │   │   │   ├── HomeComp.ets       # 首页组件
│   │   │   │   │   ├── CardComp.ets       # 卡片组件
│   │   │   │   │   ├── TopNavComp.ets     # 导航栏
│   │   │   │   │   └── ...
│   │   │   │   │
│   │   │   │   ├── model/             # 数据模型
│   │   │   │   │   ├── NavCard.ets        # 导航卡片模型
│   │   │   │   │   └── HealthyData.ets    # 健康数据模型
│   │   │   │   │
│   │   │   │   ├── view/              # 视图组件
│   │   │   │   │   ├── LineCharts.ets     # 折线图
│   │   │   │   │   └── BarCharts.ets      # 柱状图
│   │   │   │   │
│   │   │   │   ├── home/              # 首页相关页面
│   │   │   │   ├── healthydiscover/   # 健康发现页面
│   │   │   │   └── profile/           # 个人中心页面
│   │   │   │
│   │   │   └── utils/         # 工具类
│   │   │
│   │   ├── resources/         # 资源文件
│   │   │   ├── base/          # 基础资源
│   │   │   │   ├── element/       # 元素资源
│   │   │   │   │   ├── string.json    # 字符串资源
│   │   │   │   │   ├── color.json     # 颜色资源
│   │   │   │   │   └── float.json     # 尺寸资源
│   │   │   │   ├── media/         # 媒体资源(图片等)
│   │   │   │   └── profile/       # 配置文件
│   │   │   │       └── main_pages.json
│   │   │   │       └── route_map.json
│   │   │   ├── zh_CN/         # 中文资源
│   │   │   └── en_US/         # 英文资源
│   │   │
│   │   └── module.json5       # 模块配置文件
│   │
│   └── build-profile.json5    # 构建配置
│
├── oh_modules/                # 第三方依赖
│   └── @ohos/
│       └── mpchart/          # 图表库
│
├── oh-package.json5          # 包配置文件
└── hvigorfile.ts            # 构建脚本

3.2 关键文件说明

3.2.1 module.json5 - 模块配置
json5 复制代码
{
  "module": {
    "name": "entry",
    "type": "entry",
    "routerMap": "$profile:route_map",
    "mainElement": "EntryAbility",
    "pages": "$profile:main_pages",
    
    // 权限声明
    "requestPermissions": [
      {
        "name": "ohos.permission.INTERNET"  // 网络权限
      },
      {
        "name": "ohos.permission.LOCATION"  // 位置权限
      }
    ]
  }
}

重要配置项:

  • mainElement: 指定应用入口 Ability
  • pages: 页面配置文件路径
  • routerMap: 路由配置文件路径
  • requestPermissions: 权限声明列表
3.2.2 main_pages.json - 页面配置
json 复制代码
{
  "src": [
    "pages/SplashPage",
    "pages/GuidePage",
    "pages/Index",
    "pages/LoginPage"
  ]
}
3.2.3 route_map.json - 路由配置
json 复制代码
{
  "routerMap": [
    {
      "name": "HealthCheckPage",
      "pageSourceFile": "src/main/ets/pages/home/HealthCheckPage.ets"
    }
  ]
}

第四章:从零开始 - 启动流程

4.1 应用入口:EntryAbility

文件位置 : entry/src/main/ets/entryability/EntryAbility.ets

typescript 复制代码
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';

export default class EntryAbility extends UIAbility {
  
  // 1. Ability 创建时调用
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    console.log('应用创建')
  }

  // 2. 窗口阶段创建
  onWindowStageCreate(windowStage: window.WindowStage): void {
    // 加载启动页
    windowStage.loadContent('pages/SplashPage', (err) => {
      if (err.code) {
        console.error('加载页面失败:', err)
        return;
      }
      console.log('页面加载成功')
    });
  }

  // 3. 前台
  onForeground(): void {
    console.log('应用进入前台')
  }

  // 4. 后台
  onBackground(): void {
    console.log('应用进入后台')
  }

  // 5. 销毁
  onDestroy(): void {
    console.log('应用销毁')
  }
}

知识点总结:

  • UIAbility 是应用/服务的基类
  • onWindowStageCreate 中加载首页
  • ✅ 通过 loadContent 指定启动页面

4.2 启动页实现:SplashPage

文件位置 : entry/src/main/ets/pages/SplashPage.ets

typescript 复制代码
import { router } from "@kit.ArkUI";

@Entry
@Component
struct SplashPage {
  @State countDown: number = 5        // 倒计时秒数
  @State isAutoJump: boolean = true   // 是否自动跳转
  private timerID: number = 0         // 定时器ID

  // 页面显示时启动定时器
  aboutToAppear() {
    this.startCountdown()
  }

  // 页面隐藏时清除定时器
  aboutToDisappear() {
    clearInterval(this.timerID)
  }

  // 启动倒计时
  private startCountdown() {
    this.timerID = setInterval(() => {
      if (this.countDown > 0) {
        this.countDown -= 1          // 每秒减1
      } else {
        clearInterval(this.timerID)  // 清除定时器
        this.jumpToHome()            // 跳转
      }
    }, 1000)
  }

  // 手动跳过
  private skipGuide() {
    clearInterval(this.timerID)
    this.jumpToHome()
  }

  // 跳转到引导页
  private jumpToHome() {
    router.pushUrl({ url: 'pages/GuidePage' })
  }

  build() {
    Stack({ alignContent: Alignment.TopEnd }) {
      // 主内容区域
      Column() {
        Column({ space: 12 }) {
          Image($r("app.media.icon_app"))
            .width(140)
            .height(160)
          
          Text('泰克健康')
            .fontSize(38)
            .fontColor('#000000')
            .margin({ top: 10 })
          
          Text('--- 安全守护 ---')
            .fontSize(38)
            .fontColor('#A5ABCE')
        }
        .width(238)
        .height(328)
      }
      .justifyContent(FlexAlign.Center)
      .height('100%')
      .width('100%')

      // 右上角跳过按钮
      Button() {
        Text('跳过 ' + this.countDown.toString())
          .fontSize(18)
          .fontColor('#FFFFFF')
      }
      .margin({ top: 20, right: 12 })
      .padding({ left: 10, top: 5, right: 10, bottom: 5 })
      .borderRadius(15)
      .backgroundColor('#33000000')
      .onClick(() => {
        this.skipGuide()
      })
    }
  }
}

知识点详解:

4.2.1 装饰器用法
typescript 复制代码
@Entry        // 标记为页面入口,可以独立运行
@Component    // 标记为自定义组件
struct SplashPage { }
4.2.2 状态管理
typescript 复制代码
@State countDown: number = 5
// @State 装饰的变量改变时,会自动触发UI刷新
4.2.3 生命周期钩子
typescript 复制代码
aboutToAppear()      // 组件即将出现(页面加载后)
aboutToDisappear()   // 组件即将消失(页面销毁前)
4.2.4 定时器使用
typescript 复制代码
// 启动定时器
this.timerID = setInterval(() => {
  // 每秒执行
}, 1000)

// 清除定时器(重要!防止内存泄漏)
clearInterval(this.timerID)
4.2.5 Stack 布局
typescript 复制代码
Stack({ alignContent: Alignment.TopEnd }) {
  // 子组件会堆叠在一起
  Column() { }  // 第一层:主内容
  Button() { }  // 第二层:浮动按钮
}

Stack 对齐方式:

  • Alignment.TopStart - 左上角
  • Alignment.TopEnd - 右上角
  • Alignment.Center - 居中
  • Alignment.BottomEnd - 右下角

4.3 引导页实现:GuidePage

文件位置 : entry/src/main/ets/pages/GuidePage.ets

typescript 复制代码
import GuideComp from './comp/GuideComp'
import { router } from '@kit.ArkUI'

// 引导页数据接口
interface GuideItem {
  currentIndex: number
  guideText1: string
  guideText2: string
  guideImage: ResourceStr
  isDisplayBtn?: boolean
}

// 引导页数据
const guideArrays: GuideItem[] = [
  {
    currentIndex: 0,
    guideText1: '智能检测',
    guideText2: '让健康生活触手可及',
    guideImage: $r('app.media.guide01')
  },
  {
    currentIndex: 1,
    guideText1: '按时服药,健康相伴',
    guideText2: '您的智能健康提醒专家',
    guideImage: $r('app.media.guide02')
  },
  {
    currentIndex: 2,
    guideText1: '您的个人健康档案',
    guideText2: '一目了然',
    guideImage: $r('app.media.guide03'),
    isDisplayBtn: true  // 最后一页显示按钮
  }
]

@Entry
@Component
struct GuidePage {
  @State currentIndex: number = 0
  @State guideList: GuideItem[] = guideArrays

  build() {
    Swiper() {
      ForEach(this.guideList, (item: GuideItem, index) => {
        Column() {
          // 引导内容组件
          GuideComp({
            currentIndex: item.currentIndex,
            guideText1: item.guideText1,
            guideText2: item.guideText2,
            guideImage: item.guideImage
          })
          
          // 最后一页显示按钮
          if (index === this.guideList.length - 1) {
            Button('立即开启', { type: ButtonType.Normal })
              .onClick(() => {
                router.pushUrl({ url: 'pages/RegisterPage' })
              })
              .padding({ top: 12, bottom: 12 })
              .width('88%')
              .borderRadius(12)
              .linearGradient({
                angle: 180,
                colors: [
                  [0x9AE0FF, 0.0],
                  [0x526AF3, 1.0]
                ]
              })
          }
        }
        .width('100%')
        .height('100%')
      })
    }
    .loop(false)                    // 不循环
    .autoPlay(false)                // 不自动播放
    .cachedCount(3)                 // 缓存页面数
    .disableSwipe(this.currentIndex === this.guideList.length - 1)
    .indicator(                     // 指示器样式
      Indicator.dot()
        .itemWidth(15)
        .itemHeight(15)
        .selectedItemWidth(30)
        .selectedItemHeight(15)
        .selectedColor(0x5F80F5)
    )
    .onChange((index: number) => {
      this.currentIndex = index     // 更新当前索引
    })
  }
}

知识点详解:

4.3.1 Swiper 轮播组件
typescript 复制代码
Swiper() {
  // 子组件
}
.loop(false)           // 是否循环
.autoPlay(false)       // 是否自动播放
.indicator()           // 指示器
.onChange()            // 页面切换回调
4.3.2 ForEach 列表渲染
typescript 复制代码
ForEach(
  arr: Array<T>,                    // 数据源
  itemGenerator: (item: T, index: number) => void,  // 渲染函数
  keyGenerator?: (item: T, index: number) => string // 键生成函数
)

// 示例
ForEach(this.guideList, (item: GuideItem, index) => {
  Text(item.guideText1)
}, (item: GuideItem) => item.currentIndex.toString())
4.3.3 条件渲染
typescript 复制代码
if (condition) {
  // 条件为真时显示
  Button('显示')
}
4.3.4 渐变效果
typescript 复制代码
.linearGradient({
  angle: 180,          // 渐变角度
  colors: [
    [Color1, 0.0],    // 起始颜色和位置
    [Color2, 1.0]     // 结束颜色和位置
  ]
})

第五章:UI组件开发

5.1 主页面结构:Index

文件位置 : entry/src/main/ets/pages/Index.ets

typescript 复制代码
import HomeComp from './comp/HomeComp'
import DiscoverComp from './comp/DiscoverComp'
import DataComp from './comp/DataComp'
import ProfileComp from './comp/ProfileComp'

// 设置全局状态
AppStorage.setOrCreate('CurrentTabIndex', 0);
AppStorage.setOrCreate('IsLoggedIn', false);

@Entry
@Component
struct Index {
  // 创建路由栈
  pathStack: NavPathStack = new NavPathStack();
  
  aboutToAppear(): void {
    // 将路由栈存储到全局
    AppStorage.setOrCreate("pathStack", this.pathStack);
  }

  @State currentIndex: number = 0

  // 底部导航图标
  private navIcons = [
    $r('app.media.icon_home_svg'),
    $r('app.media.icon_discover_svg'),
    $r('app.media.icon_data_svg'),
    $r('app.media.icon_profile_svg')
  ]

  build() {
    Navigation(this.pathStack) {
      Tabs({ barPosition: BarPosition.End }) {
        TabContent() {
          HomeComp()
        }.tabBar(this.tabBarBuilder('首页', 0))

        TabContent() {
          DiscoverComp()
        }.tabBar(this.tabBarBuilder('健康发现', 1))

        TabContent() {
          DataComp()
        }.tabBar(this.tabBarBuilder('健康数据', 2))

        TabContent() {
          ProfileComp()
        }.tabBar(this.tabBarBuilder('个人中心', 3))
      }
      .onChange((index: number) => {
        this.currentIndex = index
      })
    }
    .mode(NavigationMode.Stack)
    .hideTitleBar(true)
  }

  // 自定义 TabBar 构建函数
  @Builder
  tabBarBuilder(name: string, index: number) {
    Column() {
      Image(this.navIcons[index])
        .fillColor(this.currentIndex === index ? '#4D91FF' : '#A9B7D8')
        .width(25)
        .height(25)
      
      Text(name)
        .fontSize(14)
        .fontColor(this.currentIndex === index ? '#677DF7' : '#A9B7D8')
    }
  }
}

知识点详解:

5.1.1 Tabs 标签页组件
typescript 复制代码
Tabs({ barPosition: BarPosition.End }) {  // 底部标签栏
  TabContent() {
    // 页面内容
  }.tabBar(自定义TabBar)
}
.onChange((index: number) => {
  // 标签切换回调
})

BarPosition 枚举:

  • BarPosition.Start - 顶部
  • BarPosition.End - 底部
typescript 复制代码
Navigation(pathStack) {
  // 页面内容
}
.mode(NavigationMode.Stack)  // 堆栈模式
.hideTitleBar(true)          // 隐藏标题栏
5.1.3 @Builder 自定义构建函数
typescript 复制代码
@Builder
tabBarBuilder(name: string, index: number) {
  Column() {
    Image(...)
    Text(name)
  }
}

// 使用
.tabBar(this.tabBarBuilder('首页', 0))

@Builder 要点:

  • ✅ 可以接收参数
  • ✅ 可以在 build() 方法外定义
  • ✅ 可以复用UI结构

5.2 自定义组件:CardComp

文件位置 : entry/src/main/ets/pages/comp/CardComp.ets

typescript 复制代码
class NavItem {
  img: ResourceStr
  title: string
  subtitle?: string

  constructor(img: ResourceStr, title: string, subtitle?: string) {
    this.img = img
    this.title = title
    this.subtitle = subtitle
  }
}

@Component
export default struct CardComp {
  // 组件属性
  wid: number | string = '100%'
  gradientStart?: string
  gradientEnd?: string
  paddingValue?: Padding | Length | LocalizedPadding = 12
  
  // 接收父组件传递的数据
  @Prop params: NavItem
  
  // 插槽参数:接收父组件传递的UI构建函数
  @BuilderParam contentBuilder: (params: NavItem) => void = initBuilder

  build() {
    Column() {
      // 调用传入的构建函数
      this.contentBuilder(this.params)
    }
    .borderRadius(12)
    .padding(this.paddingValue)
    .width(this.wid)
    .linearGradient(
      this.gradientStart && this.gradientEnd ? {
        direction: GradientDirection.Right,
        colors: [
          [this.gradientStart, 0.0],
          [this.gradientEnd, 1.0]
        ]
      } : { colors: [] }
    )
    .backgroundColor(
      this.gradientStart ? Color.Transparent : '#FFFFFF'
    )
  }
}

// 默认构建函数
@Builder
function initBuilder() {
  Text('默认占位符')
}

使用示例:

typescript 复制代码
// 父组件中使用
CardComp({
  wid: '100%',
  gradientStart: '#CCF2FF',
  gradientEnd: '#FFFFFF',
  paddingValue: 5,
  params: myNavItem,           // 传递数据
  contentBuilder: itemContent  // 传递UI构建函数
})

// 自定义内容构建函数
@Builder
function itemContent(param: NavItem) {
  Row({ space: 2 }) {
    Image(param.img).width(44).height(44)
    Text(param.title).fontSize(14)
  }.width('100%')
}

知识点详解:

5.2.1 @Prop 单向数据传递
typescript 复制代码
@Prop params: NavItem

// 特点:
// ✅ 父组件 → 子组件 单向传递
// ✅ 子组件修改不影响父组件
// ✅ 支持深拷贝
5.2.2 @BuilderParam 插槽
typescript 复制代码
@BuilderParam contentBuilder: (params: NavItem) => void = defaultBuilder

// 特点:
// ✅ 允许父组件自定义子组件的部分UI
// ✅ 类似 Vue 的 slot、React 的 children
// ✅ 可以传递参数

@BuilderParam 传参规则:

typescript 复制代码
// ❌ 错误:直接调用
contentBuilder: this.myBuilder()

// ✅ 正确:传递函数引用
contentBuilder: this.myBuilder

// ✅ 正确:传递全局Builder
contentBuilder: globalBuilder
5.2.3 条件样式
typescript 复制代码
.linearGradient(
  condition ? styleA : styleB
)

// 示例
.backgroundColor(
  this.gradientStart ? Color.Transparent : '#FFFFFF'
)

5.3 复合组件:HomeComp

文件位置 : entry/src/main/ets/pages/comp/HomeComp.ets

typescript 复制代码
import TopNavComp from '../comp/TopNavComp'
import CardComp from '../comp/CardComp'
import { NavItem, navItems, lifeReminder, checkUp } from '../model/NavCard'

@Component
export default struct HomeComp {
  // 获取全局路由栈
  pathStack: NavPathStack = AppStorage.get("pathStack") as NavPathStack;

  build() {
    NavDestination() {
      Column() {
        // 1. 顶部导航栏
        TopNavComp({ title: '泰克健康' })

        Column() {
          // 2. 功能网格
          Grid() {
            ForEach(navItems, (item: NavItem, index) => {
              GridItem() {
                CardComp({
                  wid: '50%',
                  params: item,
                  contentBuilder: navItemContent
                })
              }
              .onClick(() => {
                this.pathStack.pushPathByName(item.url, null)
              })
            })
          }
          .rowsTemplate('1fr 1fr')    // 2行
          .rowsGap(5)
          .columnsGap('2%')
          .height('24%')

          // 3. 每日签到区域
          Text('每日签到')
            .fontSize(21)
            .fontWeight(FontWeight.Bold)
          
          Column() {
            Stack({ alignContent: Alignment.Center }) {
              // 背景层
              Column()
                .width('100%')
                .height('100%')
                .borderRadius(20)
                .backgroundImage($r('app.media.bg'))
                .backgroundImageSize(ImageSize.FILL)

              // 内容层
              Column() {
                Text('每日签到,开启健康每一天')
                  .fontSize(18)
                  .fontColor('#ff3952a5')
                
                // 星期签到
                Row() {
                  Flex({ direction: FlexDirection.Row }) {
                    ForEach(['一', '二', '三', '四', '五', '六', '日'], 
                      (item: string) => {
                      Column({ space: 10 }) {
                        Text(`周${item}`)
                        Image($r('app.media.icon_true_green'))
                          .width(27)
                          .aspectRatio(1)
                      }
                      .flexGrow(1)
                    })
                  }
                }
                .padding(10)

                Button('点击签到')
                  .margin(10)
              }
              .width('100%')
              .height('100%')
              .padding(12)
              .borderRadius(20)
              .backgroundImage($r('app.media.home_meiriqiaodao2'))

              // 装饰图片
              Image($r('app.media.home_v'))
                .width(75)
                .height(95)
                .position({ x: '75%', y: -55 })
            }
          }
          .height('30%')

          // 4. 生活提醒
          Column() {
            Text('生活提醒')
              .fontSize(21)
              .fontWeight(FontWeight.Bold)
            
            CardComp({
              gradientStart: '#FFEDE4',
              gradientEnd: '#FFFFFF',
              paddingValue: 5,
              wid: '100%',
              params: lifeReminder,
              contentBuilder: itemContent
            })
          }

          // 5. 体检预约
          Column() {
            Text('体检预约')
              .fontSize(21)
              .fontWeight(FontWeight.Bold)
            
            CardComp({
              gradientStart: '#CCF2FF',
              gradientEnd: '#FFFFFF',
              paddingValue: 5,
              wid: '100%',
              params: checkUp,
              contentBuilder: itemContent
            })
          }
          .onClick(() => {
            this.pathStack.pushPathByName('HealthCheckPage', null)
          })
        }
        .padding($r('app.float.global_padding_or_margin'))
      }
      .width('100%')
      .height('100%')
      .backgroundImage($r("app.media.bg"))
      .backgroundImageSize(ImageSize.Cover)
    }
  }
}

// 导航卡片内容
@Builder
function navItemContent(param: NavItem) {
  Row({ space: 2 }) {
    Image(param.img).width(55).height(55)
    Column({ space: 5 }) {
      Text(param.title)
        .fontSize(21)
        .fontWeight(FontWeight.Bold)
      Text(param.subtitle)
        .fontSize(14)
        .fontColor(Color.Gray)
    }
    .alignItems(HorizontalAlign.Start)
  }
}

// 简单卡片内容
@Builder
function itemContent(param: NavItem) {
  Row({ space: 2 }) {
    Image(param.img).width(44).height(44)
    Text(param.title).fontSize(14)
  }
  .width('100%')
}

知识点详解:

5.3.1 Grid 网格布局
typescript 复制代码
Grid() {
  ForEach(items, (item) => {
    GridItem() {
      // 网格项内容
    }
  })
}
.rowsTemplate('1fr 1fr')    // 2行,平分高度
.columnsTemplate('1fr 1fr 1fr')  // 3列,平分宽度
.rowsGap(10)                // 行间距
.columnsGap(10)             // 列间距

模板语法:

  • 1fr - 弹性单位(平分剩余空间)
  • 100px - 固定像素
  • 1fr 2fr - 按比例分配(1:2)
5.3.2 Flex 弹性布局
typescript 复制代码
Flex({ 
  direction: FlexDirection.Row,  // 方向
  justifyContent: FlexAlign.SpaceBetween,  // 主轴对齐
  alignItems: ItemAlign.Center   // 交叉轴对齐
}) {
  // 子组件
}

FlexDirection 枚举:

  • Row - 水平(从左到右)
  • RowReverse - 水平(从右到左)
  • Column - 垂直(从上到下)
  • ColumnReverse - 垂直(从下到上)
5.3.3 flexGrow 属性
typescript 复制代码
Column() {
  // 内容
}
.flexGrow(1)  // 占据剩余空间
5.3.4 position 绝对定位
typescript 复制代码
Image($r('app.media.icon'))
  .width(75)
  .height(95)
  .position({ x: '75%', y: -55 })  // 绝对定位

第六章:状态管理

6.1 组件内部状态:@State

typescript 复制代码
@Component
struct Counter {
  @State count: number = 0  // 状态变量

  build() {
    Column() {
      Text(`计数: ${this.count}`)
      
      Button('增加')
        .onClick(() => {
          this.count++  // 修改状态,自动刷新UI
        })
    }
  }
}

@State 特点:

  • ✅ 状态改变自动刷新UI
  • ✅ 仅在当前组件内有效
  • ✅ 支持基本类型和对象类型
6.2.1 @Prop - 单向传递
typescript 复制代码
// 父组件
@Entry
@Component
struct Parent {
  @State message: string = 'Hello'

  build() {
    Column() {
      Child({ msg: this.message })
    }
  }
}

// 子组件
@Component
struct Child {
  @Prop msg: string  // 接收父组件数据

  build() {
    Text(this.msg)
  }
}

@Prop 特点:

  • ✅ 单向数据流:父 → 子
  • ✅ 子组件修改不影响父组件
  • ✅ 适合配置型数据
typescript 复制代码
// 父组件
@Entry
@Component
struct Parent {
  @State count: number = 0

  build() {
    Column() {
      Text(`父组件: ${this.count}`)
      Child({ count: $count })  // 使用 $ 传递引用
    }
  }
}

// 子组件
@Component
struct Child {
  @Link count: number  // 双向绑定

  build() {
    Column() {
      Text(`子组件: ${this.count}`)
      Button('增加')
        .onClick(() => {
          this.count++  // 修改会同步到父组件
        })
    }
  }
}

@Link 特点:

  • ✅ 双向数据流:父 ↔ 子
  • ✅ 子组件修改同步父组件
  • ✅ 使用 $ 传递引用

6.3 全局状态:AppStorage

typescript 复制代码
// 设置全局状态
AppStorage.setOrCreate('CurrentTabIndex', 0)
AppStorage.setOrCreate('IsLoggedIn', false)
AppStorage.setOrCreate('pathStack', this.pathStack)

// 获取全局状态
let tabIndex = AppStorage.get<number>('CurrentTabIndex')
let pathStack = AppStorage.get("pathStack") as NavPathStack

// 使用 @StorageLink 装饰器
@Component
struct MyComponent {
  @StorageLink('CurrentTabIndex') tabIndex: number = 0

  build() {
    Text(`当前标签: ${this.tabIndex}`)
  }
}

AppStorage 使用场景:

  • ✅ 跨页面共享数据
  • ✅ 全局配置信息
  • ✅ 用户登录状态
  • ✅ 路由栈共享

6.4 状态管理最佳实践

typescript 复制代码
// ✅ 推荐:合理使用状态
@State isLoading: boolean = false
@State userInfo: UserInfo | null = null
@State errorMsg: string = ''

// ❌ 避免:不必要的状态
// 可以通过计算得到的值不需要状态
@State fullName: string = ''  // 不推荐

// ✅ 推荐:使用计算属性
get fullName(): string {
  return this.firstName + ' ' + this.lastName
}

第七章:路由导航

7.1 router 路由跳转

7.1.1 基本用法
typescript 复制代码
import { router } from '@kit.ArkUI'

// 1. 跳转到新页面(保留当前页面)
router.pushUrl({
  url: 'pages/LoginPage'
})

// 2. 跳转并传递参数
router.pushUrl({
  url: 'pages/DetailPage',
  params: {
    id: '123',
    name: '张三'
  }
})

// 3. 替换当前页面(不保留历史)
router.replaceUrl({
  url: 'pages/HomePage'
})

// 4. 返回上一页
router.back()

// 5. 返回到指定页面
router.back({
  url: 'pages/Index'
})
7.1.2 接收路由参数
typescript 复制代码
import { router } from '@kit.ArkUI'

@Entry
@Component
struct DetailPage {
  @State id: string = ''
  @State name: string = ''

  aboutToAppear() {
    // 获取路由参数
    const params = router.getParams() as Record<string, string>
    this.id = params['id']
    this.name = params['name']
  }

  build() {
    Column() {
      Text(`ID: ${this.id}`)
      Text(`姓名: ${this.name}`)
    }
  }
}
7.2.1 配置路由

route_map.json:

json 复制代码
{
  "routerMap": [
    {
      "name": "HealthCheckPage",
      "pageSourceFile": "src/main/ets/pages/home/HealthCheckPage.ets",
      "buildFunction": "HealthCheckPageBuilder"
    },
    {
      "name": "HospitalRankingPage",
      "pageSourceFile": "src/main/ets/pages/home/HospitalRankingPage.ets",
      "buildFunction": "HospitalRankingPageBuilder"
    }
  ]
}
7.2.2 创建路由栈
typescript 复制代码
@Entry
@Component
struct Index {
  pathStack: NavPathStack = new NavPathStack()

  aboutToAppear(): void {
    // 存储到全局,供其他组件使用
    AppStorage.setOrCreate("pathStack", this.pathStack)
  }

  build() {
    Navigation(this.pathStack) {
      // 页面内容
    }
  }
}
7.2.3 使用路由栈跳转
typescript 复制代码
@Component
export default struct HomeComp {
  // 获取全局路由栈
  pathStack: NavPathStack = AppStorage.get("pathStack") as NavPathStack

  build() {
    NavDestination() {
      Column() {
        Button('跳转到健康检测')
          .onClick(() => {
            // 跳转到命名路由
            this.pathStack.pushPathByName('HealthCheckPage', null)
          })

        Button('跳转并传参')
          .onClick(() => {
            this.pathStack.pushPathByName('DetailPage', {
              id: '123',
              name: '测试'
            })
          })

        Button('返回')
          .onClick(() => {
            this.pathStack.pop()
          })

        Button('清空路由栈')
          .onClick(() => {
            this.pathStack.clear()
          })
      }
    }
  }
}
7.2.4 目标页面配置
typescript 复制代码
// 页面文件:HealthCheckPage.ets

@Builder
export function HealthCheckPageBuilder() {
  HealthCheckPage()
}

@Component
struct HealthCheckPage {
  // 接收路由参数
  @State params: Record<string, Object> = {}

  aboutToAppear() {
    // 获取路由参数的方式
    // this.params = ...
  }

  build() {
    NavDestination() {
      Column() {
        Text('健康检测页面')
      }
    }
    .title('健康检测')  // 设置页面标题
  }
}

7.3 路由对比

特性 router Navigation
使用场景 页面级跳转 组件级导航
是否需要配置 需要在 main_pages.json 需要在 route_map.json
传参方式 params 对象 pushPathByName 参数
历史管理 全局页面栈 组件内路由栈
适用场景 独立页面跳转 多层级导航

第八章:数据模型

8.1 定义数据模型

文件位置 : entry/src/main/ets/pages/model/NavCard.ets

typescript 复制代码
// 导航卡片数据模型
class NavItem {
  img: ResourceStr           // 图片资源
  title: string             // 标题
  subtitle?: string         // 副标题(可选)
  url?: string             // 跳转路径(可选)

  constructor(
    img: ResourceStr,
    title: string,
    subtitle?: string,
    url?: string
  ) {
    this.img = img
    this.title = title
    this.subtitle = subtitle
    this.url = url
  }
}

// 创建数据实例
const navItems: NavItem[] = [
  new NavItem(
    $r('app.media.img_jiankangjiance'),
    '健康检测',
    '守护生活每一刻',
    'HealthCheckPage'
  ),
  new NavItem(
    $r('app.media.img_yaopinshouce'),
    '药品手册',
    '明智用药',
    ''
  ),
  new NavItem(
    $r('app.media.img_yiyuanpaihang'),
    '医院排行',
    '选择优质资源',
    'HospitalRankingPage'
  )
]

// 单个数据实例
const lifeReminder: NavItem = new NavItem(
  $r('app.media.home_life_reminder_svg'),
  '今天需要吃降压药两粒哟'
)

// 导出供其他组件使用
export { NavItem, navItems, lifeReminder }

8.2 使用数据模型

typescript 复制代码
import { NavItem, navItems } from '../model/NavCard'

@Component
export default struct HomeComp {
  build() {
    Column() {
      // 使用数据模型
      ForEach(navItems, (item: NavItem, index) => {
        Row() {
          Image(item.img)
          Column() {
            Text(item.title)
            Text(item.subtitle)
          }
        }
      })
    }
  }
}

8.3 接口 vs 类

typescript 复制代码
// 方式1: 使用 interface(接口)
interface NavItemInterface {
  img: ResourceStr
  title: string
  subtitle?: string
}

const item1: NavItemInterface = {
  img: $r('app.media.icon'),
  title: '标题'
}

// 方式2: 使用 class(类)
class NavItemClass {
  img: ResourceStr
  title: string
  subtitle?: string

  constructor(img: ResourceStr, title: string, subtitle?: string) {
    this.img = img
    this.title = title
    this.subtitle = subtitle
  }

  // 可以添加方法
  getFullTitle(): string {
    return this.subtitle ? `${this.title} - ${this.subtitle}` : this.title
  }
}

const item2 = new NavItemClass($r('app.media.icon'), '标题')

选择建议:

  • ✅ 使用 interface - 纯数据结构
  • ✅ 使用 class - 需要方法或构造函数

第九章:图表组件

9.1 集成第三方图表库

安装依赖:

bash 复制代码
ohpm install @ohos/mpchart

oh-package.json5:

json5 复制代码
{
  "dependencies": {
    "@ohos/mpchart": "^2.0.0"
  }
}

9.2 折线图实现

文件位置 : entry/src/main/ets/pages/view/LineCharts.ets

typescript 复制代码
import {
  JArrayList,
  XAxis,
  XAxisPosition,
  YAxis,
  YAxisLabelPosition,
  LineDataSet,
  LineData,
  Mode,
  LineChart,
  LineChartModel,
  LimitLine,
  LimitLabelPosition,
  EntryOhos,
  IAxisValueFormatter
} from '@ohos/mpchart'

// 1. 自定义X轴格式化器
class WeekFormatter implements IAxisValueFormatter {
  private readonly weeks = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']

  getFormattedValue(value: number): string {
    return this.weeks[Math.round(value) % 7] || ''
  }
}

@Component
export default struct LineCharts {
  // 2. 初始化模型
  private model: LineChartModel = new LineChartModel()
  private dataSet: LineDataSet = new LineDataSet(new JArrayList<EntryOhos>(), "高压")
  private dataSet2: LineDataSet = new LineDataSet(new JArrayList<EntryOhos>(), "低压")
  
  // 阈值线
  private limitLine1: LimitLine = new LimitLine(120, '危险阈值')
  private limitLine2: LimitLine = new LimitLine(50, '警戒阈值')

  // 3. 外部传入的数据
  @Prop data1: Array<number | null> = [600, 200, 1000, 620, 790, 410, 175]
  @Prop data2: Array<number | null> = [400, 150, 900, 200, 210, 300, 405]
  @Prop wid: number | string = '300'
  @Prop hei: number | string = '200'

  aboutToAppear() {
    // 4. 基础配置
    this.model.getDescription()?.setEnabled(false)  // 禁用描述
    this.model.setDragEnabled(true)                 // 启用拖拽

    // 5. 配置坐标轴
    this.configureAxis()
    
    // 6. 配置阈值线
    this.configureLimitLines()

    // 7. 绑定数据
    this.model.setData(this.generateMockData())
    this.model.setVisibleXRangeMaximum(7)  // 显示7个点
  }

  // 8. 坐标轴配置
  private configureAxis() {
    // X轴配置
    const xAxis = this.model.getXAxis()
    xAxis?.setPosition(XAxisPosition.BOTTOM)     // 位置:底部
    xAxis?.setGranularity(1)                     // 最小间隔
    xAxis?.setLabelCount(7, true)                // 标签数量
    xAxis?.setValueFormatter(new WeekFormatter()) // 自定义格式化

    // 左Y轴配置
    const leftAxis = this.model.getAxisLeft()
    leftAxis?.setAxisMinimum(0)                  // 最小值
    leftAxis?.setAxisMaximum(1000)               // 最大值
    leftAxis?.setPosition(YAxisLabelPosition.OUTSIDE_CHART)
    leftAxis?.setSpaceTop(15)                    // 顶部留白

    // 右Y轴禁用
    this.model.getAxisRight()?.setEnabled(false)
  }

  // 9. 阈值线配置
  private configureLimitLines() {
    // 危险阈值线(红色虚线)
    this.limitLine1.setLineWidth(3)
    this.limitLine1.enableDashedLine(10, 10, 0)  // 虚线样式
    this.limitLine1.setTextSize(12)
    this.limitLine1.setLabelPosition(LimitLabelPosition.RIGHT_TOP)
    this.limitLine1.setLineColor("#FF3D71")

    // 警戒阈值线(橙色虚线)
    this.limitLine2.setLineWidth(3)
    this.limitLine2.enableDashedLine(10, 10, 0)
    this.limitLine2.setTextSize(12)
    this.limitLine2.setLabelPosition(LimitLabelPosition.RIGHT_BOTTOM)
    this.limitLine2.setLineColor("#FFAA33")

    // 添加到Y轴
    this.model.getAxisLeft()?.addLimitLine(this.limitLine1)
    this.model.getAxisLeft()?.addLimitLine(this.limitLine2)
  }

  // 10. 生成数据
  private generateMockData(): LineData {
    // 第一条线数据
    const values = new JArrayList<EntryOhos>()
    for (let i = 0; i < this.data1.length; i++) {
      values.add(new EntryOhos(i, this.data1[i]))
    }

    // 第二条线数据
    const values2 = new JArrayList<EntryOhos>()
    for (let i = 0; i < this.data2.length; i++) {
      values2.add(new EntryOhos(i, this.data2[i]))
    }

    // 设置数据
    this.dataSet.setValues(values)
    this.dataSet2.setValues(values2)

    // 数据集1样式
    this.dataSet.setMode(Mode.CUBIC_BEZIER)      // 贝塞尔曲线
    this.dataSet.setColorByColor(Color.Blue)     // 线条颜色
    this.dataSet.setLineWidth(2)                 // 线宽
    this.dataSet.setDrawCircles(false)           // 不显示圆点
    this.dataSet.setDrawFilled(true)             // 填充渐变

    // 数据集2样式
    this.dataSet2.setColorByColor(Color.Green)
    this.dataSet2.setDrawCircles(false)
    this.dataSet2.setMode(Mode.CUBIC_BEZIER)

    // 返回数据
    return new LineData([this.dataSet, this.dataSet2])
  }

  // 11. 页面构建
  build() {
    Column() {
      LineChart({ model: this.model })
        .width(this.wid)
        .height(this.hei)
        .backgroundColor("#F5F7FA")
        .onAppear(() => {
          // 启动动画
          this.model.animateXY(1500, 1500)
        })
    }
  }
}

9.3 使用图表组件

typescript 复制代码
import LineCharts from '../view/LineCharts'

@Component
struct DataPage {
  @State bpHighData: number[] = [120, 125, 118, 130, 115, 122, 119]
  @State bpLowData: number[] = [80, 85, 78, 90, 75, 82, 79]

  build() {
    Column() {
      Text('血压趋势')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)

      LineCharts({
        wid: '100%',
        hei: 300,
        data1: this.bpHighData,
        data2: this.bpLowData
      })
    }
  }
}

知识点总结:

  • ✅ 使用第三方库需要先安装依赖
  • ✅ 图表配置包括:坐标轴、样式、数据
  • ✅ 可以自定义格式化器
  • ✅ 支持多条数据线

第十章:最佳实践

10.1 组件设计原则

10.1.1 单一职责
typescript 复制代码
// ❌ 不好:一个组件做太多事
@Component
struct BadComponent {
  build() {
    Column() {
      // 导航栏
      Row() { }
      
      // 内容区
      Column() { }
      
      // 底部栏
      Row() { }
    }
  }
}

// ✅ 好:拆分成多个组件
@Component
struct GoodComponent {
  build() {
    Column() {
      TopNav()
      ContentArea()
      BottomBar()
    }
  }
}
10.1.2 可复用性
typescript 复制代码
// ✅ 好:可配置的通用组件
@Component
export default struct CustomButton {
  @Prop text: string
  @Prop bgColor: string = '#4D91FF'
  @Prop fontSize: number = 16
  onClickHandler?: () => void

  build() {
    Button(this.text)
      .backgroundColor(this.bgColor)
      .fontSize(this.fontSize)
      .onClick(() => {
        this.onClickHandler?.()
      })
  }
}

10.2 性能优化

10.2.1 减少不必要的渲染
typescript 复制代码
// ❌ 不好:频繁创建对象
build() {
  Column() {
    ForEach(this.items, (item) => {
      Row() {
        Text(item.name)
          .fontSize(this.getSize())  // 每次都调用
      }
    })
  }
}

// ✅ 好:缓存计算结果
private cachedSize: number = 16

aboutToAppear() {
  this.cachedSize = this.getSize()
}

build() {
  Column() {
    ForEach(this.items, (item) => {
      Row() {
        Text(item.name)
          .fontSize(this.cachedSize)  // 使用缓存
      }
    })
  }
}
10.2.2 LazyForEach 懒加载
typescript 复制代码
class BasicDataSource implements IDataSource {
  private listeners: DataChangeListener[] = []

  public totalCount(): number {
    return 0
  }

  public getData(index: number): Object {
    return undefined
  }

  registerDataChangeListener(listener: DataChangeListener): void {
    if (this.listeners.indexOf(listener) < 0) {
      this.listeners.push(listener)
    }
  }

  unregisterDataChangeListener(listener: DataChangeListener): void {
    const pos = this.listeners.indexOf(listener)
    if (pos >= 0) {
      this.listeners.splice(pos, 1)
    }
  }
}

@Component
struct LongList {
  private dataSource: BasicDataSource = new BasicDataSource()

  build() {
    List() {
      LazyForEach(this.dataSource, (item: Object) => {
        ListItem() {
          Text(item.toString())
        }
      }, item => item.id)
    }
  }
}

10.3 代码组织

10.3.1 文件命名
复制代码
✅ 推荐命名规范:
- 组件文件: PascalCase (HomeComp.ets, CardComp.ets)
- 页面文件: PascalCase (LoginPage.ets, Index.ets)
- 工具类: camelCase (httpUtil.ets, dateUtil.ets)
- 模型类: PascalCase (NavCard.ets, UserInfo.ets)
10.3.2 目录结构
复制代码
pages/
├── comp/          # 公共组件
├── model/         # 数据模型
├── view/          # 视图组件
├── utils/         # 工具函数
├── home/          # 首页模块
├── profile/       # 个人中心模块
└── common/        # 通用样式

10.4 样式管理

10.4.1 使用 @Extend 统一样式
typescript 复制代码
// 定义全局样式扩展
@Extend(Text)
function primaryText() {
  .fontSize(16)
  .fontColor('#333333')
  .fontWeight(FontWeight.Normal)
}

@Extend(Text)
function titleText() {
  .fontSize(24)
  .fontColor('#000000')
  .fontWeight(FontWeight.Bold)
}

// 使用
build() {
  Column() {
    Text('标题').titleText()
    Text('内容').primaryText()
  }
}
10.4.2 使用资源文件

resources/base/element/color.json:

json 复制代码
{
  "color": [
    {
      "name": "primary_color",
      "value": "#4D91FF"
    },
    {
      "name": "text_primary",
      "value": "#333333"
    }
  ]
}

resources/base/element/float.json:

json 复制代码
{
  "float": [
    {
      "name": "global_padding_or_margin",
      "value": "16vp"
    },
    {
      "name": "title_font_size",
      "value": "24fp"
    }
  ]
}

使用资源:

typescript 复制代码
Text('标题')
  .fontSize($r('app.float.title_font_size'))
  .fontColor($r('app.color.text_primary'))
  .padding($r('app.float.global_padding_or_margin'))

10.5 错误处理

typescript 复制代码
// ✅ 好:完善的错误处理
private jumpToPage(url: string) {
  try {
    router.pushUrl({ url: url })
  } catch (error) {
    console.error(`路由跳转失败: ${error.code} - ${error.message}`)
    // 显示错误提示
    promptAction.showToast({
      message: '页面跳转失败,请重试'
    })
  }
}

第十一章:常见问题

11.1 装饰器相关

Q1: @State 和 @Prop 的区别?
typescript 复制代码
// @State: 组件内部状态
@State count: number = 0
// ✅ 修改会刷新UI
// ✅ 只在当前组件有效

// @Prop: 父组件传入的属性
@Prop count: number
// ✅ 父组件修改会同步到子组件
// ✅ 子组件修改不影响父组件
Q2: 什么时候使用 @Link?
typescript 复制代码
// 需要父子组件双向同步时使用
@Component
struct Child {
  @Link count: number  // 双向绑定

  build() {
    Button(`子组件: ${this.count}`)
      .onClick(() => {
        this.count++  // 修改会同步到父组件
      })
  }
}

// 父组件传递时使用 $
Child({ count: $count })

11.2 布局相关

Q3: 如何实现水平居中?
typescript 复制代码
// 方法1: Column + alignItems
Column() {
  Text('居中文本')
}
.alignItems(HorizontalAlign.Center)

// 方法2: Row + justifyContent
Row() {
  Text('居中文本')
}
.justifyContent(FlexAlign.Center)
Q4: 如何实现垂直居中?
typescript 复制代码
// Column + justifyContent
Column() {
  Text('居中文本')
}
.justifyContent(FlexAlign.Center)
.height('100%')

11.3 路由相关

特性 router Navigation
跳转范围 页面级 组件级
配置文件 main_pages.json route_map.json
使用场景 独立页面 多层导航
返回栈 全局 组件内
Q6: 如何传递复杂对象?
typescript 复制代码
// router 方式
router.pushUrl({
  url: 'pages/DetailPage',
  params: {
    user: JSON.stringify({  // 转为JSON字符串
      id: 1,
      name: '张三'
    })
  }
})

// 接收时解析
const params = router.getParams()
const user = JSON.parse(params['user'])

11.4 性能相关

Q7: ForEach 和 LazyForEach 的区别?
typescript 复制代码
// ForEach: 一次性渲染所有
ForEach(this.items, (item) => {
  ListItem() { Text(item) }
})
// ✅ 适合少量数据(< 100)
// ❌ 大量数据会卡顿

// LazyForEach: 按需渲染
LazyForEach(this.dataSource, (item) => {
  ListItem() { Text(item) }
})
// ✅ 适合大量数据
// ✅ 性能更好

11.5 样式相关

Q8: 如何设置圆角?
typescript 复制代码
Column() { }
  .borderRadius(12)  // 所有角
  .borderRadius({    // 单独设置
    topLeft: 12,
    topRight: 12,
    bottomLeft: 0,
    bottomRight: 0
  })
Q9: 如何设置阴影?
typescript 复制代码
Column() { }
  .shadow({
    radius: 10,
    color: '#00000033',
    offsetX: 0,
    offsetY: 2
  })

总结

学习路径建议

复制代码
第1周: 熟悉项目结构,理解启动流程
  ↓
第2周: 学习组件开发,掌握装饰器
  ↓
第3周: 深入状态管理和路由导航
  ↓
第4周: 数据模型和图表组件
  ↓
第5周: 最佳实践和性能优化

实践建议

  1. 动手实践: 运行项目,修改代码,观察效果
  2. 阅读文档: 查阅 HarmonyOS 官方文档
  3. 调试技巧: 使用 console.log 和 DevEco 调试工具
  4. 代码对比: 对比本项目与官方示例
  5. 循序渐进: 从简单组件开始,逐步深入

进阶方向

  • 🚀 网络请求和 HTTP 客户端
  • 🗄️ 本地数据库(Preferences、RelationalStore)
  • 📱 系统能力调用(相机、位置、通知)
  • 🎨 动画和转场效果
  • 🔐 安全和加密
  • 📦 应用打包和发布

祝学习愉快!遇到问题随时查阅本文档。 🎉

相关推荐
君逸臣劳13 小时前
【Harmony Next】手把手撸一个支持高度自定义的Toast
harmonyos
安卓开发者13 小时前
鸿蒙NEXT传感器开发概述:开启智能感知新时代
华为·harmonyos
nju_spy1 天前
华为AI岗 -- 笔试(一)
人工智能·深度学习·机器学习·华为·笔试·dbscan·掩码多头自注意力
安卓开发者1 天前
鸿蒙NEXT按键拦截与监听开发指南
华为·harmonyos
2503_928411561 天前
10.13 Tabs选项卡布局
华为·harmonyos·鸿蒙
我爱学习_zwj1 天前
【鸿蒙进阶-7】鸿蒙与web混合开发
前端·华为·harmonyos
HMSCore1 天前
消息推送策略:如何在营销与用户体验间找到最佳平衡点
harmonyos
HMSCore1 天前
同一设备多账号登录,如何避免消息推送“串门”?
harmonyos
零點壹度ideality1 天前
鸿蒙实现可以上下左右滑动的表格-摆脱大量ListScroller
前端·harmonyos
●VON1 天前
重生之我在大学自学鸿蒙开发第七天-《AI语音朗读》
学习·华为·云原生·架构·harmonyos