在这里插入图片描述

在这里插入图片描述

引言

在 KuiKly for OpenHarmony 项目中,我于 2026 年 1 月 26 日新增了 WelcomePage.ets 欢迎页面,并完成路由与启动配置。本文将聚焦技术细节,结合实际代码,逐层剖析状态管理、声明式动画、交互反馈与工程集成等关键实现,为 OpenHarmony 开发者提供可复用的技术参考。

全文基于真实提交代码,包含完整 .ets 文件与配置变更,强调规范性、性能与跨平台前瞻性


一、欢迎页核心代码解析:WelcomePage.ets

文件路径:ohosApp/entry/src/main/ets/pages/WelcomePage.ets

在这里插入图片描述

1.1 导入与组件结构

import router from '@ohos.router';

@Entry
@Component
struct WelcomePage {
  // 状态定义
}
  • @ohos.router 是 OpenHarmony 官方路由模块,必须显式导入;
  • @Entry 标记该组件可作为应用入口(配合 EntryAbility 使用);
  • @Component 声明自定义 UI 组件。

1.2 状态变量设计:安全命名与作用域

@State iconScale: number = 0.8;
@State contentOpacity: number = 0;
@State buttonScale: number = 1;

关键规范

  • 所有动画控制属性均使用 @State 装饰,确保响应式更新;
  • 变量名避免使用 scaleopacity 等 ArkUI 内置属性名,防止冲突;
  • 初始值设定为动画起始状态(如 iconScale = 0.8 表示缩小入场)。

💡 跨平台提示:这些状态未来可抽象为 Kotlin Multiplatform 共享数据类,各平台仅需绑定到本地 UI 属性。


1.3 页面入场动画:animateTo 的精准控制

aboutToAppear(): void {
  this.animateEntry();
}

private animateEntry(): void {
  animateTo({
    duration: 800,
    curve: Curve.EaseOut
  }, () => {
    this.iconScale = 1;
    this.contentOpacity = 1;
  });
}

技术要点

  • aboutToAppear() 是页面即将显示时的生命周期钩子,适合触发动画
  • animateTo 第一个参数为动画配置对象,支持 duration(毫秒)、curve(缓动曲线);
  • 回调函数内直接修改 @State 变量,系统自动插值并驱动渲染;
  • Curve.EaseOut 提供“先快后慢”的自然效果,优于线性动画。

⚠️ 注意:animateTo 仅能修改被其包裹的 @State 变量,外部赋值无效。


1.4 UI 构建:层级结构与样式控制

build() {
  Column() {
    Column() {
      // 内容区域
    }
    .width('100%')
    .padding({ left: 40, right: 40 })
  }
  .width('100%')
  .height('100%')
  .backgroundColor('#FFFFFF')
  .justifyContent(FlexAlign.Center)
}
  • 外层 Column 实现全屏垂直居中FlexAlign.Center);
  • 内层 Column 控制内容宽度与左右留白(padding),适配不同屏幕;
  • 所有文本、图标均绑定 opacity(this.contentOpacity),实现统一淡入。
图标与文字样式
Image($r('app.media.icon'))
  .width(120)
  .height(120)
  .objectFit(ImageFit.Contain)  // 保持宽高比
  .scale({ x: this.iconScale, y: this.iconScale })
  .opacity(this.contentOpacity)

Text('你好')
  .fontSize(36)
  .fontWeight(FontWeight.Bold)
  .fontColor('#333333')
  .opacity(this.contentOpacity)
  • $r('app.media.icon') 引用 resources/base/media/icon.png
  • objectFit(ImageFit.Contain) 防止图片拉伸变形;
  • 字体颜色使用十六进制字符串(#333333),兼容性强。

1.5 按钮交互:点击反馈 + 路由跳转

在这里插入图片描述

“开始体验”按钮
Button('开始体验')
  .width(200)
  .height(50)
  .fontSize(18)
  .backgroundColor('#007DFF')
  .borderRadius(25)
  .scale({ x: this.buttonScale, y: this.buttonScale })
  .onClick(() => {
    this.buttonScale = 0.95; // 瞬间缩小模拟按压
    setTimeout(() => {
      this.buttonScale = 1;   // 恢复原尺寸
      router.pushUrl({
        url: 'pages/Index'
      });
    }, 100);
  })
  .opacity(this.contentOpacity)

交互逻辑

  1. 点击瞬间将 buttonScale 设为 0.95,视觉上产生“按下”效果;
  2. 使用 setTimeout 延迟 100ms 后恢复并跳转,避免动画卡顿;
  3. router.pushUrl 跳转至已注册页面 pages/Index

🔍 替代方案:OpenHarmony API 11+ 支持 onClickEffect,可简化反馈逻辑,但为兼容性暂未采用。

“了解更多”按钮(预留扩展)

在这里插入图片描述

Button('了解更多')
  .backgroundColor('#F5F5F5')
  .fontColor('#333333')
  .onClick(() => {
    console.info('了解更多');
    // 未来可扩展:跳转文档页、打开浏览器等
  })
  • 使用浅灰背景 + 深灰文字,形成主次区分;
  • 当前仅输出日志,便于后续功能接入。

二、工程配置变更

2.1 路由注册:main_pages.json

文件路径:ohosApp/entry/src/main/resources/base/profile/main_pages.json

{
  "src": [
    "pages/WelcomePage",
    "pages/Index",
    "pages/GestureThrough"
  ]
}
  • 必须显式注册所有页面,否则 router.pushUrl 报错;
  • 路径格式为 pages/文件名(无 .ets 后缀);
  • 顺序不影响启动页,仅用于 DevEco 预览和静态分析。

2.2 启动入口:EntryAbility.ets

文件路径:ohosApp/entry/src/main/ets/entryability/EntryAbility.ets

onWindowStageCreate(windowStage: window.WindowStage) {
  windowStage.loadContent('pages/WelcomePage', (err) => {
    if (err.code) {
      console.error('Failed to load WelcomePage. Code: ' + err.code);
      return;
    }
  });
}
  • loadContent 参数从 'pages/Index' 改为 'pages/WelcomePage'
  • 错误回调确保启动失败可追踪。

三、关键技术总结与最佳实践

技术点 实现方式 注意事项
状态管理 @State + 语义化命名 避免与内置属性冲突
声明式动画 animateTo({ duration, curve }, () => { ... }) 仅修改包裹内的 @State
路由跳转 router.pushUrl({ url: 'pages/xxx' }) 路径必须已注册
交互反馈 动态修改 scale + setTimeout 延迟需 < 150ms,避免感知卡顿
资源引用 $r('app.media.xxx') 图片放 resources/base/media/

四、跨平台迁移可行性分析

当前代码虽运行于 OpenHarmony,但结构已具备良好抽象潜力:

  1. 状态层iconScalecontentOpacity 可移至 KMP commonMain
  2. 逻辑层animateEntry()、跳转逻辑可封装为平台无关函数;
  3. UI 层:ArkTS 的 Column/Button 对应 Compose 的 Column/Button,SwiftUI 的 VStack/Button,只需适配 DSL。

例如,未来可定义共享导航接口:

// commonMain
interface AppRouter {
    fun navigateTo(route: String)
}

// ohosMain
class OhosRouter : AppRouter {
    override fun navigateTo(route: String) {
        // 调用 ArkTS router.pushUrl
    }
}

当前 onClick 中的跳转逻辑即可替换为 appRouter.navigateTo("index"),实现完全解耦。


五、验证与调试

  • 构建结果BUILD SUCCESSFUL in 7.626s
  • 真机测试:HarmonyOS NEXT 设备上动画流畅(60fps),按钮反馈及时;
  • 常见问题
    • 若页面空白:检查 main_pages.json 是否注册;
    • 若动画不生效:确认 @State 变量是否在 animateTo 回调内修改;
    • 若跳转失败:核对 url 字符串是否与注册路径一致。

结语

这个不到 100 行的欢迎页,浓缩了 OpenHarmony 开发中的典型模式:状态驱动 UI、声明式动画、显式路由、安全命名。作为独立开发者,我坚持每一处细节都要经得起“未来迁移”的考验。

KuiKly 的目标是成为 Kotlin Multiplatform 在鸿蒙生态的 UI 落地范例。欢迎页只是第一步,后续将逐步引入手势识别、响应式布局、主题系统等能力。

源码部分

import router from '@ohos.router';

@Entry
@Component
struct WelcomePage {
  @State iconScale: number = 0.8;
  @State contentOpacity: number = 0;
  @State buttonScale: number = 1;

  aboutToAppear(): void {
    this.animateEntry();
  }

  private animateEntry(): void {
    animateTo({
      duration: 800,
      curve: Curve.EaseOut
    }, () => {
      this.iconScale = 1;
      this.contentOpacity = 1;
    });
  }

  build() {
    Column() {
      Column() {
        Image($r('app.media.icon'))
          .width(120)
          .height(120)
          .objectFit(ImageFit.Contain)
          .margin({ bottom: 30 })
          .scale({ x: this.iconScale, y: this.iconScale })
          .opacity(this.contentOpacity)

        Text('你好')
          .fontSize(36)
          .fontWeight(FontWeight.Bold)
          .fontColor('#333333')
          .margin({ bottom: 10 })
          .opacity(this.contentOpacity)

        Text('Kuikly for OpenHarmony')
          .fontSize(28)
          .fontWeight(FontWeight.Medium)
          .fontColor('#666666')
          .margin({ bottom: 40 })
          .opacity(this.contentOpacity)

        Text('基于 Kotlin Multiplatform 的')
          .fontSize(16)
          .fontColor('#999999')
          .margin({ bottom: 5 })
          .opacity(this.contentOpacity)

        Text('跨平台 UI 框架')
          .fontSize(16)
          .fontColor('#999999')
          .margin({ bottom: 60 })
          .opacity(this.contentOpacity)

        Button('开始体验')
          .width(200)
          .height(50)
          .fontSize(18)
          .fontWeight(FontWeight.Medium)
          .backgroundColor('#007DFF')
          .borderRadius(25)
          .scale({ x: this.buttonScale, y: this.buttonScale })
          .onClick(() => {
            this.buttonScale = 0.95;
            setTimeout(() => {
              this.buttonScale = 1;
              router.pushUrl({
                url: 'pages/Index'
              });
            }, 100);
          })
          .margin({ bottom: 20 })
          .opacity(this.contentOpacity)

        Button('了解更多')
          .width(200)
          .height(50)
          .fontSize(18)
          .fontWeight(FontWeight.Medium)
          .backgroundColor('#F5F5F5')
          .fontColor('#333333')
          .borderRadius(25)
          .onClick(() => {
            console.info('了解更多');
          })
          .opacity(this.contentOpacity)
      }
      .width('100%')
      .padding({ left: 40, right: 40 })
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#FFFFFF')
    .justifyContent(FlexAlign.Center)
  }
}

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐