HarmonyOS 3D 拆解分析布局重设计:间距、对齐与信息密度的平衡
前言
3D 拆解分析 App 经历了三版 UI:第一版"能跑",第二版"好看",第三版"好用"。这一版的改动集中在布局层面——没有加新功能,没有换颜色,只是调整了间距、对齐方式和信息密度。
但效果很明显:首页从"松散"变成了"紧凑",同样的内容在屏幕上不需要滚动就能看完;场景卡片从"抽象网格"变成了"直觉设备",用户一眼就知道该选哪个。
首页:信息密度的提升
从宽松到紧凑
旧版的间距:
- 标题区 margin-bottom: 22
- Hero Card margin-bottom: 28
- 场景卡片区 margin-bottom: 28
- 三步拆解 margin-bottom: 14
新版的间距:
- 标题区 margin-bottom: 20
- Hero Card margin-bottom: 22
- 场景卡片区 margin-bottom: 24
- 三步拆解 margin-bottom: 10

每个区域的间距都缩小了 2-8px。单看一个区域差别不大,但四个区域加起来节省了约 16px——差不多是半个卡片的高度。
CTA 按钮的布局变化
这是最大的改动。旧版的 CTA 按钮固定在屏幕底部(用 Stack({ alignContent: Alignment.Bottom })),新版把按钮放进了 Scroll 内容区:
// 旧版:固定在底部
Stack({ alignContent: Alignment.Bottom }) {
Scroll() { Column() { ... } }
Row() { Button('拍照开始分析') } // 固定底部
}
// 新版:在 Scroll 内容区
Scroll() {
Column() {
// ... 其他内容
Button() {
Row() {
Text('📸 ').fontSize(16)
Text('拍照开始分析').fontSize(15).fontWeight(FontWeight.Bold).fontColor(Color.White)
}
}
.width('100%').height(48).backgroundColor(A).borderRadius(24)
.shadow({ radius: 10, color: A + '35', offsetY: 4 })
.onClick(() => { this.push(this.cols(), this.rows(), '') })
.margin({ top: 26, bottom: 24 })
}
}

为什么这样改?
固定底部按钮在长内容页面有个问题:用户滚动时按钮始终可见,但按钮下面的内容(如果有的话)会被遮挡。把按钮放进 Scroll 后,它跟随内容流,用户滚动到哪它就在哪。
对于我们的场景——首页内容不多,按钮在最后一行——放进 Scroll 和固定底部的效果几乎一样。但代码更简单,不需要额外的 Stack 层。
场景卡片:从网格到设备
旧版的场景卡片信息是"3×3 网格 · 9 部件",新版改成"3×3 · 9部件"——去掉了"网格"两个字,更紧凑。
场景名称也缩短了:
- “平板/相机” → “相机”
- “大件设备” → “大件”
这些改动看起来微不足道,但在 2×2 的卡片网格里,每个字的宽度都很宝贵。缩短名称后,卡片不需要换行,布局更整齐。
场景卡片的对齐改进
旧版的单选按钮用 position 定位:
if (this.preset === idx) {
Text('✓').fontSize(12).fontWeight(FontWeight.Bold).fontColor(Color.White)
.position({ x: 6, y: 3 })
}
新版改用 Stack 居中:
Stack({ alignContent: Alignment.Center }) {
Row() {}.width(22).height(22).borderRadius(11)
.backgroundColor(this.preset === idx ? A : '#E8E5F0')
.border({ width: 2, color: this.preset === idx ? A : '#DDD' })
if (this.preset === idx) {
Text('✓').fontSize(12).fontWeight(FontWeight.Bold).fontColor(Color.White)
}
}

position 是绝对定位,需要手动计算偏移量(x: 6, y: 3)。Stack + Alignment.Center 是相对定位,自动居中。
好处:不需要硬编码偏移量,字号变了也不用重新调。
Flow 步骤:等宽分布
旧版的 Flow 步骤用 SymbolGlyph 箭头连接,每个步骤宽度固定 90px:
Row() {
this.Flow('1', '拍照上传', ...)
Column() { SymbolGlyph($r('sys.symbol.chevron_right')) }
this.Flow('2', '智能切分', ...)
Column() { SymbolGlyph($r('sys.symbol.chevron_right')) }
this.Flow('3', '3D 爆炸', ...)
}
.width('100%').justifyContent(FlexAlign.Center)
新版用 Text(' › ') 连接,每个步骤用 layoutWeight(1) 等宽:
Row() {
this.Flow('📸', '拍照', '拍摄物品\n清晰照片')
Text(' › ').fontSize(14).fontColor('#DDD')
this.Flow('🧩', '切分', '网格划分\n识别部件')
Text(' › ').fontSize(14).fontColor('#DDD')
this.Flow('💥', '爆炸', '部件飞散\n一目了然')
}
.width('100%').justifyContent(FlexAlign.SpaceEvenly)

FlexAlign.SpaceEvenly 让三个步骤均匀分布在 Row 中,两侧留白和中间间距相等。比固定宽度更灵活——屏幕变窄时步骤自动缩小,不会溢出。
Flow 步骤的简化
旧版的 Flow 有编号(1, 2, 3)和描述文字,新版去掉了编号,简化了描述:
旧版:
1 📸 拍照上传
拍摄物品的
清晰正面照片
新版:
📸
拍照
拍摄物品
清晰照片
去掉了编号——箭头 › 本身已经暗示了顺序,编号是冗余信息。简化描述后,Flow 卡片更紧凑,不需要固定宽度。
分析工作台:细节优化
复用上一版的设计
分析页面的布局基本延续了上一版的设计——头部、工作区、滑块、网格选择、部件清单。主要改动是代码层面的简化。
代码简化
旧版的变量名比较长:
@State imageUri: string = ''
@State itemName: string = ''
@State gridCols: number = 3
@State gridRows: number = 3
@State partCount: number = 0
@State intensity: number = 0
@State isPlaying: boolean = false
@State showPartList: boolean = false
新版缩短了变量名:
@State uri: string = ''
@State name: string = ''
@State cols: number = 3
@State rows: number = 3
@State count: number = 0
@State val: number = 0
@State on: boolean = false
@State list: boolean = false
对于组件内部的 @State 变量,短名称不会影响可读性——因为作用域小,上下文清晰。但在跨组件传递的 Props 或公共接口中,长名称更合适。
部件清单的深度条
部件清单的布局没有大改,但深度条的位置和样式有微调:
Row() {
Column() {
Text(c.idx.toString()).fontSize(11).fontWeight(FontWeight.Bold).fontColor(A)
}
.width(24).height(24).borderRadius(12).backgroundColor(AL)
.justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)
Row() {}.width(c.d * 42).height(5).borderRadius(3)
.backgroundColor(A + '50').margin({ left: 10, right: 10 })
Column() {
Text(c.n).fontSize(13).fontWeight(FontWeight.Medium).fontColor(T1)
Text(c.sub).fontSize(10).fontColor(T3)
}.alignItems(HorizontalAlign.Start).layoutWeight(1)
}
深度条的最大宽度从 c.d * 40 调整到 c.d * 42——多了 2px,在 100% 深度时差异不大,但在低深度时更明显。深度条的颜色从 A + '60' 降低到 A + '50',更柔和。
布局设计的原则
1. 间距要一致
整个页面的间距遵循一个规律:
- 区块之间:20-28px
- 元素之间:8-14px
- 内边距:12-18px
不遵循规律的间距会让页面看起来"不整齐"。比如标题和卡片之间 22px,卡片和卡片之间 28px,用户会觉得"这两个卡片之间的距离怎么比标题和卡片之间还大"。
2. 对齐要明确
每个 Row 和 Column 都有明确的对齐方式:
// 标题区:左侧对齐
Row() {
Column() { ... }.alignItems(HorizontalAlign.Start).layoutWeight(1)
Stack() { ... } // 右侧图标
}
// 场景卡片:左侧对齐
Row() {
Text(icon).fontSize(20)
Blank() // 推到两侧
Stack() { ... } // 右侧单选
}
layoutWeight(1) + Blank() 是 ArkUI 中实现"两端对齐"的标准模式。
3. 信息密度要适中
旧版的信息密度偏低——每个区域之间留白很多,用户需要滚动才能看到所有内容。新版提高了密度,但没有过度压缩。
经验法则:一个屏幕应该能展示完整的信息层级——标题 + 主操作 + 核心选项 + 辅助信息。如果需要滚动才能看到核心选项,说明密度太低;如果信息挤在一起看不清,说明密度太高。
踩坑记录
1. Column 的 space 参数
Column({ space: 8 }) {
Row({ space: 8 }) { ... }
Row({ space: 8 }) { ... }
}
Column({ space: 8 }) 会让子组件之间保持 8px 间距。这比手动给每个子组件加 .margin({ bottom: 8 }) 更简洁,而且最后一个子组件不会有多余的底部间距。
2. FlexAlign.SpaceEvenly vs FlexAlign.Center
// SpaceEvenly:均匀分布,两侧有留白
.justifyContent(FlexAlign.SpaceEvenly)
// Center:居中排列,两侧无留白
.justifyContent(FlexAlign.Center)
对于 Flow 步骤,SpaceEvenly 比 Center 更好——因为步骤之间需要间距,Center 会让步骤挤在一起。
3. Stack 的 alignContent vs alignItems
Stack({ alignContent: Alignment.Center }) {
// 子组件在 Stack 中居中
}
alignContent 控制子组件在 Stack 主轴方向的对齐。Alignment.Center 让子组件在水平和垂直方向都居中。
如果 Stack 只有一个子组件,alignContent 和不设置效果一样。如果有多个子组件,它们会重叠在居中位置。
4. Scroll 内的按钮高度
Button() { ... }
.width('100%').height(48)
.margin({ top: 26, bottom: 24 })
按钮放在 Scroll 内时,底部需要足够的 margin(24px),否则滚动到底部时按钮会贴着屏幕边缘,视觉上很挤。
写在最后
布局重设计的核心是"做减法"——去掉冗余的编号、缩短文字、压缩间距、简化对齐方式。每一处改动都很小,但组合在一起让整个页面更紧凑、更高效。
移动端屏幕寸土寸金,每一 px 的间距、每一个字的长度都值得仔细考量。不是越宽松越好,也不是越紧凑越好——关键是找到那个"信息完整且不拥挤"的平衡点。
更多推荐

所有评论(0)