第1章:ArkUI声明式UI基础

各位同学,咱们开始进入正题。

ArkUI 是鸿蒙原生开发的 UI 框架。说白了,它就是用来画界面的。你看到的按钮、文字、输入框,都是用它搭出来的。我刚开始接触 ArkUI 时,第一反应是——这不就是 Flutter 吗?嗯,确实有相似之处,但细节上差别不小。

1.1 声明式UI:换个思路写界面

传统开发里,我们写界面通常是这样的:

  • 先创建一个布局文件
  • 然后在代码里找到控件
  • 再手动更新它的属性

这叫「命令式 UI」。你告诉系统每一步该怎么做。

ArkUI 不一样。它是「声明式 UI」。你只需要描述界面「长什么样」,系统会自动帮你搞定更新。举个例子:

@State message: string = 'Hello, HarmonyOS!'

build() {
  Column() {
    Text(this.message)
      .fontSize(30)
      .fontWeight(FontWeight.Bold)
    
    Button('点击修改')
      .onClick(() => {
        this.message = '你点了我一下'
      })
  }
}

看到没?我只需要声明一个 message 变量,然后在 Text 组件里引用它。当按钮被点击时,我直接修改 message 的值,界面就自动刷新了。不需要手动调用 setText() 之类的方法。

核心思想:UI 是状态的一个函数。状态变了,UI 自动跟着变。

1.2 基础组件:Text、Button、Image、TextInput

这几个组件,你写任何 App 都绕不开。我一个个说。

Text — 显示文字

最常用的组件,没有之一。用法很简单:

Text('这是一段文字')
  .fontSize(16)
  .fontColor(Color.Blue)
  .textAlign(TextAlign.Center)

我在项目中遇到过一个问题:文字太长被截断了。后来发现加个 .maxLines(2).textOverflow(TextOverflow.Ellipsis) 就能优雅地显示省略号。

Button — 按钮

用户交互的核心。ArkUI 的 Button 支持多种样式:

Button('默认按钮')
  .onClick(() => {
    console.info('按钮被点击了')
  })

Button('圆角按钮')
  .borderRadius(20)
  .backgroundColor('#007AFF')

Button('带图标的按钮')
  .type(ButtonType.Normal)
  .icon($r('app.media.icon_star'))

嗯,这里要注意:onClick 是点击事件,别写成 onClick() 带括号,那是调用函数,不是绑定事件。我刚开始就犯过这个错。

Image — 图片

显示图片,支持本地资源和网络图片:

// 本地图片
Image($r('app.media.logo'))
  .width(100)
  .height(100)

// 网络图片
Image('https://example.com/photo.png')
  .objectFit(ImageFit.Cover)

你想想看,网络图片加载失败怎么办?ArkUI 提供了 .alt() 方法设置占位图:

Image('https://example.com/photo.png')
  .alt($r('app.media.placeholder'))

小技巧:本地图片放在 resources/base/media 目录下,用 $r() 引用。别直接写路径字符串,那样容易出错。

TextInput — 文本输入框

用户输入信息的地方。登录注册页面必备:

@State inputValue: string = ''

build() {
  TextInput({ placeholder: '请输入用户名', text: this.inputValue })
    .onChange((value: string) => {
      this.inputValue = value
    })
    .maxLength(20)
    .type(InputType.Normal)
}

我曾经踩过一个坑:密码输入框忘记设置 .type(InputType.Password),结果用户输入的密码明文显示在屏幕上。嗯,这种低级错误,希望大家别犯。

1.3 @State 装饰器:状态驱动的核心

这是 ArkUI 最核心的概念。没有它,声明式 UI 就玩不转。

@State 的作用很简单:标记一个变量为「响应式状态」。当这个变量的值发生变化时,所有依赖它的 UI 组件都会自动重新渲染。

看个完整例子:

@Entry
@Component
struct CounterPage {
  @State count: number = 0

  build() {
    Column() {
      Text(`当前计数: ${this.count}`)
        .fontSize(24)
      
      Row() {
        Button('+1')
          .onClick(() => {
            this.count++
          })
        
        Button('-1')
          .onClick(() => {
            this.count--
          })
      }
    }
  }
}

为什么点击按钮后,Text 会自动更新?因为 count@State 装饰了。ArkUI 框架会监听它的变化,一旦改变,就通知 UI 重新渲染。

重要提醒:只有被 @State 装饰的变量才能触发 UI 更新。普通变量改了,界面不会变。我见过有人写了半天,发现界面死活不刷新,最后发现忘了加 @State

1.4 状态驱动的更新机制

咱们深入一点。ArkUI 的状态更新机制,其实是一个「观察者模式」的变体。

流程是这样的:

  1. 你用 @State 声明了一个变量
  2. ArkUI 框架自动为这个变量建立「依赖追踪」
  3. 当变量变化时,框架会找到所有「依赖」这个变量的 UI 组件
  4. 只重新渲染这些组件,其他不变

说白了,就是「精准打击」。不会因为一个状态变了,整个页面都重绘。性能上比传统方式好很多。

我建议你记住一个原则:能用 @State 解决的问题,别用全局变量。全局变量虽然方便,但没法触发 UI 更新,而且容易造成代码耦合。

1.5 实战:写一个登录表单

光说不练假把式。咱们把今天学的知识串起来,写一个简单的登录界面:

@Entry
@Component
struct LoginPage {
  @State username: string = ''
  @State password: string = ''
  @State isLogging: boolean = false

  build() {
    Column() {
      // 标题
      Text('欢迎登录')
        .fontSize(28)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 40 })
      
      // 用户名输入
      TextInput({ placeholder: '请输入用户名' })
        .onChange((value) => {
          this.username = value
        })
        .margin({ bottom: 16 })
      
      // 密码输入
      TextInput({ placeholder: '请输入密码' })
        .type(InputType.Password)
        .onChange((value) => {
          this.password = value
        })
        .margin({ bottom: 32 })
      
      // 登录按钮
      Button('登录')
        .width('100%')
        .onClick(() => {
          this.isLogging = true
          // 这里可以调用登录接口
          console.info(`用户名: ${this.username}, 密码: ${this.password}`)
        })
    }
    .padding(24)
    .width('100%')
    .height('100%')
  }
}

这个例子包含了 Text、TextInput、Button 三个组件,以及 @State 状态管理。你把它跑起来,输入内容,点击按钮,看看控制台输出。嗯,这就是 ArkUI 声明式开发的基本套路。

1.6 本章小结

咱们今天聊了这些:

  • 声明式 UI 是什么——描述界面,而不是命令界面
  • 四个基础组件——Text、Button、Image、TextInput
  • @State 装饰器——让变量变成响应式
  • 状态驱动机制——状态变了,UI 自动更新

下一章,咱们会深入布局系统,看看 Row、Column、Stack 这些容器组件怎么用。到时候你会发现,掌握了这些基础,写复杂界面其实也不难。

好,今天就到这儿。动手写写代码,比看十遍文章都管用。