在上一节中,我们搭建了开发环境并创建了第一个“Hello World”应用。

今天,我们探索一下ArkUI框架的内部机制,学习如何使用最核心的UI组件来构建用户界面,并掌握声明式UI编程——@State状态管理。

一、修改项目和模块

上一节我们新建了一个'HarmonyHelloWorld'的项目,里面有一个默认的模块'entry'。

为了方便后续的学习,我们把项目和模块重新梳理下,进行修改。

1.1修改项目

1.1.1找到项目位置,关闭DevEco Studio

关闭DevEco Studio(先记录下项目的路径)。

image-20251002105744089

在资源管理器中找到项目所在位置。

也可以直接右键项目Open In->Explorer直接打开项目目录。

image-20251002110005582

1.1.2修改项目名称

直接重命名该文件夹,我把项目名称修改成了HarmonyOS_Study。

image-20251002110256859

1.1.3重新打开DevEco Studio

在欢迎界面看到我们之前的哪个项目已经不可用了。直接点右侧的‘x’删除。

image-20251002110444677

在欢迎界面点击Open...,找到我们修改后的'HarmonyOS_Study'所在位置。

有可能显示的还是之前的HarmonyHelloWorld,点一下上面的刷新即可。

image-20251002110858881

然后点击OK,准备加载项目。

image-20251002110948849

这个界面中直接点击'Trust Project'就行,我们自己写的项目。

这样,我们的项目就成功改名了,IDE会重新建立索引,所有内容都会保持完好。

1.2修改模块

模块不能直接去修改文件夹的名称。

1.2.1重命名模块

在IDE左侧的项目结构视图中找到entry模块。用鼠标右键点击他。

在弹出的菜单中,选择Refactor -> Rename...(重构 -> 重命名)。

image-20251002111516518

点击后会弹出一个选择框,这时候选择Rename module。

image-20251002111713473

在输入框中输入新的模块名称(全部使用小写字母、数字和下划线)

image-20251002111821194

输入完成点击OK。

IDE会自动查找所有引用了 "entry" 的地方(比如配置文件build-profile.json5等)并进行修改。

修改完成。

image-20251002112005526

修改完项目和模块备用,我们开始今天的正文。

二、ArkUI核心思想:UI是状态的函数

在传统的命令式UI编程中,我们通常需要手动获取UI组件的实例,然后在事件发生时编写代码来直接更新这个组件的属性(比如,textView.setText("text"))。这种方式在简单页面里还可以,但如果界面逻辑变得复杂,代码会变得越来越难维护。

ArkUI采用了更现代的声明式UI范式。核心思想可以总结为一句话:UI = f(State)。

这里的State是指驱动UI显示的所有数据。在ArkUI里,你只需要声明在特定状态下,UI应该呈现成什么样子。

当状态(数据)发生变化的时候,UI框架会自动、高效地重新渲染界面,来反映最新的状态。

我们不再需要关心繁琐的DOM操作,只专注于业务逻辑和数据管理。

而连接数据(State)和UI的桥梁,就是我们今天要看的重点——@State装饰器。

2.1最常用的UI组件

让我们先来认识几个构建界面的基础组件。

1. Text:文本显示

Text组件用于在界面上显示一段文本,是最基础、最常见的组件。

基础用法:

 Text('你好,懒惰蜗牛!')

常用属性:

你可以通过链式调用的方式为Text组件设置各种样式属性。

Text('欢迎来到懒惰蜗牛的ArkUI世界')
   .fontSize(24) // 设置字体大小,单位vp
   .fontWeight(FontWeight.Bold) // 设置字体粗细
   .fontColor('#333333') // 设置字体颜色
   .textAlign(TextAlign.Center) // 设置文本对齐方式
   .width('100%') // 设置宽度
   .padding(10) // 设置内边距

2. Button:交互按钮

Button是用户与应用进行交互的核心组件,通常用于响应用户的点击事件。

基础用法:

Button可以包含子组件,最常见的就是放一个Text来显示按钮的标题。

 Button() {
   Text('点我')
     .fontColor(Color.White)
 }
 .width(200)
 .height(48)
 .backgroundColor('#007DFF')
 .borderRadius(8)

响应点击事件:

使用.onClick()方法来监听按钮的点击事件。

 Button() {
   Text('Click Me')
 }
 .onClick(() => {
   console.info('按钮被点击了!');
   // 在这里处理业务逻辑
 })

3. Image:图像显示

Image组件用于展示图片资源。图片可以来自应用的本地资源,也可以是网络图片。

基础用法:

使用$r来引用本地resources目录下的图片资源。

 // 假设在/resources/base/media/目录下有一张名为icon.png的图片
 Image($r('app.media.icon'))
   .width(100)
   .height(100)
   .objectFit(ImageFit.Contain) // 设置图片缩放模式

4. TextInut:文本输入

当需要用户输入文本时,TextInput组件就派上用场了。

基础用法:

 TextInput({ placeholder: '请输入您的姓名' })
   .width('90%')
   .height(48)
   .fontSize(18)
   .backgroundColor('#F1F3F5')
   .borderRadius(8)
   .padding({ left: 10, right: 10 })

三、@State状态管理

了解了基本组件后,我们来看看怎么让他们协作起来。

@State是ArkUI提供的最基本的状态管理装饰器。

核心作用:

  1. 持有状态:@State装饰的变量成为组件内部私有的、可观察的状态。

  2. 驱动更新:一旦@State变量的值发生改变,ArkUI框架会自动重新执行build函数,从而更新所有依赖该变量的UI组件。

简单来说,就是你用@State告诉框架:“听着,这个变量很重要,只要它变了,就刷新一下界面。”

四、计数器小案例

理论讲了这么多,让我们通过一个计数器例子来试下。

这个应用包含一个用来显示数字的Text和一个“+1”的Button。

4.1新建模块

image-20251002113206613

选择一个Empty Ability:

image-20251002113258570

配置模块信息:

image-20251002113555619

模块名称(Module name)自行定义。

模块类型(Module type)我们选择feature。

模块类型中entry和feature的区别:

entry是应用的主模块和唯一入口。我们可以把他想象成一个房子的正门。

一个项目(应用)中,有且只能有一个entry类型的模块。(之前day1_helloworld模块已经是entry类型)

这个模块的作用就是应用安装后,用户在桌面上能看到的那个图标所启动的主程序。通常包含了应用的核心框架和启动界面。

而feature是功能模块或动态特性模块。我们可以把他看成一个个功能不同的独立的房间,比如卧室、厨房等等。

一个项目(应用)中,可以有零个或多个feature类型的模块。

这种类型的模块主要是用来承载相对独立的功能。

对于我们学习的项目,每一课的新代码都做成一个独立的feature模块最好。

虽然不能作为桌面图标直接启动,但我们可以配置IDE来独立运行和调试。

配置Ability

image-20251002114549827

如果你接触过Android开发,你可以把Ability通俗的理解成Android的Activity,或者前端开发中的一个页面/视图。

这是一个负责跟用户交互、承载UI界面的基本单元。

刚才我们创建了一个feature模块,现在IDE需要知道这个模块的入口页面叫什么、以及他有什么特性。

Ability name是IDE自动生成(IDE根据模块名自动生成)的主页面的类名。后续会看到一个名字叫Day2_counterAbility.ets的文件。

关于这个计数器应用的UI和逻辑就在这个文件里。

Exported是个开关按钮,这个开关决定了该Ability是否可以被其他应用调用。

打开的情况下,意味着这个Ability不仅可以在我们的App内部被调用,理论上也可以被设备上的其他App唤起(需要正确的权限和配置)。

关闭的情况下,意味着这个Ability是私有的,只能在我们自己的应用内部访问和跳转。其他任何应用都无法直接调用他。

对于我们当前的课程学习和独立运行调试来说,这个选项肯定是要开的。如果关闭了,IDE可能没办法从外部(比如通过命令行)启动这个页面进行预览和调试。

模块创建完成:

image-20251002115531406

4.2代码实现

把下面的代码复制到Index.ets中(注意是day2_counter模块)

@Entry
@Component
struct Index { // 组件名与文件名保持一致
  // 1. 使用@State装饰器来定义一个状态变量 `count`
  //    并给它一个初始值 0。
  @State count: number = 0;
  build() {
    // 使用Column组件让按钮和文本垂直排列
    Column() {
      // 2. 将Text组件的内容与@State变量`count`进行绑定。
      Text(`当前计数值: ${this.count}`)
        .fontSize(30)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 30 }) // 添加一些外边距
      // 3. 定义一个按钮
      Button('+1')
        .width(200)
        .height(60)
        .fontSize(24)
        // 4. 在按钮的onClick事件中,修改`count`变量的值。
        .onClick(() => {
          this.count++; // 仅仅是改变这个变量的值,UI会自动更新
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center) // 使用Flex布局让内容垂直和水平居中
  }
}

关键步骤都写上了注释,再大致梳理下:

定义了一个名为count的变量,并用@State装饰它,这意味着count成为了这个组件的状态。

Text组件显示的内容来自于this.count。相当于Text组件就订阅了count状态。

当用户点击Button的时候onClick回调就会被触发。

在回调里,我们执行了this.count++。这个操作改变了@State变量的值。

ArkUI框架侦测到count的变化,就自动重新调用build函数。

build函数执行时,Text组件获取到count的新值,渲染到屏幕上。

整个过程我们没有手动操作UI,只是改变了数据。这就是声明式UI:我们只管改变状态,UI的更新交给框架。

4.3运行

先把我们的模拟器运行起来

然后选择我们的day2_counter模块运行。

最后我们会在模拟器上看到我们新开发的计数器模块。

image-20251002123545120

总结

今天,我们学习了ArkUI中最核心的几个UI组件Text、Button、Image和TextInput的基础用法。

我们理解了声明式UI的核心思想,并通过@State装饰器和计数器实例,掌握了如何实现由数据驱动的UI自动更新。

posted on 2025-10-18 09:06  lxjshuju  阅读(11)  评论(0)    收藏  举报