鸿蒙开发2--常用UI组件与@State状态管理入门
在上一节中,我们搭建了开发环境并创建了第一个“Hello World”应用。
今天,我们探索一下ArkUI框架的内部机制,学习如何使用最核心的UI组件来构建用户界面,并掌握声明式UI编程——@State
状态管理。
一、修改项目和模块
上一节我们新建了一个'HarmonyHelloWorld'的项目,里面有一个默认的模块'entry'。
为了方便后续的学习,我们把项目和模块重新梳理下,进行修改。
1.1修改项目
1.1.1找到项目位置,关闭DevEco Studio
关闭DevEco Studio(先记录下项目的路径)。
在资源管理器中找到项目所在位置。
也可以直接右键项目Open In->Explorer直接打开项目目录。
1.1.2修改项目名称
直接重命名该文件夹,我把项目名称修改成了HarmonyOS_Study。
1.1.3重新打开DevEco Studio
在欢迎界面看到我们之前的哪个项目已经不可用了。直接点右侧的‘x’删除。
在欢迎界面点击Open...,找到我们修改后的'HarmonyOS_Study'所在位置。
有可能显示的还是之前的HarmonyHelloWorld,点一下上面的刷新即可。
然后点击OK,准备加载项目。
这个界面中直接点击'Trust Project'就行,我们自己写的项目。
这样,我们的项目就成功改名了,IDE会重新建立索引,所有内容都会保持完好。
1.2修改模块
模块不能直接去修改文件夹的名称。
1.2.1重命名模块
在IDE左侧的项目结构视图中找到entry模块。用鼠标右键点击他。
在弹出的菜单中,选择Refactor -> Rename...(重构 -> 重命名)。
点击后会弹出一个选择框,这时候选择Rename module。
在输入框中输入新的模块名称(全部使用小写字母、数字和下划线)
输入完成点击OK。
IDE会自动查找所有引用了 "entry" 的地方(比如配置文件build-profile.json5等)并进行修改。
修改完成。
修改完项目和模块备用,我们开始今天的正文。
二、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提供的最基本的状态管理装饰器。
核心作用:
持有状态:@State装饰的变量成为组件内部私有的、可观察的状态。
驱动更新:一旦@State变量的值发生改变,ArkUI框架会自动重新执行build函数,从而更新所有依赖该变量的UI组件。
简单来说,就是你用@State告诉框架:“听着,这个变量很重要,只要它变了,就刷新一下界面。”
四、计数器小案例
理论讲了这么多,让我们通过一个计数器例子来试下。
这个应用包含一个用来显示数字的Text和一个“+1”的Button。
4.1新建模块
选择一个Empty Ability:
配置模块信息:
模块名称(Module name)自行定义。
模块类型(Module type)我们选择feature。
模块类型中entry和feature的区别:
entry是应用的主模块和唯一入口。我们可以把他想象成一个房子的正门。
一个项目(应用)中,有且只能有一个entry类型的模块。(之前day1_helloworld模块已经是entry类型)
这个模块的作用就是应用安装后,用户在桌面上能看到的那个图标所启动的主程序。通常包含了应用的核心框架和启动界面。
而feature是功能模块或动态特性模块。我们可以把他看成一个个功能不同的独立的房间,比如卧室、厨房等等。
一个项目(应用)中,可以有零个或多个feature类型的模块。
这种类型的模块主要是用来承载相对独立的功能。
对于我们学习的项目,每一课的新代码都做成一个独立的feature模块最好。
虽然不能作为桌面图标直接启动,但我们可以配置IDE来独立运行和调试。
配置Ability
如果你接触过Android开发,你可以把Ability通俗的理解成Android的Activity,或者前端开发中的一个页面/视图。
这是一个负责跟用户交互、承载UI界面的基本单元。
刚才我们创建了一个feature模块,现在IDE需要知道这个模块的入口页面叫什么、以及他有什么特性。
Ability name是IDE自动生成(IDE根据模块名自动生成)的主页面的类名。后续会看到一个名字叫Day2_counterAbility.ets的文件。
关于这个计数器应用的UI和逻辑就在这个文件里。
Exported是个开关按钮,这个开关决定了该Ability是否可以被其他应用调用。
打开的情况下,意味着这个Ability不仅可以在我们的App内部被调用,理论上也可以被设备上的其他App唤起(需要正确的权限和配置)。
关闭的情况下,意味着这个Ability是私有的,只能在我们自己的应用内部访问和跳转。其他任何应用都无法直接调用他。
对于我们当前的课程学习和独立运行调试来说,这个选项肯定是要开的。如果关闭了,IDE可能没办法从外部(比如通过命令行)启动这个页面进行预览和调试。
模块创建完成:
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模块运行。
最后我们会在模拟器上看到我们新开发的计数器模块。
总结
今天,我们学习了ArkUI中最核心的几个UI组件Text、Button、Image和TextInput的基础用法。
我们理解了声明式UI的核心思想,并通过@State装饰器和计数器实例,掌握了如何实现由数据驱动的UI自动更新。