鸿蒙 一多适配/屏幕适配/设备适配/分栏
此文章内容兼容API12,使用harmony next应用开发
鸿蒙其中一个显著的特点是“一次开发,多端部署”,又名“一多”。同一套代码,修改布局适配,可很好的兼容不同的尺寸和设备。
我们的项目主要采用分栏的思想,不用过多设计屏幕适配方案,A+B+C区域,分别展示侧边栏+首页tab+二级/三级/多级页面。目前试过在手机、折叠屏、pad、PC端表现良好。
设计层面可参考官网,本文仅从技术层面表述。官网地址:一次开发,多端部署-核心技术理念 - 华为HarmonyOS开发者
概念定义
1,栅格系统
栅格系统,即以设备的水平宽度为依据,形成了一套断点规则,将设备宽度分为 xs sm md lg 四大类。[更细一步的划分包括 xs sm md lg xl xxl 六类]
宽度类型 | 取值范围(vp) | 设备描述 |
---|---|---|
xs | [0,320) | 最小宽度类型设备 |
sm | [320,520) | 小宽度类型设备 |
md | [520,840) | 中等宽度类型设备 |
lg | [840,+∞) | 大宽度类型设备 |
2,2in1 设备
2in1设备一般是指二合一的设备,和手机、平板是同类型的,在开发中2in1设备应当是功能集成在一起的电脑设备。
例如有的设备结合了平板电脑和笔记本电脑两种使用形态,这种设备通常具有可拆卸的键盘和触摸屏。也就是继续用支持触屏逻辑,也需要支持键鼠
3,自适应布局、响应式布局
(1)自适应布局:根据容器的大小,自动调整内部元素的布局。 如使用Row、Column、Flex组件实现 拉伸、均分、占比能立等,实现页面的自适应效果
(2)但当窗口变换幅度过大时,如从 MD 变成 LG,仅依靠自适应布局可能出现异常、稀疏、留白等问题。此时需要使用响应式布局,动态调整页面结构
4,一多分栏响应式变化布局定义
- A+B+C:即SideBarContainer组件组合Navigation组件
- A+C:SideBarContainer组件
- B+C:Navigation组件
操作步骤
1,获取设备类型
(1)在EntryAbility的onWindowStageCreate中获取设备类型
this.updateBreakpoint(windowClass.getWindowProperties().windowRect.width,windowClass.getWindowProperties().windowRect.height)
updateBreakpoint(windowWidth:number,height:number){
let windowWidthVp = px2vp(windowWidth)
if (windowWidthVp < 320) {
newBp = BREAKPOINT_XS
} else if (windowWidthVp < 600) {
newBp = BREAKPOINT_SM
} else if (windowWidthVp < 840) {
newBp = BREAKPOINT_MD
} else {
newBp = BREAKPOINT_LG
}
}
(2)监听屏幕设备宽度变化
try {
windowClass.on('windowSizeChange', (windowSize) => {
this.updateBreakpoint(windowSize.width, windowSize.height)
EmitterUtil.sendEvent(EmitterIdConstants.DZH_WINDOW_SIZE_CHANGE)
console.info('Succeeded in enabling the listener for window size changes. Data: ' + JSON.stringify(windowSize));
});
} catch (exception) {
console.error(`Failed to enable the listener for window size changes. Cause code: ${exception.code}, message: ${exception.message}`);
}
(3)存储设备状态。我们使用的是 AppStorage 内存存储,这样全局可用,很方便(可选)
//定义
AppStorage.setOrCreate('CurBP', newBp)
//使用
@Component
...
@StorageProp('CurBP') curBP: string = BREAKPOINT_SM;
2,首页-导航栏
分成两种情况:
(1)使用系统Tabs组件,作为导航栏
barPosition属性设置页签的位置(顶部、底部;vertical属性设置页签的排列方向(横向、纵向);barWidth barHeight 定义宽高
Tabs({
barPosition: this.curBP === BREAKPOINT_LG ? BarPosition.Start :BarPosition.End,
})
.barWidth(this.curBP === BREAKPOINT_LG ? 96 : '100%')
.barHeight(this.curBP === BREAKPOINT_LG ? '60%' : this.isOlderMode ? 64 : 56)
// 设置TabBar放置在水平或垂直方向
.vertical(this.curBP === BREAKPOINT_LG)
(2)自定义导航栏
使用 SideBarContainer 组件,强行分栏
提供侧边栏可以显示和隐藏的侧边栏容器,通过子组件定义侧边栏和内容区,第一个子组件表示侧边栏,第二个子组件表示内容区。
if (this.curBP === BREAKPOINT_LG) {
//Embed侧边栏嵌入到组件内,和内容区并列显示
SideBarContainer(SideBarContainerType.Embed) {
//侧边栏
//内容区
}
.showControlButton(false)
.sideBarWidth(96)
.autoHide(false)
} else {
//非分栏,正常展示布局
}
3,首页分栏后,右侧内容区填充
如tab指向资讯,此时 A+B+C 布局分别展示 侧边栏+资讯首页+第一条资讯详情页
@StorageProp('CurBP') @Watch('onBreakpointChanged') curBP: string = BREAKPOINT_SM;
if (当前是大屏 && 当前资讯NavPathStack为空){
// pushPathByName ('资讯详情页')
}
4,分栏模式下的跳转
内容C区展示二级页面。跳转三级页面存在两种情况:1)新页面打开三级页面;2)仍然在C区打开三级页面
//新页面打开三级页面
pushPathByName
//内容C区分屏打开三级页面
replacePath
5,分栏模式下的返回
(1)A+B+C 区的返回:侧边栏 + 首页 + C内容区
- C内容区打开二级页面去掉title 的返回按钮
- C内容区打开三级页面,正常pop
- 返回事件拦截处理,如果 大屏&&分栏,则 “onBackPress” “return false” 将返回事件传递给根布局NavDestination 的 onBackPressed 事件处理
6,其他布局要点
Swiper、Grid 等这些布局,如果需要配置响应式布局来动态适应屏幕,可以参考官方文档。典型布局场景-布局能力-页面开发的一多能力介绍-一次开发,多端部署 - 华为HarmonyOS开发者