【学习目标】理解声明式UI与命令式UI的核心差异建立“数据驱动视图”的核心认知熟练掌握ArkTS页面核心结构Entry/Component build函数能独立搭建标准页面骨架掌握vp/fp/px/lpx等尺寸单位的适用场景与适配原则熟练使用系统原生尺寸转换方法养成规范的单位使用习惯学会使用State状态变量实现“数据修改→UI自动刷新”的基础交互能快速定位并解决多根组件、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. 代码对比实现“点击按钮变色”命令式UIiOS原生SwiftimportUIKitclassViewController:UIViewController{varbtn:UIButton!override funcviewDidLoad(){super.viewDidLoad()btnUIButton(type:.system)btn.setTitle(点击变色,for:.normal)btn.backgroundColorUIColor(red:0/255,green:125/255,blue:255/255,alpha:1)btn.setTitleColor(.white,for:.normal)btn.frameCGRect(x:100,y:200,width:200,height:80)btn.addTarget(self,action:#selector(btnClick),for:.touchUpInside)self.view.addSubview(btn)}objc funcbtnClick(){btn.backgroundColorUIColor(red:255/255,green:103/255,blue:0/255,alpha:1)}}声明式UIArkTS// 引入通用布局常量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按钮视觉上极大。为解决这种差异鸿蒙引入了vplpxfp三位一体的单位体系vp 解决“密度差异”保证触控区域物理大小一致lpx 解决“尺寸差异”保证布局比例适配不同宽度屏幕fp 解决“字体适配”兼容系统字体缩放的无障碍需求。4.2 鸿蒙核心尺寸单位标准定义核心价值单位完整名称定义核心价值适用场景px物理像素1px 代表屏幕上的一个实际像素点与设备分辨率直接相关。例如1920x1080 表示横向1920px纵向1080px。像素级精准控制❌ 仅用于Canvas绘制、图标资源等无需适配的场景禁止普通布局使用vp虚拟像素根据屏幕密度动态转换物理像素公式vp px / (DPI/160)。默认单位数值不带单位时默认为vp。保障不同密度设备的物理尺寸一致如按钮触控区均为1cm²✅ 组件尺寸宽/高、间距/边距保证触控体验一致性fp字体像素默认与 vp 等值1fp1vp但会随系统字体设置缩放1fp 1vp × scalescale 为用户字体缩放系数。继承vp的密度适配额外支持系统字体缩放✅ 所有文字的fontSize唯一推荐单位兼容无障碍设置lpx逻辑像素基于屏幕实际宽度与逻辑宽度designWidth默认720的比值计算1lpx (屏幕宽度px / designWidth)。保障不同宽度设备的布局比例一致✅ 流式布局宽度列表/栅格、响应式间距适配多尺寸屏幕4.3 “vplpx”组合适配实战核心原则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()获取上下文后调用且建议增加空值安全处理避免上下文未就绪导致的异常// 安全调用示例空值兜底避免崩溃constpxValuethis.getUIContext()?.vp2px(100)??100;// vp转pxconstlpxValuethis.getUIContext()?.px2lpx(200)??200;// px转lpx注意getUIContext()禁止在build()中直接调用应在 onClick、onPageShow 等生命周期或事件回调中使用折叠屏设备中lpx 转换值会随折叠状态动态变化需做好适配处理若UI稿基于720px宽度设计直接使用lpx可实现像素级等比缩放如设计稿100px → 代码100lpx。4.5 核心配置文件1全局通用布局常量路径entry/src/main/ets/common/constants/LayoutConstants.ets/** * 全局通用布局常量 * 统一管理百分比、基础间距等避免硬编码提升适配一致性 */exportclassLayoutConstants{// 宽度常量publicstaticreadonlyFULL_WIDTH:string100%;publicstaticreadonlyHALF_WIDTH:string50%;// 高度常量publicstaticreadonlyFULL_HEIGHT:string100%;// 基础间距/内边距vp单位保证触控体验publicstaticreadonlyBASE_PADDING:number24;publicstaticreadonlyBASE_SPACE:number16;}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 适配黄金法则标准化规范通用布局抽常量100%/50%等全项目通用百分比、基础间距统一抽离到LayoutConstants字体大小必抽离所有fontSize必须通过$r(app.float.xxx)引用float.json禁止硬编码且必须使用fp单位页面文字必抽离所有页面/按钮/提示文字必须通过$r(app.string.xxx)引用string.json禁止硬编码单位组合规范核心尺寸/间距用vp保证按钮、输入框等可交互区域的触控体验宽度/布局比例用lpx适配不同宽度屏幕保证布局比例协调文字大小用fp兼容系统字体缩放满足无障碍适配要求仅Canvas/图标资源可用px禁止用于普通布局转换慎使用特殊场景需单位转换时仅使用系统原生UIContext方法禁止自定义换算公式。五、核心实战State与数据驱动UI完整示例import{LayoutConstants}from../common/constants/LayoutConstants;Entry Component struct Index{// State 基础状态装饰器修改后自动驱动UI刷新State btnColor:string#007dff;State count:number0;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不刷新/适配异常常见原因动态变量未加State装饰器普通变量修改后无法触发UI刷新单位使用错误如用px设置按钮尺寸导致不同设备触控体验差用vp设置宽屏布局导致大屏比例失衡直接修改对象/数组内部属性需整体重新赋值才能触发刷新lpx使用时未结合vp导致布局比例适配但触控区域大小失控。六、内容总结6.1 核心语法规则ArkTS页面必须包含EntryComponentstructbuild()四大核心部分缺一不可build()函数仅描述UI结构禁止编写业务逻辑且只能有一个根组件多根组件解决方案使用Column/Row/Stack等容器组件包裹所有子组件。6.2 尺寸适配核心vplpxfplpx是鸿蒙适配多尺寸屏幕的关键保证不同宽度设备的布局比例一致必须与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掌握主轴、交叉轴的对齐规则。