《鸿蒙原生应用开发实战》第三篇:沉浸式 UI 设计与组件化开发
2026/6/14 17:08:03 网站建设 项目流程

《鸿蒙原生应用开发实战》第三篇:沉浸式 UI 设计与组件化开发

前言

上一篇我们完成了数据模型和状态管理,本篇聚焦 UI 层面。作为一名鸿蒙开发者,ArkTS 的声明式 UI 语法和丰富的组件库让我们能够轻松实现精美的视觉设计。

「光遇·心境」应用的核心是光影沉浸体验,所有 UI 都围绕"光"和"色彩"展开。本文将详细拆解如何在 ArkTS 中实现:

  • 渐变背景与色彩方案
  • 毛玻璃效果与光晕
  • 卡片设计与阴影体系
  • 横向/纵向滚动布局
  • @Builder 组件化实战
  • 响应式尺寸与资源引用

一、整体的视觉风格设计

深色主题基调

整个应用采用深色主题,使用从深蓝到靛蓝的渐变色作为默认背景:

.linearGradient({direction:GradientDirection.Bottom,colors:[['#1a1a2e',0],// 深空蓝['#16213e',0.5],// 中蓝['#0f3460',1]// 深靛蓝]})

这种配色方案的好处:

  • 护眼:深色背景减少眩光,长时间使用更舒适
  • 突出内容:深色背景让彩色的场景卡片更加醒目
  • 沉浸感:像在黑暗的房间中欣赏光影作品

颜色资源管理

使用color.json统一管理颜色值:

{"color":[{"name":"start_window_background","value":"#1a1a2e"},{"name":"text_primary","value":"#FFFFFF"},{"name":"text_secondary","value":"#B0B0C0"},{"name":"text_accent","value":"#FFD700"}]}

在代码中通过$r()引用:

Text('副标题').fontColor($r('app.color.text_secondary'))// 引用 color.json// 对比硬编码Text('副标题').fontColor('#B0B0C0')// 硬编码,不推荐

二、沉浸式卡片设计

每日推荐卡片(大卡片)

首页的大卡片是应用的门面,我们使用了三种视觉技术:

1. 渐变背景 + 阴影
DailyRecommendCard(){Column(){// 卡片内容...}.width('88%').aspectRatio(1.6).borderRadius($r('app.float.large_radius'))// 24vp 大圆角.linearGradient({direction:GradientDirection.RightBottom,colors:[[this.dailyScene.colors[0],0],[this.dailyScene.colors[1],0.5],[this.dailyScene.colors[2],1]]}).shadow({radius:20,color:this.dailyScene.colors[0]+'66',// 半透明阴影offsetY:8})}

这里的shadow是点睛之笔 —— 阴影颜色取自场景的主色调并加上 40% 透明度,让"光"从卡片上"溢出":

场景"黎明破晓"的主色是 #FF6B35(橙色) 阴影颜色为 #FF6B3566(半透明橙色光晕)
2. 光晕装饰元素

在卡片内部,我们用椭圆和模糊效果制造出"发光"的视觉效果:

Stack(){// 光晕效果Ellipse().width('60%').height(40).fill(`rgba(255, 107, 53, 0.3)`).blur(30)// 30px 高斯模糊.position({x:'20%',y:'0%'})Column(){Text('每日推荐').fontSize($r('app.float.caption_font_size')).fontColor($r('app.color.text_accent'))// 金色.letterSpacing(4)// 字间距(高级感)Text(this.dailyScene.name).fontSize(32)// ... 更多内容}}

blur(30)是关键 API —— 相当于 CSS 的backdrop-filter: blur(),创建了梦幻的光晕效果。


3. 场景卡片(网格布局)

在场景列表页(ScenePage),我们使用 2 列网格布局展示卡片:

@BuilderSceneCard(item:SceneItem){Column(){// 色块预览(3个彩色小方块)Row(){ForEach(item.colors,(color:string)=>{Row().width(20).height(20).backgroundColor(color).borderRadius(4).margin({right:4})})}.margin({bottom:10})Text(item.name).fontSize(18).fontColor(Color.White).fontWeight(FontWeight.Bold)Text(item.desc).fontSize(11).fontColor($r('app.color.text_secondary'))Blank()// 自动撑开// 底部标签Row(){/* category 和 duration */}}.width('46%')// 每行两个,留出间距.aspectRatio(0.9)// 固定宽高比.padding(14).borderRadius($r('app.float.card_radius'))// 16vp.linearGradient({direction:GradientDirection.Bottom,colors:[[item.colors[0]+'33',0],// 20% 透明度[item.colors[1]+'22',0.7],[item.colors[2]+'11',1]]}).border({width:1,color:'rgba(255,255,255,0.1)'})// 微光边框}

设计技巧:在渐变颜色后添加+ '33'等透明度后缀,使卡片呈现半透明效果,隐约透出底部的深色背景,增强层次感。


三、横向滚动 —— 精选推荐区

首页的"精选推荐"区域使用了横向滚动容器:

// 横向可滚动的推荐列表Scroll(){Row(){ForEach(this.hotScenes,(item:SceneItem)=>{this.HotSceneCard(item)},(item:SceneItem)=>item.id.toString())}.padding({left:$r('app.float.padding_medium')})}.scrollable(ScrollDirection.Horizontal)// 设置为横向滚动.width('100%').height(200)

每个推荐卡片 150×180,纯渐变背景,居中展示名称和时长:

@BuilderHotSceneCard(item:SceneItem){Column(){Text(item.name).fontSize(18).fontColor(Color.White).fontWeight(FontWeight.Bold)Text(item.desc).fontSize(12).fontColor('rgba(255,255,255,0.7)')Text(`${item.duration}min`).fontSize(12).margin({top:8}).padding({left:10,right:10,top:3,bottom:3}).backgroundColor('rgba(255,255,255,0.12)').borderRadius(10)}.width(150).height(180).padding(16).justifyContent(FlexAlign.Center).borderRadius($r('app.float.card_radius')).linearGradient({direction:GradientDirection.Bottom,colors:[[item.colors[0],0],[item.colors[1],0.6],[item.colors[2],1]]})}

注意:横向滚动时,Row内的内容不要设置固定宽度,否则无法滚动。滚动容器的width'100%'即可。


四、详情页的沉浸式设计

详情页(DetailPage)是沉浸感最强的一页,分为上下两部分:

上半部分:渐变背景 + 光晕

// 55% 高度的渐变背景Stack(){// 渐变背景Column().width('100%').height('55%').linearGradient({direction:GradientDirection.Bottom,colors:[[this.scene.colors[0],0],[this.scene.colors[1],0.4],[this.scene.colors[2],1]]})// 光晕装饰 1Ellipse().width('80%').height(100).fill(Color.White).opacity(0.08).blur(40)// 光晕装饰 2Ellipse().width('50%').height(150).fill(this.scene.colors[0]).opacity(0.3).blur(50)// 前景内容Column(){// 返回按钮 + 收藏按钮Row(){/* ... */}Blank()// 场景名称、描述、标签Column(){/* ... */}}}.clip(newRect())// 裁剪溢出部分

两层不同大小和透明度的椭圆叠加,产生丰富的光晕层次感。

下半部分:可滚动详情

Scroll(){Column(){Text('场景描述').fontSize(16).fontColor(Color.White)Text(this.scene.detail).fontSize(14).fontColor($r('app.color.text_secondary'))// 分隔线Row().width('100%').height(1).backgroundColor('rgba(255,255,255,0.08)')// 色彩分析Text($r('app.string.color_analysis')).fontSize(16)Row(){ForEach(this.getColorRects(),(rect:ColorRect)=>{Column(){Row().width(48).height(48).backgroundColor(rect.color).borderRadius(12)Text(rect.name).fontSize(10)}})}// 推荐白噪音Text($r('app.string.recommend_sound'))Row(){/* 白噪音卡片 */}}}.backgroundColor('#1a1a2e')

这里使用了ForEach渲染色彩分析方块,每个方块显示场景的一个主题色:

getColorRects():ColorRect[]{constnames:string[]=['主色调','辅助色','点缀色'];constrects:ColorRect[]=[];for(leti=0;i<this.scene!.colors.length;i++){rects.push(makeColorRect(this.scene!.colors[i],i<names.length?names[i]:`${i+1}`));}returnrects;}

五、布局实战要点

1. Flex 布局技巧

ArkTS 中,RowColumn默认就是 Flex 容器:

// 左右两端对齐(左侧内容 → 空白 → 右侧内容)Row(){Text('左侧')Blank()// 自动填充剩余空间Text('右侧')}.width('100%')// 居中对齐(垂直和水平)Column(){Text('居中内容')}.width('100%').height('100%').justifyContent(FlexAlign.Center)// 垂直居中.alignItems(HorizontalAlign.Center)// 水平居中

2. 分隔线组件

ArkTS 中没有独立的<Divider>组件,我们用空Row实现:

Row().width('100%').height(1).backgroundColor('rgba(255,255,255,0.08)').margin({top:20,bottom:20})

3. 等分布局

统计卡片中使用layoutWeight实现三等分:

Row(){this.StatCard('💖',`${this.favCount}`,'已收藏').layoutWeight(1)// 等分权重this.DividerVertical()this.StatCard('👁️',`${this.visitedCount}`,'已浏览').layoutWeight(1)this.DividerVertical()this.StatCard('🎵','8','白噪音').layoutWeight(1)}

六、响应式尺寸与资源管理

float.json —— 统一管理尺寸

{"float":[{"name":"title_font_size","value":"28fp"},// 大标题{"name":"body_font_size","value":"14fp"},// 正文{"name":"caption_font_size","value":"12fp"},// 小标题{"name":"card_radius","value":"16vp"},// 卡片圆角{"name":"padding_large","value":"24vp"}// 大间距]}

fp 与 vp 单位

单位含义受字体缩放影响适用场景
fp字体像素字号
vp虚拟像素间距、尺寸
%百分比相对布局

引用方式

// 引用 float.json 中的尺寸.fontSize($r('app.float.body_font_size')).padding($r('app.float.padding_large')).borderRadius($r('app.float.card_radius'))

七、踩坑记录

坑1:Scroll 嵌套 Scroll 无法滚动

现象:横向 Scroll 内的内容超出仍无法滑动
原因:外层 Scroll 拦截了触摸事件
解决:避免嵌套可滚动容器,或设置scrollable(ScrollDirection.Horizontal)明确方向

坑2:aspectRatio不生效

原因:未设置宽度或高度其中一个
解决:设置width('46%')后,aspectRatio(0.9)会自动计算height = width / 0.9

坑3:ellipse 模糊效果性能问题

现象:多 Ellipse + blur 出现卡顿
解决:减少同时渲染的 Ellipse 数量,使用低模糊值,或使用opacity替代更多 blur


总结

本篇我们深入学习了:

  1. ✅ 深色主题 + 渐变背景设计
  2. ✅ 毛玻璃光晕效果实现
  3. ✅ 卡片设计体系(阴影、半透明、圆角)
  4. ✅ 横向/纵向滚动布局实战
  5. ✅ @Builder 组件化设计模式
  6. ✅ fp/vp 响应式尺寸管理

从视觉角度看,整个应用的 UI 统一且有特色 —— 深色底、彩色卡、光影晕。这也是应用名「光遇·心境」在 UI 层面的体现。

下一篇我们将聚焦页面之间的导航与参数传递,深入剖析路由器的工作原理。

下一篇预告:多页面导航与参数传递实战 —— router 路由机制、参数序列化、页面生命周期管理

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询