HarmonyOS 一多布局实战:先懂分层再碰断点,一份代码喂饱五个端

本文基于 HarmonyOS 6.1.0 Release 官方 Codelab《基于一多能力实现天气应用》整理编写。文中代码为本人自写的完整示例(非官方示例搬运),API 名称、枚举取值与版本号来自华为官方文档;未在真机逐行验证的部分,请以真机实测为准。

引子:一份代码真能喂饱五个端吗

上周V哥想做一个能跑在手机、折叠屏、平板、电脑、手表上的天气应用。第一反应是:写五套界面?那不成五倍工作量了。

后来翻到一个官方 Codelab,叫《基于一多能力实现天气应用》。跟着它做完,V哥搞清楚了一件事:一多不是"写一份 UI 跑五个端",是"一套工程 + 分层架构 + 断点驱动"。 业务逻辑只写一遍,界面差异交给分层和断点去消化。

这篇文章不重复官方章节顺序,V哥把实操路径拆成七步:先拆"一多"这句话,再讲三层怎么分,然后死磕断点,最后给一个最小示例和一份真机校准清单。


一、先把"一多"这句话拆开

官方对一多的概括是"一套代码工程,一次开发上架,多端按需部署"(Codelab《基于一多能力实现天气应用》)。

但这句话容易被读歪成"写一份界面到处跑"。V哥做完天气应用才明白,它实际是两件事拧在一起:

  • 业务统一:看天气、管城市这套逻辑,五端必须一致,不能各写一套;
  • 界面差异:手机单列、平板多列、电脑带侧边栏、手表独立翻页------这些差异要能被"收口",不能污染业务逻辑。

解决这对矛盾的钥匙,就是下面要说的分层架构。把"不变的"和"变的"分开,一多就成了。

反过来说更扎心:不分层会怎样?手机写一套天气页、平板再抄一套、电脑又抄一套、手表再写一套------四套界面各自演化,三个月后"看天气"的逻辑就悄悄分叉了:手机显示晴,平板显示多云。这种漂移,靠人肉 review 根本拦不住。分层就是给"业务只此一份"上一道硬保险。


二、三层架构怎么落:V哥的分层三问

官方 Codelab 把工程按 products / features / common 三层组织。但新手最容易犯的错是:代码随手一放,结果手机要改、平板也得跟着改。

V哥给自己定了三个问题,拿到一段代码先问:

代码类型 归属层 官方职责 V哥的口诀(放哪一层)
设备专属页面、入口 Ability(EntryAbility / PcAbility / WearableAbility) products 存放不同设备的独立界面与交互逻辑 随变进 P:会因设备而改,就进产品层
可复用的核心业务(天气视图、天气逻辑) features 多设备复用,保证核心逻辑统一 可复用进 F:每端都要用,就进特性层
通用常量、数据模型、公共组件、工具类 common 与业务无关,给上层提供基础支持 通用进 C:谁都可能用,就进公共层

三个问题连起来就是V哥的判定口诀:

这代码会不会因为设备而改?会 → P。每端都要用吗?是 → F。谁都可能用吗?是 → C。

天气应用里,所有"看天气"的视图(WeatherForecastViewWeekWeatherView 等)都进 features 层,三个产品入口统一调用,保证每端的天气业务一模一样;常量、模型、日志、窗口工具进 common 层。差异全锁在 products 层,业务层干干净净。


三、断点是唯一抓手:先定断点,再谈布局

分层解决了"代码放哪",断点解决"界面怎么变"。

官方把窗口宽度按断点划分区间(响应式布局最佳实践),推荐值如下:

断点 窗口宽度(vp) 典型设备形态
WIDTH_XS < 320 智能穿戴
WIDTH_SM [320, 600) 手机竖屏
WIDTH_MD [600, 840) 折叠屏展开 / 小平板
WIDTH_LG [840, 1440) 平板 / 电脑
WIDTH_XL ≥ 1440 大屏

这个枚举在 @kit.ArkUI 里叫 WidthBreakpoint,通过 getUIContext().getWindowWidthBreakpoint() 拿到当前值。

V哥踩过的最大的坑,是对着机型写布局 。折叠屏合上是手机、展开是平板,同一台设备横竖一转就换了断点。官方也明确:断点面向窗口而非设备类型(响应式布局)。

所以V哥的第一句铁律:

看断点,别看机型。机型只是障眼法,宽度才是真相。

断点值不是凭空来的。实践中V哥在页面里用一个轻量工具类监听窗口宽度,再写回 @State

typescript 复制代码
// common/WidthBreakpointType.ets
import { WidthBreakpoint } from '@kit.ArkUI';

class BpUtil {
  // 取当前窗口断点枚举
  static current(): WidthBreakpoint {
    return this.getUIContext().getWindowWidthBreakpoint();
  }
}

aboutToAppear 里读一次初值,窗口尺寸变化(折叠、分屏、拖窗口)时在回调里更新。关键提醒:折叠屏展开/合上只触发回调、不重建组件,所以布局切换逻辑必须放在回调里,不能只在初始化时算一次。先看窗口落在哪个断点,再决定单列还是多列、侧边栏悬浮还是嵌入。这一步没想清楚,后面全是补丁。


四、最小示例:一个 build 吃完五端

把断点接进页面。下面是V哥自写的天气主页最小示例,演示"同一份代码随断点变版式":

typescript 复制代码
// pages/WeatherHome.ets
import { WidthBreakpoint, SideBarContainerType } from '@kit.ArkUI';

@Component
struct WeatherHome {
  // 当前宽度断点,由上层监听窗口宽度写入(WidthBreakpoint 枚举)
  @State bp: WidthBreakpoint = WidthBreakpoint.WIDTH_SM;
  // 已添加的城市列表
  private cities: string[] = ['北京', '上海', '深圳'];

  build() {
    // 窄屏用悬浮侧边栏,宽屏用嵌入侧边栏
    SideBarContainer(
      this.bp === WidthBreakpoint.WIDTH_SM
        ? SideBarContainerType.Overlay   // 悬浮:覆盖在内容之上,需收起
        : SideBarContainerType.Embed     // 嵌入:常驻在内容区旁
    ) {
      // 侧边栏:管理城市
      CityList({ onPick: (c: string) => this.switchCity(c) })

      // 内容区:多城市用 Swiper 横向滑动切换
      Swiper() {
        ForEach(this.cities, (city: string) => {
          CityWeatherPage({ city: city })
        })
      }
      .loop(true)        // 首尾循环滑动
      .indicator(false)  // 窄屏隐藏指示点,宽屏按需开启
    }
  }

  // 切换当前展示城市
  private switchCity(city: string): void {
    // 实际项目中在此驱动内容区 Swiper 的 index
  }
}

内容区里的天气信息,再用 GridRow / GridCol 控制列数:窄屏 span 占满,宽屏占一半或三分之一。这样"一屏一列、大屏多列"就自动来了。


五、SideBarContainer:窄屏悬浮、宽屏嵌入

上面示例里最关键的一行就是 SideBarContainer 的构造参数。SideBarContainerType 有两个值,行为完全不同:

  • Embed嵌入------侧边栏常驻在内容区旁边,宽屏下不占额外层;
  • Overlay悬浮------侧边栏盖在内容之上,窄屏下默认收起,点按钮才展开。

Codelab 的做法是:窗口处于 WIDTH_SM 时给 Overlay,其余断点给 EmbedCodelab 完整示例)。这个切法很顺:手机屏幕窄,侧边栏常驻会挤掉内容,所以让它悬浮收起;平板电脑屏幕宽,侧边栏常驻反而更顺手。

V哥加的一条经验:别让 SideBarContainer 单独背锅。 它只管"侧边栏怎么挂",列数还要靠 GridRow/GridCol 配合。断点一变,两处要一起变,少一处版式就错位。

如果页面还要"列表 + 详情"的双栏导航,可以换成 Navigation:它的 mode 在窄屏走单栏、宽屏走双栏(导航栏与内容区分栏显示)。SideBarContainer + Navigation 组合还能做三分栏------侧边栏、导航栏、内容区三层并排。这也是官方组件布局场景里点名的响应式组件之一(组件布局场景最佳实践)。选 SideBarContainer 还是 Navigation,看你要的是"抽屉"还是"导航分栏"。


六、三个 hap 入口:default / pc / wearable

界面差异收口到 products 层后,交付形态是多个 hap 包。天气应用建了三个产品入口模块:

入口模块 hap 名称 安装目标
products/default multiweatherdefaultsample 手机、平板
products/pc multiweatherpcsample 电脑
products/wearable multiweatherwearablesample 穿戴设备

features 层的天气模块和 common 层模块以 HAR 形式被三个入口统一调用------这就是"业务一份、界面多端"在工程上的落点。

这里有个容易忽略的点:手表屏幕和手机差太大,不能把手机 UI 缩小塞进去 。穿戴入口要做独立的页面序列(授权页 → 首页 → 上下滑动看预报),放进 wearable 模块单独写。源码见 GitCode 仓库


七、真机断点校准清单

多设备真机差异大,模拟器跑通不等于真机 OK。V哥把上线前要核的对列成清单,建议贴进 PR:

  • WidthBreakpoint 取值区间记住了吗?SM 是 [320,600),不是"手机专属"
  • 断点切换逻辑写在回调里了吗?折叠屏展开/合上只触发回调,不重建组件
  • 折叠屏合上(SM)侧边栏是 Overlay 吗?展开(MD+)是 Embed 吗?
  • 内容区列数随断点变了吗?GridRow columns / GridCol span 配齐了吗?
  • 手表入口独立写了吗?没把手机 UI 直接缩小吧?
  • 三个 hap 包分别装到对应设备验证了吗?还是只跑了一个入口?
  • 横屏/分屏/自由窗口测了吗?同一设备不同窗口形态可能落不同断点
  • Swiper 的 indicator 在不同断点表现一致吗?窄屏关、宽屏开的对吗?
  • getWindowWidthBreakpoint() 打日志,确认真机断点和你预期一致吗?

注:折叠屏、电脑、穿戴的真机表现以真机实测为准,本文未在真机逐行验证。


参考与出处

本文涉及的事实性信息(API 名称、枚举取值、版本号、官方约束)来自以下官方文档,文中的结构、代码示例、决策流程与自检清单为本人整理编写:


最后一句 :一多真正难的不是某个组件------组件就那几行。难的是先想清楚分层 (代码放哪)和断点(界面怎么变)。这两步想明白,一份工程就能老老实实喂饱五个端。

相关推荐
传奇开心果编程1 小时前
【ArkUI 练中学】第1课:从零开始
学习·华为·前端框架
万物智能信息科技1 小时前
硬件看门狗MAX6369设置—【万物智能之开源鸿蒙OpenHarmony系统实战开发系列教程】
人工智能·华为·开源·harmonyos·鸿蒙
贾伟康15 小时前
【HarmonyOS 7新能力|026】Agent Framework Kit工程封装:把接入逻辑放进可维护的分层结构
agent·harmonyos·arkts·a2a·harmonyos 7
贾伟康16 小时前
【HarmonyOS 7新能力|025】Core File Kit mmap入门实战:从能力边界到最小可运行链路
harmonyos·arkts·文件读写·mmap·harmonyos 7
贾伟康19 小时前
【HarmonyOS 7新能力|028】Core Vision Kit工程封装:把接入逻辑放进可维护的分层结构
计算机视觉·harmonyos·arkts·端侧ai·harmonyos 7
马剑威(威哥爱编程)21 小时前
【共创稿事节】HarmonyOS 7 互动卡片实战:摇一摇触发静态转动态,前景元素出框
华为·harmonyos
贾伟康21 小时前
【HarmonyOS 7新能力|029】3DGS工程封装:把接入逻辑放进可维护的分层结构
harmonyos·arkts·三维重建·3dgs·harmonyos 7
马剑威(威哥爱编程)21 小时前
【共创稿事节】HarmonyOS 7 闪控窗实战:标准悬浮窗、侧边栏暂存与闪控球一键切换
华为·harmonyos
贾伟康1 天前
【HarmonyOS 7新能力|019】平行视界入门实战:从能力边界到最小可运行链路
harmonyos·arkts·arkui·harmonyos 7·平行视界