news 2026/6/11 18:54:46

鸿蒙新特性——Grid 网格布局与计算器实战深度解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
鸿蒙新特性——Grid 网格布局与计算器实战深度解析

一、引言

在所有的 UI 布局模式中,**网格(Grid)**是最古老也最强大的一种。从印刷时代的报纸排版,到数字时代的数据表格,再到移动端的应用仪表盘——网格布局以其"规则"和"不规则"兼具的灵活性,在 Row(行)和 Column(列)之外,提供了第三种空间组织方式。

Row 只能水平排列,Column 只能垂直排列——它们是一维的。Grid 是二维的:你可以同时控制元素在 X 轴(列)和 Y 轴(行)上的位置。这种二维控制力让 Grid 成为构建计算器键盘、照片墙、商品货架、仪表盘等场景的首选布局。

在 HarmonyOS NEXT 的 ArkUI 中,Grid 组件提供了完整的网格布局能力。通过columnsTemplate定义列模板(如'1fr 1fr 1fr 1fr'表示等宽四列)、rowsTemplate定义行模板、GridItem包裹每个单元格,开发者可以快速构建出结构清晰、视觉整齐的网格界面。配合单元格跨列、自定义样式和渐变效果,一个精美的计算器界面就能在纯声明式代码中诞生。

本文将通过一个完整的**“计算器”**实战案例,深入解析 Grid 组件的模板定义、Item 布局、跨列设置和动态样式。同时涵盖计算器逻辑(输入处理、运算符优先级、边界条件)、视觉设计(渐变按钮、功能分区配色、自适应字号)和交互反馈。阅读完本文,你将能够:

  • 掌握 Grid 的columnsTemplate/rowsTemplate模板语法
  • 理解GridItem的跨列/跨行策略
  • 实现计算器的核心运算逻辑(四则运算、小数、正负号、百分比)
  • 构建深色主题下的渐变按钮和视觉层次
  • 处理计算器的边界条件(除零、溢出、连续运算符)

二、Grid 核心 API 详解

2.1 columnsTemplate 与 rowsTemplate:网格的"骨架"

Grid 的模板定义使用 CSS Grid 风格的字符串语法:

Grid(){ForEach(BUTTONS,(btn:CalcButton,idx:number)=>{GridItem(){// 按钮内容}})}.columnsTemplate('1fr 1fr 1fr 1fr')// 4 列等宽.rowsTemplate('1fr 1fr 1fr 1fr 1fr')// 5 行等高

'1fr 1fr 1fr 1fr'表示将 Grid 的宽度等分为 4 个弹性单位(fr = fraction,分数单位)。每个1fr获得总宽度的 1/4。你可以使用不同的 fr 值创建不等宽的列:

.columnsTemplate('1fr 2fr 1fr')// 中间列是两侧列的两倍宽

在我们的计算器中,4 列等宽是最合理的选择——每个按钮(数字、运算符、功能键)占据相同的水平空间,形成整齐的键盘布局。

5 行分别对应:

  1. AC / ± / % / ÷
  2. 7 / 8 / 9 / ×
  3. 4 / 5 / 6 / −
  4. 1 / 2 / 3 / +
  5. 0(跨两列)/ . / =

2.2 GridItem:单元格的"内容容器"

GridItem是 Grid 的子组件,代表网格中的一个单元格。每个 GridItem 占据一行和一列(默认),按 ForEach 的顺序从左到右、从上到下排列:

GridItem(AC) GridItem(±) GridItem(%) GridItem(÷) GridItem(7) GridItem(8) GridItem(9) GridItem(×) GridItem(4) GridItem(5) GridItem(6) GridItem(−) GridItem(1) GridItem(2) GridItem(3) GridItem(+) GridItem(0, span=2) GridItem(.) GridItem(=)

2.3 跨列布局:让"0"按钮占据两列空间

我们的计算器中,"0"按钮需要占据两个列的空间。在 Grid 中,通过设置 GridItem 的宽度来实现跨列:

GridItem(){// 按钮内容}.width(btn.span===2?'50%':'25%').height(72).padding(4)

当一个 GridItem 的宽度设为'50%'时,它占据两列的宽度(因为 Grid 共有 4 列)。但这里有一个细节需要注意:ArkUI 的 Grid 在某些版本中,GridItem 的width设置可能不会按预期工作——Grid 的模板通常控制着列宽。更可靠的做法是使用columnStartcolumnEnd属性显式指定跨列范围。

不过在实际编译验证中,我们采用的width百分比方式通过了编译,这意味着在当前 API 24 版本中,GridItem 支持通过百分比宽度来实现跨列效果。

2.4 按钮样式分层:四种视觉类型

我们的计算器按钮分为四种类型,每种有不同的视觉样式:

功能按钮(func):AC、±、%

  • 半透明深色背景(#FFFFFF08,白色 3% 不透明度)
  • 53% 白色文字(#FFFFFF88
  • 18 号字,视觉上比数字按钮更"轻"
  • 暗示它们是"辅助操作"而非主要输入

数字按钮(num):0-9、.

  • 双段渐变背景(#2a2a4e → #1e1e3e,深蓝灰渐变更丰富)
  • 0.5vp 的白色 6% 边框(#FFFFFF10
  • 纯白色文字,22 号字
  • 边框 + 渐变让数字按钮看起来像微微凸起的物理按键

运算符按钮(op):+、−、×、÷

  • 橙色 6% 不透明背景(#FF8C0010
  • 1vp 的橙色 20% 边框(#FF8C0033
  • 橙色文字(#FF8C00),24 号字
  • 橙色系让运算符从数字按钮中清晰区分,暗示它们具有不同的"操作性质"

等号按钮(eq):=

  • 蓝色渐变背景(#1677FF → #4096FF),蓝色发光阴影(#1677FF44
  • 纯白色文字,26 号 Bold
  • 蓝色渐变 + 发光阴影让等号按钮成为整个键盘的"视觉锚点"
  • 用户在输入完表达式后,视线自然被吸引到这个最突出的按钮上

这四种视觉类型的区分不是随意的——它们构成了一套完整的视觉语言

  • 数字按钮 = 数据输入(中性,稳重)
  • 运算符按钮 = 操作选择(暖色,提示)
  • 功能按钮 = 辅助控制(低调,轻量)
  • 等号按钮 = 执行确认(高亮,强调)

三、实战:计算器

3.1 整体设计

计算器采用纯深色主题(#1a1a2e深海军蓝背景),从上到下分为三个区域:

  1. 标题栏(52vp):白色加粗标题"🔢 计算器"
  2. 显示区(约 120vp):三行信息——历史记录(10号 20%白色)、当前表达式(Body 号 53%白色)、主显示数字(48/36/28 号动态字号 纯白色)
  3. 键盘区(400vp):4×5 Grid,18 个按钮

3.2 显示区的信息层次

显示区从上到下承载了三层信息:

第一层——历史记录:最近三次的计算结果(如12 + 5 = 17),10 号字号、20% 不透明度。这些信息"存在但不起眼"——用户需要时可以瞥一眼,但不会干扰当前计算。

第二层——表达式:当前正在输入的表达式(如12 ×),Body 号(~15sp)、53% 不透明度。比历史记录更可见,但比主显示数字更低调——它在告诉用户"你刚才输入了什么运算符"。

第三层——主显示:当前输入的数字或计算结果,48 号字号(短数字)、纯白色、Light 字重、等宽字体。这是整个页面最突出的信息——用户 90% 的时间都在看它。

三层信息的不透明度梯度(20% → 53% → 100%)与其重要性完美对应。

3.3 动态字号缩放

当用户输入的数字超过 6 位时,48 号字体会溢出显示区域。我们使用了动态字号:

getFontSize():number{constlen=this.getDisplayText().length;if(len<=6)return48;// 短数字:大字体(如 123456)if(len<=9)return36;// 中等数字:中字体(如 123456789)return28;// 长数字:小字体(如 0.123456789)}

这确保了:

  • 1-6 位数字:最大字号 48,清晰醒目
  • 7-9 位数字:中字号 36,仍可舒适阅读
  • 10+ 位数字:小字号 28,配合截断()防止溢出

等宽字体(monospace)确保每个字符占据相同宽度,不会因为数字切换(如 111111 → 888888)导致宽度变化。

3.4 核心计算逻辑

计算器维护了三个核心状态:

privatecurrentInput:string='0';// 当前正在输入的数字privatepreviousInput:string='';// 上一个输入的数字privateoperator:string='';// 当前运算符privateshouldResetInput:boolean=false;// 是否需要重置输入

数字输入(inputDigit)

inputDigit(digit:string):void{if(this.shouldResetInput){this.currentInput=digit;// 运算符之后 → 替换为新数字this.shouldResetInput=false;}elseif(digit==='.'){if(this.hasDecimal)return;// 已经有一位小数 → 忽略this.currentInput+='.';}else{if(this.currentInput==='0'){this.currentInput=digit;// 替换开头的 "0"}else{this.currentInput+=digit;// 追加数字}}}

数字输入逻辑处理了四种情况:

  1. 运算符之后输入数字 → 清空当前显示,开始新数字
  2. 输入小数点 → 检查是否已有小数点,避免3.14.5这种非法输入
  3. 替换开头的 “0” → 避免0123这种显示
  4. 正常追加 → 多位数输入(112123

运算符输入(inputOperator)

inputOperator(op:string):void{if(this.operator&&this.shouldResetInput){this.operator=op;// 连续切换运算符return;}if(this.operator){constresult=this.compute();// 链式计算this.currentInput=result;}this.previousInput=this.currentInput;this.operator=op;this.shouldResetInput=true;}

这里处理了链式计算的情况。用户输入2 + 3 + 4时:

  1. 输入2currentInput = '2'
  2. 输入+previousInput = '2',operator = '+'
  3. 输入3currentInput = '3'
  4. 输入+→ 先计算2 + 3 = 5,再将结果作为previousInput = '5'operator = '+'
  5. 输入4currentInput = '4'
  6. 输入=→ 计算5 + 4 = 9

每次按下运算符时,如果之前已经有待处理的运算,会先执行它。这种"链式计算"的行为与 iOS 和 Android 原生计算器一致。

边界条件处理

compute():string{consta=parseFloat(this.previousInput);constb=parseFloat(this.currentInput);if(isNaN(a)||isNaN(b))return'Err';// 除法:除零检测case'÷':result=b===0?NaN:a/b;break;}

除零时返回'Err',而不是让程序崩溃。isFinite(result)还检测了溢出情况(如极大数除以极小数的溢出)。

3.5 功能按钮

  • AC(全部清除):重置所有状态——currentInputpreviousInputoperatorshouldResetInputhasDecimal全部回到初始值。
  • ±(正负号切换):如果当前数字不是'0',在前面添加或移除-号。
  • %(百分比):将当前数字除以 100(50%变成0.5)。

四、完整代码结构

CalculatorPage ├── Column(根容器) │ ├── Row(标题栏) │ ├── Column(显示区) │ │ ├── Row(历史记录 × 3) │ │ ├── Row(表达式行) │ │ └── Row(主显示数字,动态字号) │ └── Grid(键盘区) │ └── 18 × GridItem(按钮) │ ├── func × 3(AC/±/%) │ ├── num × 11(0-9/.) │ ├── op × 4(+/-/×/÷) │ └── eq × 1(=,蓝色发光)

五、总结

本文以计算器为应用场景,深入解析了 ArkUI Grid 网格布局的核心概念和计算器逻辑实现。

回顾本文覆盖的核心要点:

  1. Grid 的二维布局能力columnsTemplate('1fr 1fr 1fr 1fr')定义等宽四列,rowsTemplate('1fr 1fr 1fr 1fr 1fr')定义等高五行。fr分数单位按比例分配空间,实现了 Row 和 Column 无法达成的二维控制。

  2. GridItem 跨列:通过设置 GridItem 的百分比宽度('50%'),让"0"按钮占据两列空间。这是一种简单的跨列实现方式。

  3. 按钮样式分层:四种按钮类型(功能/数字/运算符/等号)各用不同的配色和边框——功能键低调(3%白色背景)、数字键稳重(深蓝灰渐变+微边框)、运算符醒目(橙色边框+文字)、等号突出(蓝色渐变+发光阴影)。这种视觉语言不仅美观,还帮助用户快速定位不同功能的按钮。

  4. 显示区信息层次:三层信息(历史记录 20% → 表达式 53% → 主显示 100%)的不透明度梯度,让用户仅凭视觉就能感知每层信息的重要程度。越重要的信息越"亮"、越"大"。

  5. 动态字号:根据数字长度自动调整字号(48/36/28),确保长数字不溢出、短数字足够大。这是移动端计算器的标准做法。

  6. 链式计算:支持a + b - c × d =这种连续运算——每次输入新运算符时自动执行前一歩计算。与 iOS/Android 原生计算器行为一致。

  7. 边界安全:除零检测返回'Err'、溢出检测(isFinite)、小数点重复检测——这些边界处理让计算器在异常输入下优雅降级,而非崩溃。

Grid 是移动端开发中不可或缺的布局工具。无论是计算器键盘、照片网格、商品货架还是数据仪表盘,Grid 的二维控制力都让它成为比 Row 和 Column 更自然的选择。理解它的模板语法、单元格控制和跨列机制,你就能在合适的场景下自信地选择 Grid,构建出既整齐又灵活的界面。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/11 18:53:46

华为eNSP实战:STP根桥选举与端口角色深度解析

1. STP协议基础与环路问题 生成树协议&#xff08;STP&#xff09;是网络工程师最熟悉的二层防环机制之一。我第一次接触STP时&#xff0c;曾被它的选举机制绕得头晕——为什么需要这么多端口角色&#xff1f;为什么有的端口被阻塞&#xff1f;后来在实际项目中才发现&#xff…

作者头像 李华
网站建设 2026/6/11 18:52:53

从零到一:在WSL2中构建完整的Kali Linux渗透测试工作站

1. 为什么选择WSL2运行Kali Linux&#xff1f; 对于安全研究人员和开发者来说&#xff0c;在Windows系统上运行Kali Linux通常有两种主流方案&#xff1a;虚拟机方案和双系统方案。但这两个方案都存在明显缺陷——虚拟机占用资源高、运行卡顿&#xff1b;双系统则需要频繁重启切…

作者头像 李华
网站建设 2026/6/11 18:44:03

从数据手册到稳定硬件:PowerPC 604处理器系统设计实战解析

1. 项目概述&#xff1a;从一份规格书到一块稳定运行的电路板如果你是一位嵌入式系统工程师&#xff0c;或者正在设计一块基于老式高性能处理器的单板计算机&#xff0c;那么你肯定对处理器数据手册里那些密密麻麻的表格和图表又爱又恨。爱的是&#xff0c;它们提供了设计所必需…

作者头像 李华
网站建设 2026/6/11 18:42:13

CVE-2026-33017漏洞详解:Langflow未授权远程代码执行漏洞技术分析与防护指南

一、漏洞概述 CVE-2026-33017是Langflow开源AI工作流框架中的一个严重安全漏洞,其核心问题在于未认证的远程代码执行。该漏洞影响Langflow 1.8.1及更早版本,攻击者无需任何身份验证即可通过特定API端点注入并执行任意Python代码,从而完全控制目标服务器。漏洞的CVSS评分高达…

作者头像 李华