当前位置: 首页 > news >正文

鸿蒙应用开发UI基础第八节: ArkTS声明式UI与页面基础结构

【学习目标】

  1. 理解声明式UI与命令式UI的核心差异,建立“数据驱动视图”的核心认知;
  2. 熟练掌握ArkTS页面核心结构(@Entry/@Component + build函数),能独立搭建标准页面骨架;
  3. 掌握vp/fp/px/lpx等尺寸单位的适用场景与适配原则,熟练使用系统原生尺寸转换方法,养成规范的单位使用习惯;
  4. 学会使用@State状态变量,实现“数据修改→UI自动刷新”的基础交互;
  5. 能快速定位并解决多根组件、UI不刷新、尺寸适配等新手高频页面问题。

【课前铺垫】

从本节开始,我们正式进入ArkTS UI开发的核心阶段:声明式UI开发。这是鸿蒙官方推荐的主流开发方式,也是让应用“看得见、可交互”的关键。本节将围绕“页面基础结构”展开,从页面核心组成、多设备尺寸适配,到数据驱动UI刷新,全方位解析声明式UI的核心逻辑,学好这些内容能为后续组件、布局、状态管理的学习筑牢基础。

先思考几个核心问题,带着问题学习更高效:

  • 为什么鸿蒙优先推荐声明式UI?它比传统命令式UI更高效的核心原因是什么?
  • 一个可运行的ArkTS页面,哪些结构是缺一不可的?少了会导致什么问题?
  • vp和fp是什么,有什么区别?为什么同样的尺寸在平板和手机上显示效果不同?
  • 修改变量后UI不刷新,大概率是哪里出了问题?
  • 特殊场景需要px与vp/fp互转时,如何使用系统原生方法实现?

一、工程结构

本节创建新工程AppDemo(基于鸿蒙API 12+/Stage模型),聚焦讲解UI页面的基本组成和声明式UI,工程核心目录结构如下:

AppDemo ├── AppScope # 应用全局配置目录 │ └── app.json5 # 全局配置(包名/应用名称/图标/版本等) ├── entry # 主模块目录(Entry HAP,应用核心代码包) │ ├── src/main │ │ ├── ets # ArkTS代码核心目录 │ │ │ ├── common # 新增:通用工具/常量目录 │ │ │ │ └── constants │ │ │ │ └── LayoutConstants.ets # 通用布局常量 │ │ │ ├── entryability # UIAbility组件目录 │ │ │ └── pages # Page页面目录 │ │ │ └── Index.ets # 工程默认首页 │ │ ├── resources # 资源文件目录(字体/文字/数值等放这里) │ │ │ └── base │ │ │ ├── element │ │ │ │ ├── float.json # 字体大小(fp)/数值型资源 │ │ │ │ └── string.json # 字符串型资源(页面文字/提示语等) │ │ │ └── profile │ │ └── module.json5 # 模块配置文件 └── 其他目录(构建/测试/配置相关)

二、声明式UI与命令式UI核心差异

两种UI开发模式的本质区别在于关注焦点UI更新逻辑,通过对比能更清晰理解声明式UI的优势:

1. 核心思维

  • 命令式UI:关注“怎么做”,需要开发者编写每一步操作指令(创建组件→设置属性→添加事件→手动更新),全程掌控UI的绘制和修改。
  • 声明式UI:关注“是什么”,只需描述UI的最终状态和数据关联关系,框架自动处理渲染、更新逻辑,数据变化时UI自动刷新。

2. 代码对比(实现“点击按钮变色”)

命令式UI(iOS原生Swift)
importUIKitclassViewController:UIViewController{varbtn:UIButton!override funcviewDidLoad(){super.viewDidLoad()btn=UIButton(type:.system)btn.setTitle("点击变色",for:.normal)btn.backgroundColor=UIColor(red:0/255,green:125/255,blue:255/255,alpha:1)btn.setTitleColor(.white,for:.normal)btn.frame=CGRect(x:100,y:200,width:200,height:80)btn.addTarget(self,action:#selector(btnClick),for:.touchUpInside)self.view.addSubview(btn)}@objc funcbtnClick(){btn.backgroundColor=UIColor(red:255/255,green:103/255,blue:0/255,alpha:1)}}
声明式UI(ArkTS)
// 引入通用布局常量import{LayoutConstants}from'../common/constants/LayoutConstants';// 声明页面入口@Entry// 声明组件@Component struct Index{// 状态变量:被@State修饰,数据与UI绑定,修改后自动刷新@State btnColor:string="#007dff";// build()内定义组件层级和属性build(){Column({space:20}){Button("点击变色").backgroundColor(this.btnColor).width(200)// 数值无单位默认vp.height(80).fontSize($r('app.float.font_size_normal')).onClick(()=>{// 点击修改颜色this.btnColor="#ff6700";});}.width(LayoutConstants.FULL_WIDTH)// 设置根组件宽度.height(LayoutConstants.FULL_HEIGHT)// 设置根组件高度.justifyContent(FlexAlign.Center);}}

三、ArkTS页面核心结构

一个可运行的ArkTS页面,必须包含入口标记、组件标记、结构体、UI构建函数四大核心部分,缺一不可。

1. 核心组成详解

组成部分作用说明核心禁忌
@Entry页面入口标识,告知系统该组件可作为独立页面加载一个文件仅能有一个@Entry,否则编译报错
@Component自定义组件标识,所有UI组件(页面级/子组件级)都需添加无此标识,结构体无法作为UI组件使用,编译直接报错
structArkTS组件的核心载体,基于结构体实现,无需继承类命名需大驼峰(如HomePage),小写开头会报语法警告
build()函数唯一的UI构建函数,仅负责描述UI结构1. 禁止编写业务逻辑(console.log/网络请求等);2. 必须只有一个根组件

2. 核心禁忌(新手必避)

  • ❌ 禁止一个文件多个@Entry:编译直接报错,需拆分组件或页面。
  • ❌ 禁止build()函数多根组件:多根组件报错In an '@Entry' decorated component, the 'build' method can have only one root node, which must be a container component. <ArkTSCheck>
  • ❌ 禁止在build()函数写非UI逻辑:如console.log、网络请求等,需封装到独立方法中,在事件回调(如onClick)或生命周期中调用。

四、多设备尺寸适配

4.1 为什么需要尺寸适配?—— 屏幕底层逻辑

(1)屏幕尺寸的核心定义

我们常说的6.7英寸手机11英寸平板,这里的“英寸”是屏幕发光区域对角线的物理长度

  • 单位换算:1英寸 = 2.54厘米
  • 计算公式:对角线长度=屏幕宽度2+屏幕高度2\text{对角线长度} = \sqrt{\text{屏幕宽度}^2 + \text{屏幕高度}^2}对角线长度=屏幕宽度2+屏幕高度2
(2)影响显示效果的三大核心概念
  • 物理尺寸:屏幕实际大小(英寸),决定设备握持感;
  • 分辨率:屏幕横向和纵向的物理像素总数(px),如1080×2340,代表屏幕“精细度”;
  • 像素密度(PPI):每英寸屏幕包含的物理像素数,是影响显示效果的核心参数。PPI数值越高,像素点越密集,屏幕显示越细腻(手机PPI≈400+,平板PPI≈200+)。
(3)适配的核心痛点

px(物理像素)是屏幕硬件的最小显示单元,直接与屏幕PPI绑定,导致相同px值在不同设备上视觉大小差异极大:

  • 100px按钮在普通手机(中等PPI)上大小适中;
  • 在高清屏上,因像素点更密,100px按钮视觉上极小;
  • 在低像素平板上,因像素点更疏,100px按钮视觉上极大。

为解决这种差异,鸿蒙引入了vp+lpx+fp三位一体的单位体系:

  • vp 解决“密度差异”,保证触控区域物理大小一致;
  • lpx 解决“尺寸差异”,保证布局比例适配不同宽度屏幕;
  • fp 解决“字体适配”,兼容系统字体缩放的无障碍需求。

4.2 鸿蒙核心尺寸单位(标准定义+核心价值)

单位完整名称定义核心价值适用场景
px物理像素1px 代表屏幕上的一个实际像素点,与设备分辨率直接相关。
例如:1920x1080 表示横向1920px,纵向1080px。
像素级精准控制❌ 仅用于Canvas绘制、图标资源等无需适配的场景,禁止普通布局使用
vp虚拟像素根据屏幕密度动态转换物理像素,公式:vp = px / (DPI/160)
默认单位(数值不带单位时默认为vp)。
保障不同密度设备的物理尺寸一致(如按钮触控区均为1cm²)✅ 组件尺寸(宽/高)、间距/边距,保证触控体验一致性
fp字体像素默认与 vp 等值(1fp=1vp),但会随系统字体设置缩放:
1fp = 1vp × scale(scale 为用户字体缩放系数)。
继承vp的密度适配,额外支持系统字体缩放✅ 所有文字的fontSize(唯一推荐单位),兼容无障碍设置
lpx逻辑像素基于屏幕实际宽度与逻辑宽度(designWidth,默认720)的比值计算:
1lpx = (屏幕宽度px / designWidth)
保障不同宽度设备的布局比例一致✅ 流式布局宽度(列表/栅格)、响应式间距,适配多尺寸屏幕

4.3 “vp+lpx”组合适配(实战核心原则)

lpx是鸿蒙适配多尺寸屏幕的关键单位,但必须与vp配合使用,二者缺一不可:

维度推荐单位核心目的示例场景
组件尺寸/间距vp保证触控体验(按钮、输入框等可交互区域物理大小一致)按钮高度80vp、组件间距16vp
宽度/布局比例lpx适配不同宽度屏幕(大屏/小屏布局比例协调)列表项宽度700lpx、栅格列宽120lpx
字体大小fp兼容系统字体缩放(无障碍适配)正文20fp、标题24fp

示例:720px设计稿的列表项宽度为360px → 代码中用360lpx,在1440px宽平板上自动变为720px,比例始终占屏幕宽度50%;同时列表项高度设为80vp,保证触控高度一致。

4.4 系统原生尺寸转换方法

鸿蒙系统在UIContext中内置了完整的尺寸转换方法,适配当前UI实例所在屏幕的像素比例:

  • vp2px(value: number): number:将vp单位值转换为px单位值;
  • px2vp(value: number): number:将px单位值转换为vp单位值;
  • fp2px(value: number): number:将fp单位值转换为px单位值;
  • px2lpx(value: number): number:将px单位值转换为lpx单位值;

调用规则:需通过当前组件的this.getUIContext()获取上下文后调用,且建议增加空值安全处理,避免上下文未就绪导致的异常:

// 安全调用示例:空值兜底避免崩溃constpxValue=this.getUIContext()?.vp2px(100)??100;// vp转pxconstlpxValue=this.getUIContext()?.px2lpx(200)??200;// px转lpx

注意:

  1. getUIContext()禁止在build()中直接调用,应在 onClick、onPageShow 等生命周期或事件回调中使用;
  2. 折叠屏设备中,lpx 转换值会随折叠状态动态变化,需做好适配处理;
  3. 若UI稿基于720px宽度设计,直接使用lpx可实现像素级等比缩放(如设计稿100px → 代码100lpx)。

4.5 核心配置文件

(1)全局通用布局常量

路径:entry/src/main/ets/common/constants/LayoutConstants.ets

/** * 全局通用布局常量 * 统一管理百分比、基础间距等,避免硬编码,提升适配一致性 */exportclassLayoutConstants{// 宽度常量publicstaticreadonlyFULL_WIDTH:string='100%';publicstaticreadonlyHALF_WIDTH:string='50%';// 高度常量publicstaticreadonlyFULL_HEIGHT:string='100%';// 基础间距/内边距(vp单位,保证触控体验)publicstaticreadonlyBASE_PADDING:number=24;publicstaticreadonlyBASE_SPACE:number=16;}
(2)字体大小资源文件(float.json)

路径:entry/src/main/resources/base/element/float.json

{"float":[{"name":"font_size_normal","value":"20fp"},{"name":"font_size_medium","value":"24fp"},{"name":"font_size_large","value":"30fp"},{"name":"font_size_counter","value":"26fp"},{"name":"button_size_width","value":"200vp"// 按钮宽度用vp,保证触控大小},{"name":"list_item_width","value":"700lpx"// 列表项宽度用lpx,适配屏幕比例},{"name":"list_item_height","value":"80vp"// 列表项高度用vp,保证触控体验}]}
(3)文字资源文件(string.json)

路径:entry/src/main/resources/base/element/string.json

{"string":[{"name":"page_title_main","value":"从页面结构、尺寸适配到数据驱动的全方位解析"},{"name":"page_title_home","value":"我的首页"},{"name":"btn_click_change","value":"点击变色"},{"name":"text_counter","value":"当前计数:"},{"name":"btn_change_color","value":"点击按钮修改颜色"}]}

4.6 适配黄金法则(标准化规范)

  1. 通用布局抽常量:100%/50%等全项目通用百分比、基础间距,统一抽离到LayoutConstants
  2. 字体大小必抽离:所有fontSize必须通过$r('app.float.xxx')引用float.json,禁止硬编码,且必须使用fp单位;
  3. 页面文字必抽离:所有页面/按钮/提示文字必须通过$r('app.string.xxx')引用string.json,禁止硬编码;
  4. 单位组合规范(核心):
    • 尺寸/间距用vp:保证按钮、输入框等可交互区域的触控体验;
    • 宽度/布局比例用lpx:适配不同宽度屏幕,保证布局比例协调;
    • 文字大小用fp:兼容系统字体缩放,满足无障碍适配要求;
    • 仅Canvas/图标资源可用px,禁止用于普通布局;
  5. 转换慎使用:特殊场景需单位转换时,仅使用系统原生UIContext方法,禁止自定义换算公式。

五、核心实战:@State与数据驱动UI(完整示例)

import{LayoutConstants}from'../common/constants/LayoutConstants';@Entry @Component struct Index{// @State 基础状态装饰器:修改后自动驱动UI刷新@State btnColor:string="#007dff";@State count:number=0;build(){Column({space:LayoutConstants.BASE_SPACE}){// 标题:字体用fp,兼容系统缩放Text($r('app.string.page_title_main')).fontSize($r('app.float.font_size_medium'))// 按钮:尺寸用vp保证触控,字体用fpButton($r('app.string.btn_click_change')).backgroundColor(this.btnColor).width($r('app.float.button_size_width'))// vp单位.height(80)// 默认vp,保证触控高度.fontSize($r('app.float.font_size_normal'))// fp单位.onClick(()=>{this.btnColor="#ff6700";});// 计数器展示:字体用fpText(){Span($r('app.string.text_counter'))Span(`${this.count}`)}.fontSize($r('app.float.font_size_counter'));// 计数器按钮组:宽度用lpx适配比例,高度用vp保证触控Row({space:20}){Button("计数增加").width($r('app.float.list_item_width'))// lpx单位,适配屏幕宽度.height($r('app.float.list_item_height'))// vp单位,保证触控.onClick(()=>{this.count++;});Button("计数减少").width($r('app.float.list_item_width')).height($r('app.float.list_item_height')).onClick(()=>{this.count--;});}.width('80%')// 百分比适配外层容器}.width(LayoutConstants.FULL_WIDTH).height(LayoutConstants.FULL_HEIGHT).justifyContent(FlexAlign.Center);}}

【新手排查】UI不刷新/适配异常常见原因:

  1. 动态变量未加@State装饰器(普通变量修改后无法触发UI刷新);
  2. 单位使用错误:如用px设置按钮尺寸,导致不同设备触控体验差;用vp设置宽屏布局,导致大屏比例失衡;
  3. 直接修改对象/数组内部属性(需整体重新赋值才能触发刷新);
  4. lpx使用时未结合vp,导致布局比例适配但触控区域大小失控。

六、内容总结

6.1 核心语法规则

  • ArkTS页面必须包含@Entry+@Component+struct+build()四大核心部分,缺一不可;
  • build()函数仅描述UI结构,禁止编写业务逻辑,且只能有一个根组件;
  • 多根组件解决方案:使用Column/Row/Stack等容器组件包裹所有子组件。

6.2 尺寸适配核心(vp+lpx+fp)

  • lpx是鸿蒙适配多尺寸屏幕的关键:保证不同宽度设备的布局比例一致,必须与vp配合使用;
  • vp保障物理尺寸/触控体验,lpx保障布局比例,fp兼容字体缩放;
  • 单位使用口诀:尺寸间距用vp,宽度比例用lpx,字体大小用fp,精准绘制用px。

6.3 资源管理规范

  • 通用布局常量抽离到LayoutConstants,字体大小/文字分别管理在float.json/string.json;
  • 不推荐硬编码数值和文字,提升代码可维护性和适配一致性。

七、代码仓库

  • 工程名称:AppDemo
  • 仓库地址:https://gitee.com/HarmonyOS-UI-Basics/harmony-os-ui-basics.git

八、下节预告

本节我们掌握了页面的基本组成、数据驱动逻辑、资源引用以及屏幕尺寸适配核心,下一节将学习线性布局容器(Column/Row),掌握主轴、交叉轴的对齐规则。

http://www.cnnetsun.cn/news/1312336.html

相关文章:

  • OpenClaw安装操作方法Windows,附vmware虚拟机文件。
  • Git for Windows
  • 分布式电源中风机(直驱与双馈)与光伏(mppt+双闭环及单功率闭环)的Matlab/Simul...
  • 机器学习和深度学习基础
  • AudioSeal Pixel Studio效果展示:M4A转WAV再加印全程无损保真验证
  • (133页PPT)数据中心基础设施规划设计(附下载方式)
  • YOLO系列算法改进 | 主干改进篇 | 替换EdgeViT边缘视觉Transformer网络 | 增强模型全局感知与多粒度特征融合,在小目标检测中保持轻量化与高精度 | ECCV 2022
  • 文献 环境因子是否会影响eDNA检测?
  • 鸿蒙常见问题分析五十:自定义Video组件的控制栏功能
  • 问题解决方法:铺铜修改后无反应的完整排查与解决步骤
  • IO及网络进程
  • Springboot集成kafka
  • 本科论文不用愁!Paperzz AI 毕业论文写作:四步搞定原创范文,图表公式全包含
  • 基于卷积神经网络-门控循环单元的时间序列预测 CNN-GRU 基于MATLAB环境 替换自己的...
  • 探秘风机螺栓预紧力的超声波检测:COMSOL 5.6仿真之旅
  • “南北合”随感
  • Vue+SpingBoot+MyBaits框架
  • 光电对抗:超构表面用于无源干扰的实践
  • AI简历分析:HR最在意的5大指标
  • 先甩个最核心的计数器代码镇楼
  • 【C++】C++类的幕后高手:友元、内部类、匿名对象与编译器优化深度解析
  • Python基于微信小程序的农产品溯源平台
  • 小白友好:Open-AutoGLM手机AI框架部署指南,10分钟跑通第一个自动化任务
  • Gemma-3-12b-it企业落地实践:中小团队低成本部署多模态AI助手方案
  • FR4 PCB透光LED反贴设计:丝印画中的隐藏式状态指示
  • ESP32-S3嵌入式HMI终端:LVGL驱动TFT+多模态感知设计
  • 支付宝周期扣款实战:从签约到代扣的全流程避坑指南(附代码示例)
  • TensorRT实战:FP16加速在边缘计算中的高效部署
  • STM32+Proteus仿真智能家居:手把手教你用虚拟串口模拟语音控制(附完整代码)
  • 构建网络安全培训系统:利用CosyVoice生成钓鱼邮件语音模拟与警示