【HarmonyOS 7新能力|019】平行视界入门实战:从能力边界到最小可运行链路

【HarmonyOS 7新能力|019】平行视界入门实战:从能力边界到最小可运行链路

平行视界不是简单地把手机页面横向复制两份,而是让同一个应用在大屏上同时呈现相互关联的主内容与从内容。典型场景是左侧列表、右侧详情:用户选择列表项后,详情区更新;窗口收窄时,两栏又应自然回到单栏导航。

真正的工程难点不在"画两个容器",而在状态连续性。选中项、返回行为、窗口变化、空详情和进程恢复必须保持一致。本文建立"读取窗口信息---选择单双栏模式---同步主从状态---恢复导航位置---验证返回链路"的最小实现。文中的类型与服务是应用侧示例,不冒充 HarmonyOS 7 官方接口;具体能力、适配范围和 API 请以当前 SDK 与华为开发者官方资料为准。

一、先定义平行视界的业务边界

适合双栏的页面通常存在稳定的主从关系,例如会话与消息、商品与详情、目录与正文。两个区域若彼此独立,只是碰巧并排,普通响应式布局可能更合适。

第一版应满足四个条件:大窗显示主从两栏;小窗保持正常单栏;选中项在模式切换后不丢失;系统返回始终符合用户预期。不要把"双栏出现"当成完成,因为业务状态错位比布局未展开更影响使用。

二、用窗口模式而不是设备名称做判断

折叠屏展开、平板分屏和 2in1 窗口缩放都会改变可用空间。布局策略应依赖当前窗口宽度与项目验证过的断点,而不是写死"平板一定双栏"。

ts 复制代码
type PaneMode = 'single' | 'dual'

interface WindowSnapshot {
  widthVp: number
  heightVp: number
  updatedAt: number
}

function resolvePaneMode(snapshot: WindowSnapshot, dualMinWidth: number): PaneMode {
  if (!Number.isFinite(snapshot.widthVp)) return 'single'
  return snapshot.widthVp >= dualMinWidth ? 'dual' : 'single'
}

示例中的断点必须通过目标设备、字体缩放和真实内容验证,不能从其他项目直接复制。

三、主从状态需要单一事实来源

列表页和详情页不能各自保存一份选中项,否则窗口切换后很容易出现左侧高亮 A、右侧显示 B。把选择状态放在编排层,由两个区域共同读取。

ts 复制代码
interface SplitViewState {
  mode: PaneMode
  selectedId: string | null
  detailStatus: 'idle' | 'loading' | 'ready' | 'error'
  revision: number
}

function selectItem(state: SplitViewState, id: string): SplitViewState {
  return {
    ...state,
    selectedId: id,
    detailStatus: 'loading',
    revision: state.revision + 1
  }
}

页面只提交"选择某项"的意图,不直接修改另一栏组件。这样单栏页面跳转和双栏局部更新可以复用同一状态模型。

四、模式切换是一条状态迁移链路

从单栏进入双栏时,如果已经有选中项,应直接恢复对应详情;如果没有选中项,可以保留空态,而不是擅自选择第一条。双栏退回单栏时,要根据当前焦点决定展示列表还是详情,并保留用户可理解的返回路径。

ts 复制代码
interface NavigationSnapshot {
  route: 'list' | 'detail'
  selectedId: string | null
}

function collapseToSingle(state: SplitViewState): NavigationSnapshot {
  return state.selectedId
    ? { route: 'detail', selectedId: state.selectedId }
    : { route: 'list', selectedId: null }
}

窗口变化可能连续触发,编排层应对等价模式去重。宽度仍处于双栏范围时,不要反复重建详情页。

五、详情加载要防止旧结果覆盖

用户快速选择多条记录时,较早请求可能最后返回。无论数据来自本地还是网络,都应以修订号确认结果仍属于当前选择。

ts 复制代码
class DetailCoordinator {
  private revision: number = 0

  async load(id: string, fetcher: (id: string) => Promise<string>): Promise<string | null> {
    const current = ++this.revision
    const result = await fetcher(id)
    return current === this.revision ? result : null
  }

  cancel(): void {
    this.revision += 1
  }
}

这段代码只展示结果去重思路。真实项目还需要在生命周期结束时取消任务、释放监听,并把错误映射为明确页面状态。

六、分层架构隔离平台变化

页面层负责列表、详情与设置的显示;状态编排层维护选中项、导航快照和窗口模式;布局策略层选择单栏或双栏;平台适配层读取真实窗口信息与生命周期事件。业务层不直接依赖具体窗口对象。

ts 复制代码
interface WindowModeAdapter {
  current(): Promise<WindowSnapshot>
  subscribe(listener: (snapshot: WindowSnapshot) => void): () => void
}

interface SplitViewPolicy {
  resolve(snapshot: WindowSnapshot): PaneMode
}

适配层返回取消订阅函数,页面退出时必须调用,避免重复监听导致模式判断多次执行。

七、返回行为要按用户焦点设计

双栏状态下,详情区出现不一定意味着新增了一层全屏路由。返回键若直接退出应用,用户会感觉内容被突然关闭;若永远只清空详情,又可能破坏已有导航习惯。

建议记录"进入详情的来源"和当前窗口模式:单栏详情返回列表;双栏详情优先保持主列表,并按照产品规则清除焦点或回到上一个选中项;根页面再次返回才交给系统。每个分支都要用真实按键和手势返回验证。

ts 复制代码
function handleBack(state: SplitViewState): 'clear-detail' | 'show-list' | 'system' {
  if (state.mode === 'dual' && state.selectedId) return 'clear-detail'
  if (state.mode === 'single' && state.selectedId) return 'show-list'
  return 'system'
}

这只是可测试的策略示例,最终规则应与项目的导航栈保持一致。

八、空态、错误态和加载态都要双栏化

右栏没有选中内容时,应显示轻量空态并说明操作方式;加载时保持左栏可用;详情失败时提供局部重试,而不是让整个大屏页面变成错误页。数据被删除或权限变化时,清理过期选中项并回到安全状态。

字体放大后要检查列表标题、详情工具栏和空态文案是否溢出。双栏宽度不足时,应优先切回单栏,而不是把两个区域压缩到无法操作。

九、模式恢复不能只保存宽度

进程恢复时,窗口尺寸可能已经改变。持久化业务选择可以,但不要把上次的 dual 模式当成永久配置。启动后重新读取当前窗口,再把可恢复的选中项映射到新模式。

ts 复制代码
interface RestorableSplitState {
  selectedId: string | null
  lastRoute: 'list' | 'detail'
}

function restore(
  saved: RestorableSplitState,
  snapshot: WindowSnapshot,
  dualMinWidth: number
): SplitViewState {
  return {
    mode: resolvePaneMode(snapshot, dualMinWidth),
    selectedId: saved.selectedId,
    detailStatus: saved.selectedId ? 'loading' : 'idle',
    revision: 0
  }
}

十、最小测试矩阵

  1. 手机竖屏从列表进入详情并正常返回。
  2. 大窗双栏选择不同记录,左右状态始终一致。
  3. 双栏缩小为单栏后仍显示当前详情,返回可到列表。
  4. 单栏放大为双栏后恢复选中项,不重复加载旧数据。
  5. 无选中项、详情删除、加载失败都有明确回退。
  6. 连续拖动窗口时不反复创建页面或闪烁。
  7. 字体放大、长标题、深浅色模式下内容仍可读。
  8. 页面退出后窗口监听与异步任务都被释放。

十一、常见误区

不要在每次尺寸变化时清空导航栈;不要把窗口宽度写进业务数据;不要在没有选择时强制加载第一条;不要让左右组件各自维护选中状态;也不要用截图看起来是双栏就代替真实返回、旋转、缩放和恢复测试。

十二、结语

平行视界的核心是"同一业务状态在不同窗口模式下连续呈现"。先统一选中项和导航快照,再把单双栏判断放入策略层,通过平台适配层读取窗口信息,最后验证返回、异常与恢复链路,才能让大屏空间真正提升效率,而不是简单拉宽页面。

参考资料:

相关推荐
技术任我行XTing1 小时前
【DFX系列】Flutter 鸿蒙应用外接纹理介绍及问题定位
flutter·harmonyos
三翼鸟数字化技术团队1 小时前
WiFi-DensePose × OpenHarmony 智慧家居融合
harmonyos
HarmonyOS_SDK2 小时前
AI 赋能 Push Kit 场景化消息开发,高效完成鸿蒙应用推送能力接入
harmonyos
大雷神3 小时前
【共创稿事节】HarmonyOS ArkGraphics 3D实操——做一个可暂停、可拖动的音箱场景动画
harmonyos
anthonyzhu3 小时前
MacOS27 x86限制引发的pod的问题处理
华为·harmonyos
贾伟康3 小时前
【HarmonyOS 7新能力|030】沉浸光感工程封装:把接入逻辑放进可维护的分层结构
harmonyos·arkts·arkui·harmonyos 7·交互动效
万物智能信息科技3 小时前
GPIO控制状态灯—【万物智能之开源鸿蒙OpenHarmony系统实战开发系列教程】
人工智能·华为·开源·harmonyos·鸿蒙
威哥爱编程3 小时前
HarmonyOS 7 平行视界 EasyGo 实战:配置文件接入应用内分屏,1:2/2:1 随意切
harmonyos·arkts
威哥爱编程3 小时前
HarmonyOS 7 空间重建实战:3DGS 端侧重建从建模到商品展示
harmonyos·arkts