【口算王|16】HarmonyOS ArkTS 多设备布局实战:适配手机、平板和 PC/2in1 的窗口变化

【口算王|16】HarmonyOS ArkTS 多设备布局实战:适配手机、平板和 PC/2in1 的窗口变化

证据边界:本文依据 D:/huawei/one16-11 当前可读取源码整理;本轮未执行构建、模拟器、真机、折叠展开、旋转、分屏、自由窗口、平板或 PC/2in1 验证。验证矩阵和修复路径属于建议实现,不代表多设备验收已通过。

多设备适配不是把手机页面等比例拉宽。手机竖屏适合单列内容和底部导航,平板横屏更需要网格与双栏,PC/2in1 还会频繁改变窗口尺寸。如果只依据设备名称决定布局,分屏、小窗、折叠屏展开和桌面拖拽都可能落入错误形态。

口算王已经建立两类窗口信号:BreakpointSystem 用 MediaQuery 把窗口划分为 sm/md/lg,页面再通过 onAreaChange() 记录实际内容宽度。首页、题库列表、挑战页、题库详情和学习统计会在 lg 且局部宽度不小于 700 时切换宽屏结构;分类页则独立按 720 宽度调整卡片比例。源码中也有一个真实缺口:Index 的条件把 sm/md/lg 全部判进底部导航分支,导致已编写的侧边导航 else 永远无法到达。

本文基于口算王项目 D:\huawei\one16-11 的真实源码,复核 EntryAbility.etsBreakpointSystem.etsIndex.etsHomePage.etsBankListPage.etsBankDetailPage.etsCategoryPage.etsExamTab.etsExamResultPage.etsLearningStatsPage.ets。包名 com.jiaweikang.one16 是本文草稿核验使用的唯一标记。项目目标和兼容 SDK 均为 HarmonyOS 6.0 系列,本文讨论的方法适用于 HarmonyOS 5.0 及以上 ArkUI Stage 模型。

本文将完成六件事:

  • 拆解全局断点与局部容器宽度的分工;
  • 验证断点注册、更新和释放的完整生命周期;
  • 分析手机列表、宽屏网格和详情双栏的真实切换;
  • 说明底部安全区如何避免系统手势区遮挡;
  • 找出侧边导航不可达、md 利用不足等当前缺口;
  • 给出手机、平板和 PC/2in1 的可执行验证矩阵。

一、多设备适配首先是窗口问题

HarmonyOS 同一应用可能运行在手机、折叠屏、平板和 2in1 上。即便是同一台设备,旋转、分屏和自由窗口也会改变可用空间。因此布局决策应回答两个问题:

  1. 当前窗口属于哪个宽度档位;
  2. 当前组件实际拿到了多少内容宽度。

口算王没有直接判断设备型号,而是用媒体查询和组件测宽组合。这比"平板就三列"更稳健,因为平板分屏后可能只剩手机宽度,2in1 小窗也不应强行显示三列。

信号 范围 用途
sm width <= 600vp 手机或窄窗
md 600vp < width <= 840vp 折叠展开或小平板
lg width > 840vp 大平板或 2in1
pageWidth 页面实际 Area 宽度 防止内容区被侧栏等再次压缩

二、BreakpointSystem 集中管理全局窗口档位

BreakpointSystem 位于基础能力库 libraryb,定义了严格的联合类型:

复制代码
#export type BreakpointType = 'sm' | 'md' | 'lg'export type BreakpointType = 'sm' | 'md' | 'lg'

注册时创建三个 MediaQuery 监听器:

复制代码
#matchMediaSync('(width<=600vp)')
matchMediaSync('(600vp<width<=840vp)')matchMediaSync('(width<=600vp)')
#matchMediaSync('(width<=600vp)')
matchMediaSync('(600vp<width<=840vp)')matchMediaSync('(600vp<width<=840vp)')

matchMediaSync('(840vp<width)')

任何监听结果变为匹配时,系统调用 update(),把当前断点写入:

复制代码
#AppStorage.setOrCreate<string>('currentBreakpoint', bp)AppStorage.setOrCreate<string>('currentBreakpoint', bp)

页面通过 @StorageLink('currentBreakpoint') 接收变化,因此窗口跨越阈值时,所有消费页面可以重新计算布局。

三、初始判断不能等待第一次 change

只监听 change 事件还不够。应用首次启动时,窗口可能已经处于平板宽度,如果等待下一次窗口变化,页面会一直保留默认 sm

当前 register 在绑定监听后立即检查:

复制代码
#if (smListener.matches) {
  update('sm')if (smListener.matches) {
#if (smListener.matches) {
  update('sm')  update('sm')

} else if (mdListener.matches) {

复制代码
#update('md')
} else {  update('md')
#update('md')
} else {} else {

update('lg')

复制代码
#}}

这样首帧前就能获得正确断点。三个区间彼此覆盖且不重叠,合法窗口总能落入一个分支。

四、监听生命周期与 UIAbility 对齐

EntryAbility.onCreate() 调用 BreakpointSystem.register()onDestroy() 调用 unregister()。这使监听器生命周期跟随应用能力,而不是由某个页面临时维护。

复制代码
#onCreate(): void {
  BreakpointSystem.register()onCreate(): void {
#onCreate(): void {
  BreakpointSystem.register()  BreakpointSystem.register()

}

复制代码
#onDestroy(): void {
  BreakpointSystem.unregister()onDestroy(): void {
#onDestroy(): void {
  BreakpointSystem.unregister()  BreakpointSystem.unregister()

}

集中注册的好处是页面不重复创建 MediaQueryListener。页面只关心断点值,不关心监听器如何产生。

当前 unregister 对三个 listener 调用 off('change'),但没有把静态引用重置为 null。正常单次能力生命周期不会直接造成页面问题;若未来存在重复 register,需要进一步验证是否会产生重复监听。增强方案可以增加幂等保护,并在释放后清空引用。

五、为什么还需要 onAreaChange

全局窗口宽度不等于页面内容宽度。根布局可能增加侧栏、内边距或分栏,导致子页面真正可用空间更小。

HomePage、BankListPage、ExamTab、BankDetailPage 和 LearningStatsPage 都维护:

复制代码
#@State pageWidth: number = 360@State pageWidth: number = 360

.onAreaChange((oldArea: Area, newArea: Area) => {

复制代码
#const width = Number(newArea.width)
  if (width > 0) this.pageWidth = width  const width = Number(newArea.width)
#const width = Number(newArea.width)
  if (width > 0) this.pageWidth = width  if (width > 0) this.pageWidth = width

})

它们进入宽屏布局的条件不是只看 lg

复制代码
#return this.currentBp === 'lg' && this.pageWidth >= 700return this.currentBp === 'lg' && this.pageWidth >= 700

这是双重门槛:

  • lg 确认整个窗口处于宽屏档;
  • pageWidth >= 700 确认当前页面确实拥有足够内容宽度。

六、首页在宽屏下把题库列表变为三列

HomePage 的题库区域在普通布局中顺序展示卡片,在宽屏条件成立时改用可换行 Flex,每个题库容器宽度为 32%

复制代码
#Flex({
  wrap: FlexWrap.Wrap,Flex({
#Flex({
  wrap: FlexWrap.Wrap,  wrap: FlexWrap.Wrap,

justifyContent: FlexAlign.SpaceBetween

复制代码
#}) {
  ForEach(BANKS, (bank: Bank) => {}) {
#}) {
  ForEach(BANKS, (bank: Bank) => {  ForEach(BANKS, (bank: Bank) => {

Column() {

复制代码
#BankCard({ bank: bank })
    }      BankCard({ bank: bank })
#BankCard({ bank: bank })
    }    }

.width('32%')

复制代码
#})
}  })
#})
}}

三列不是固定像素,因此窗口继续变宽时卡片会随容器增长。SpaceBetween 负责分配列间空隙,最后一行也能保持稳定起点。

在窄窗中继续使用纵向结构,避免三列卡片被压到无法阅读。

七、题库列表和挑战页复用同一宽屏规则

BankListPage 与 ExamTab 同样使用 lg + pageWidth >= 700,并把卡片宽度设为 32%。这种一致规则减少同一应用不同主 Tab 在窗口变化时的视觉跳跃。

复用的不是一个抽象组件,而是一致的决策契约:

复制代码
#全局断点是 lg
  AND全局断点是 lg
#全局断点是 lg
  AND  AND

局部内容宽度 >= 700

复制代码
#-> 三列网格
否则  -> 三列网格
#-> 三列网格
否则否则

-> 单列布局

如果后续调整阈值,最好把规则集中为共享工具,避免三个页面分别修改后产生差异。

八、题库详情不是加列,而是重新组织阅读顺序

BankDetailPage 的宽屏适配更有价值。手机上内容按顺序排列:

  1. 题库封面;
  2. 学习进度;
  3. 题库简介;
  4. 训练重点;
  5. 章节列表。

宽屏时改为左右两块独立 Scroll:

  • 左侧 38%:封面、学习摘要、简介;

  • 右侧剩余空间:训练重点与章节;

  • 两列间距 20;

  • 两边可以独立滚动。

    #Row({ space: 20 }) {
    Scroll() { /* hero + summary + profile / }Row({ space: 20 }) {
    #Row({ space: 20 }) {
    Scroll() { /
    hero + summary + profile / } Scroll() { / hero + summary + profile */ }

.width('38%')

复制代码
#Scroll() { /* focus + chapters */ }
    .layoutWeight(1)  Scroll() { /* focus + chapters */ }
#Scroll() { /* focus + chapters */ }
    .layoutWeight(1)    .layoutWeight(1)

}

这不是把手机长页机械切半,而是按信息职责重排:左侧稳定展示题库身份,右侧承载主要操作内容。

九、宽屏 Hero 高度也随布局调整

题库详情 Hero 在宽屏下高度为 260,普通布局为 210。图片使用 ImageFit.Cover,同时保持圆角和底部渐变层。

高度变化服务于更宽卡片的视觉比例,而不是简单放大所有元素。标题仍限制单行省略,副标题限制两行,避免长文本覆盖标签。

这说明自适应不仅是列数,还包括媒体比例、文本行数和信息密度。

测试这一块时还要检查封面主体是否在 Cover 裁切后仍位于可见区域。手机与宽屏使用同一资源,但容器宽高比不同;如果关键内容靠近图片边缘,260 高度并不能自动保证信息完整。当前代码保证了比例填充和文字可读,具体裁切效果仍需要用真实题库封面逐张目视确认。

十、学习统计同时改变指标与列表结构

LearningStatsPage 在宽屏下做了两处重排:

  • 概览指标由手机上的两行两列变为一行四项;
  • 各年级进度由单列列表变为三列卡片。

同一个 useGridLayout() 控制两处变化,保证页面密度同步提升。手机仍保留更大的单项宽度,避免数字、标签和进度条互相挤压。

DataSummary 的三个统计卡始终使用 layoutWeight(1),能够按剩余空间均分;文字使用 maxLines(1) 与省略,降低窄屏溢出风险。

十一、分类页按局部宽度独立适配

CategoryPage 没有链接全局断点,而是直接依据 pageWidth

复制代码
#private itemWidth(): string {
  return this.pageWidth >= 720 ? '31.5%' : '47.8%'private itemWidth(): string {
#private itemWidth(): string {
  return this.pageWidth >= 720 ? '31.5%' : '47.8%'  return this.pageWidth >= 720 ? '31.5%' : '47.8%'

}

小于 720 时每行两张卡,大于等于 720 时每行三张卡。Flex 使用 Wrap 和 SpaceBetween。

这种写法适合简单局部网格,但阈值与全局断点体系并不完全一致。例如窗口处于 md 的 720 至 840 区间时,分类页已经三列,而其他页面仍保持非宽屏布局。它不是运行错误,却会带来中等窗口下的信息密度不一致。

十二、ExamResultPage 依赖流式与滚动适配

挑战结果页没有读取 currentBreakpoint,也没有显式宽屏分栏。它通过以下方式保证窄屏可达:

  • 根容器 100% 宽高;
  • 主内容放在 Scroll;
  • 答题格使用可换行 Flex;
  • 三个指标通过 layoutWeight 均分;
  • 底部操作栏保留安全区;
  • 长内容不会把按钮永久推出屏幕。

这属于"弹性布局",不是"断点重排"。在手机上有效,在宽屏上则可能出现内容过宽、阅读行长过大。后续增强可以为结果页增加最大内容宽度或左右分栏,但不能声称当前已实现。

十三、安全区是多设备适配的一部分

多设备页面除了宽度,还要避免系统导航条和手势区遮挡。EntryAbility 获取:

  • TYPE_NAVIGATION_INDICATOR
  • TYPE_SYSTEM

底部取导航指示区和系统底部区域的较大值,写入 navigationIndicatorHeightPx。页面再转换为 vp:

复制代码
#return Math.max(
  Sizes.BOTTOM_NAV_MIN_PADDING,return Math.max(
#return Math.max(
  Sizes.BOTTOM_NAV_MIN_PADDING,  Sizes.BOTTOM_NAV_MIN_PADDING,

this.getUIContext().px2vp(this.navigationIndicatorHeightPx)

复制代码
#))

这样底部按钮、空白区和导航栏至少保留项目定义的最小间距,同时尊重设备真实避让区。

十四、避免区监听会随窗口变化更新

EntryAbility 在 WindowStage 创建时取得主窗口,首次调用 updateNavigationIndicatorHeight(),随后监听 avoidAreaChange

当系统导航模式、窗口状态或设备形态改变时,回调重新计算顶部和底部区域。onDestroy 和 onWindowStageDestroy 都会按当前引用解除监听。

异常路径会把高度回退到 0 并记录非敏感警告。页面自己的 Math.max() 仍提供最小底部间距,因此获取系统区域失败时不会完全贴底。

十五、Index 的侧边导航当前实际不可达

Index 已编写两套主导航:

  • BottomNavItem:手机底部五 Tab;
  • SideNavItem:平板或折叠屏侧栏。

但 build 中的判断是:

复制代码
#if (currentBp === 'sm' ||
    currentBp === 'md' ||if (currentBp === 'sm' ||
#if (currentBp === 'sm' ||
    currentBp === 'md' ||    currentBp === 'md' ||

currentBp === 'lg') {

复制代码
#// 底部导航
} else {  // 底部导航
#// 底部导航
} else {} else {

// 侧边导航

复制代码
#}}

BreakpointType 的所有合法值只有 sm、md、lg,因此 if 永远为真,else 永远不可达。也就是说:

  • 宽屏页面内部的三列和双栏会生效;
  • 根导航仍会保持底部 Tab;
  • 侧边栏代码存在,但当前不会显示。

这一区分必须写清,不能把"代码中有 SideNavItem"描述成"平板已使用侧边栏"。

十六、最小修正是明确导航切换阈值

若产品目标是 lg 使用侧边栏,最小修正为:

复制代码
#if (this.currentBp !== 'lg') {
  this.PhoneLayout()if (this.currentBp !== 'lg') {
#if (this.currentBp !== 'lg') {
  this.PhoneLayout()  this.PhoneLayout()

} else {

复制代码
#this.WideLayout()
}  this.WideLayout()
#this.WideLayout()
}}

但是否让 md 也进入侧栏需要结合折叠屏体验决定。600 至 840vp 的空间未必适合 80vp 侧栏,保留底部导航通常更稳妥。

修正后还要测试内容页的实际宽度。加入 80vp 侧栏后,原本大于 840vp 的窗口内容区可能接近 760vp,仍可通过页面自身 pageWidth >= 700 校验决定是否三列。

十七、md 断点目前主要承担"保持手机结构"

当前多数宽屏页面只在 lg 时切换。md 虽然被识别并广播,但主要继续使用单列或底部导航。

这并非完全无用:md 让系统知道窗口已经离开手机窄屏,只是页面尚未针对它增加专门结构。后续可以按真实测试决定:

  • 首页在 md 使用两列题库;
  • 详情页保持单列但扩大左右边距;
  • 分类页统一为两列或三列策略;
  • 主导航继续底部;
  • 控制最大内容宽度,避免单列无限拉伸。

这些属于增强方向,当前源码没有统一 md 专属布局。

十八、断点与局部阈值需要命名统一

目前出现两组阈值:

  • BreakpointSystem:600、840;
  • 页面局部:700、720。

这种组合有合理性,但魔法数字分散会增加维护成本。建议集中为语义常量:

复制代码
#const BREAKPOINT_MD = 600
const BREAKPOINT_LG = 840const BREAKPOINT_MD = 600
#const BREAKPOINT_MD = 600
const BREAKPOINT_LG = 840const BREAKPOINT_LG = 840

const GRID_MIN_CONTENT_WIDTH = 700

复制代码
#const CATEGORY_THREE_COLUMN_WIDTH = 720const CATEGORY_THREE_COLUMN_WIDTH = 720

更进一步,可让组件根据最小卡片宽度和间距计算列数,而不是为每个页面手写百分比。

十九、百分比网格要验证极端宽度

三列卡片使用 32% 或 31.5%,配合 SpaceBetween。在常规平板宽度下简单有效,但在非常宽的 2in1 窗口中,卡片可能过宽,文本行长和图片比例也会变化。

可增加最大内容宽度:

复制代码
#Column() {
  this.Content()Column() {
#Column() {
  this.Content()  this.Content()

}

复制代码
#.constraintSize({ maxWidth: 1280 })
.width('100%').constraintSize({ maxWidth: 1280 })
#.constraintSize({ maxWidth: 1280 })
.width('100%').width('100%')

也可以让外层居中,避免内容无限铺满。当前源码没有统一 maxWidth,发布前需要在大窗口检查视觉密度。

二十、触控与 2in1 输入方式不能只看尺寸

PC/2in1 除了大屏,还可能使用鼠标和键盘。现有卡片主要通过 onClick() 操作,触控目标尺寸总体明确,但源码中没有系统化的键盘焦点、方向键导航、Hover 状态和快捷键设计。

因此"适配 PC/2in1"在当前项目中主要指窗口布局重排和安全区,不应扩大为完整桌面输入体验。后续增强应验证:

  • Tab 键焦点顺序;
  • Enter/Space 激活;
  • 鼠标 Hover 反馈;
  • 滚轮行为;
  • 小窗口拖拽时的连续重排;
  • 键盘出现后底部按钮可达。

二十一、四层职责让适配逻辑可复用

当前实现可以归纳为四层:

EntryAbility

在能力生命周期中注册断点系统、监听窗口避免区,并负责释放资源。

BreakpointSystem

把原始媒体查询结果归一为 sm、md、lg,通过 AppStorage 广播。

Page Area

页面通过 onAreaChange 获取真实内容宽度,防止仅凭全局窗口误判。

Layout Builders

页面根据条件选择单列、三列、双栏或流式结构。Builder 只负责声明 UI,不承担窗口监听细节。

二十二、窗口变化测试要连续,而不是只截三张图

多设备布局最容易在阈值附近出问题。测试不能只用 360、800、1200 三个固定宽度,还要连续拖动窗口。

建议重点观察:

  • 599 -> 601:sm 到 md;
  • 699 -> 701:局部宽屏门槛;
  • 719 -> 721:分类页两列到三列;
  • 839 -> 841:md 到 lg;
  • 加入侧栏后内容宽度是否又跌破 700;
  • 横竖屏切换是否保留当前 Tab;
  • 反复跨阈值是否出现重复监听或状态抖动。

每次切换都要检查文本、图片、按钮、滚动位置和点击区域,而不只是列数。

二十三、手机、平板和 2in1 验证矩阵

场景 导航 内容布局 必查项
手机竖屏 底部 Tab 单列 手势区、长文本、按钮可达
手机横屏 当前仍底部 Tab 多数单列 垂直空间、Scroll
折叠展开/md 当前仍底部 Tab 多数单列,分类可能三列 密度一致性
大平板/lg 当前仍底部 Tab 三列/双栏已生效 侧栏不可达问题
2in1 大窗 当前仍底部 Tab 三列/双栏 最大宽度、鼠标键盘
2in1 小窗 随断点回落 单列 连续拖拽稳定性
系统导航变化 同上 同上 底部避让高度更新

二十四、性能关注布局重建而不是单次判断

MediaQuery 变化频率不高,currentBp === 'lg' 的判断成本可以忽略。真正需要关注的是:

  • onAreaChange 连续触发;
  • 大列表跨阈值时整体重建;
  • 图片在三列与单列间重新布局;
  • 两个独立 Scroll 的状态;
  • 统计方法在每次 build 中重复计算。

可以在调试环境记录断点、局部宽度、布局模式与重建耗时,不要输出用户学习数据。若窗口拖拽出现卡顿,再考虑对宽度状态做去重或只在布局档位变化时更新。

二十五、发布审核前的多设备检查

HarmonyOS 应用上架前应把布局适配视为稳定性门槛:

  • 所选设备类型与 AGC 材料一致;
  • 折叠、旋转、分屏和缩放时不截断;
  • 图片保持比例,不拉伸;
  • 文字有 maxLines、ellipsis 或足够高度;
  • 底部操作不进入系统手势区;
  • 弹窗和长页面最后一项可滚动到;
  • 状态栏和导航栏内容可读;
  • 宽屏不会出现无法利用的大块空白;
  • 手机与宽屏返回路径一致;
  • 修正 Index 后侧栏切换不丢失当前 Tab。

二十六、常见问题排查表

症状 真实可能原因 检查位置
平板仍显示底部导航 Index 条件覆盖 sm/md/lg 根布局 if/else
窗口已很宽仍单列 未同时满足 lg 与 pageWidth>=700 useGridLayout
分类页与其他页密度不同 独立使用 720 阈值 itemWidth
首次启动断点不对 没有初始 matches 判断 register
反复进入产生多次回调 register 缺少幂等保护 listener 生命周期
底部按钮被手势区挡住 未消费 avoid area bottomSafePadding
大窗卡片过宽 缺少 maxWidth 页面内容容器
结果页宽屏过空 只有弹性布局,无断点重排 ExamResultPage
加侧栏后三列拥挤 只看全局宽度 pageWidth 二次校验
PC 键盘无法操作 只完成窗口适配 焦点和输入测试

二十七、总结:用真实空间驱动结构变化

口算王的多设备基础已经能够从源码完整复核:EntryAbility 注册和释放 BreakpointSystem,MediaQuery 将窗口分为 sm、md、lg,AppStorage 向页面广播断点,页面再用 onAreaChange 确认局部可用宽度。首页、题库、挑战、统计的三列网格,以及题库详情的左右双栏,都已经具备真实实现。

当前最重要的修正点同样明确:Index 的条件覆盖全部合法断点,侧边导航分支不可达。修正后还应统一 md 策略、局部阈值和最大内容宽度,并补充 2in1 的鼠标键盘体验。

多设备适配的目标不是为每种设备复制一套页面,而是建立稳定契约:

  • 全局断点描述窗口档位;
  • 局部测宽确认真实空间;
  • 布局 Builder 负责结构重排;
  • 安全区保证关键操作可达;
  • 连续窗口测试验证阈值附近行为。

做到这些,同一份 ArkUI 代码才能在手机、平板和 PC/2in1 上保持信息清楚、操作可达和窗口变化可预测。

本文部分内容由 AI 辅助整理,所有现有行为、代码片段与问题边界均依据上述本地源码复核;Index 条件修正、md 专属布局、最大内容宽度、监听幂等与桌面输入增强部分为基于当前实现的工程建议。

二十四、修复不可达侧边导航时先明确产品契约

Index 已经写有侧边导航结构,但当前条件把 smmdlg 三种断点全部纳入底部导航分支,因此 else 无法执行。修复不能只把某个判断删掉,因为这会直接改变大屏用户的主导航形态。应先确认产品契约:sm 是否使用底部导航,md 在折叠展开或平板分屏时采用哪种导航,lg 是否始终显示侧栏,以及窗口从 lg 缩到 md 时如何保持当前页和返回栈。

一个较小的候选方案是仅让 smmd 进入底部导航,lg 进入侧边导航。但这只是建议,仍需检查侧栏会占用多少宽度、页面的 pageWidth 是否在扣除侧栏后重新测量,以及 700vp 的局部门槛会不会造成刚进入 lg 时仍保持单列。导航切换过程中还要验证当前 Tab、焦点、滚动位置和无障碍语义是否连续。

二十五、把窗口验证写成可重复矩阵

多设备验证不应只记录"手机正常、平板正常"。可以按宽度临界点建立矩阵,例如 599vp、600vp、601vp、839vp、840vp、841vp,再补充页面局部宽度 699vp、700vp 和 701vp。每个点检查导航形态、列数、文字截断、卡片触控区、滚动可达性和底部安全区。这样能够发现边界符号写错、全局断点与局部阈值相互打架等问题。

还应覆盖窗口连续拖拽,而不只是固定尺寸截图。连续缩放时要观察布局是否频繁抖动、监听回调是否重复注册、卡片是否短暂重叠,以及从三列退回单列后内容顺序是否仍正确。折叠屏展开、横竖屏切换和 PC/2in1 自由窗口本质上都是窗口变化,但系统栏、输入方式和安全区域不同,仍需分别验证。

本文没有执行上述设备和窗口测试,因此这些内容是可执行的验收设计,不是测试通过记录。当前能从源码确认的是断点体系、局部测宽和若干页面重排已经存在,同时侧边导航不可达、中等宽度密度不统一等问题仍待后续代码修复与运行验证。

AI 辅助声明

本文在人工复核口算王 ArkTS 源码、断点系统和页面布局后,使用 AI 辅助整理结构、润色表达并生成配图;未执行的构建、设备与窗口验证均未写成已通过。

相关推荐
ChinaDragon11 小时前
HarmonyOS:6.0 新增和增强特性
harmonyos
贾伟康12 小时前
【句匠|01】HarmonyOS ArkTS 英语纠错页实战:把原句、修改建议和解释层级展示清楚
harmonyos·arkts·textarea·学习应用·英语纠错
淡写成灰15 小时前
「Flutter 文件保存太难了?」一个插件打通 7 大平台,我把方案开源了 🎉
flutter·harmonyos
大雷神20 小时前
HarmonyOS ArkGraphics 2D 自定义字体实操:注册字体并验证中文回退
pytorch·华为·harmonyos
Nayana1 天前
《Web 到 HarmonyOS》-- 第一课:前端经验哪些能带走
vue.js·harmonyos
大雷神1 天前
HarmonyOS ArkGraphics 2D 复杂文本排版实操:用 ParagraphBuilder 做资讯阅读卡片
华为·harmonyos
大雷神1 天前
HarmonyOS ArkGraphics 2D NativeImage 实操:获取 NativeWindow 与 SurfaceId
华为·harmonyos
小玮看世界1 天前
当“教用户配置系统“成为产品的遮羞布:从鸿蒙拦截栈与小红书联系机制看科技公司的“驯化式创新“
科技·华为·harmonyos
梦想不只是梦与想1 天前
鸿蒙 应用类型:企业应用
华为·harmonyos·企业应用