目前对于鸿蒙版本的适配,大多数都是由之前移动端开发来担任。针对不同设备的适配一直是移动端开发的头等大事,鸿蒙开发在设计之初就已经考虑到这个问题,并且提供了相关的解决方案。当前鸿蒙系统的产品形态主要有手机、折叠屏、平板和2in1四种。本文主要从页面开发来介绍如何使用自适应布局和响应式布局实现不同的UI效果,实现多端的适配方案。
提到"一多"的布局能力,就不得不说自适应布局 和响应式布局了。
自适应布局通常包括拉伸能力、均分能力、占比能力、缩放能力、延伸能力、隐藏能力、折行能力。这些能力在传统的移动端开发中也经常用到。比如拉伸能力就用到Flex布局的flexGrow和flexShrink属性,占比能力用到layoutWeight属性,布局约束的aspectRatio属性可以锁定控件的长宽比,Flex组件的wrap属性设置为FlexWrap.Wrap提供了折行能力。这些组件提供的能力甚至和名字都和其他前端技术很类似,有着其他前端经验的同学可以平滑的过渡过来,类似微信小程序,ReactNative等等。
响应式布局对于移动端开发同学来说可能是一个新鲜事物,它为"一多"的布局能力提供了一个新的思路。响应式布局是指页面内的元素可以根据特定的特征(如窗口宽度、屏幕方向等)自动变化以适应外部容器变化的布局能力。这里特别是指窗口宽度,不同设备的主要区分(手机、折叠屏、平板等)是由窗口宽度体现的。鸿蒙系统提供了三种响应式布局能力,它们是断点,媒体查询和栅格布局。
断点是将窗口宽度划分为不同的范围(即断点),监听窗口尺寸变化,当断点改变时同步调整页面布局。你可以理解为获取当前断点的值来判断当前处于何种设备。媒体查询支持监听窗口宽度、横竖屏、深浅色、设备类型等多种媒体特征,当媒体特征发生改变时同步调整页面布局。栅格布局组件将其所在的区域划分为有规律的多列,栅格组件可以针对不同断点值时让不同的组件参数生效,以实现不同的布局效果。
系统提供了多种方法,判断应用当前处于何种断点,进而可以调整应用的布局。常见的监听断点变化的方法如下所示:
- 获取窗口对象并监听窗口尺寸变化
- 通过媒体查询监听应用窗口尺寸变化
- 借助栅格组件能力监听不同断点的变化
一般情况下,断点取值如下:
我们这里举个栅格组件的例子:
js
@Entry
@Component
struct GridRowSample1 {
@State private currentBreakpoint: string = 'unknown'
build() {
// 修改断点的取值范围同时启用更多断点,注意,修改的断点值后面必须加上vp单位。
GridRow({breakpoints: {value: ['600vp', '700vp', '800vp', '900vp', '1000vp'],
reference: BreakpointsReference.WindowSize}}) {
GridCol({span:{xs: 12, sm: 12, md: 12, lg:12, xl: 12, xxl:12}}) {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Text(this.currentBreakpoint).fontSize(50).fontWeight(FontWeight.Medium)
}
}
}.onBreakpointChange((currentBreakpoint: string) => {
this.currentBreakpoint = currentBreakpoint
})
}
}
修改默认的断点范围,同时启用xl和xxl断点。图片右下角显示了当前设备屏幕的尺寸(即应用窗口尺寸),可以看到随着窗口尺寸发生变化,栅格的断点也相应发生了改变。
其他组件可以通过判断this.currentBreakpoint的值来处理不同断点属性适配,比如Tab栏的适配:
js
// features/home/src/main/ets/view/Home.ets
// 底部/侧边页签区域
Tabs({
// lg断点时,页签栏在侧边;sm、md断点时,页签栏在底部
barPosition: this.currentBreakpoint === 'lg' ? BarPosition.Start : BarPosition.End
}) {
...
}
我们也可以使用栅格组件的默认断点配置,进行布局:
js
@Entry
@Component
struct GridRowSample {
private bgColors: ResourceColor[] = [
$r('sys.color.ohos_id_color1'),
$r('sys.color.ohos_id_color2'),
$r('sys.color.ohos_id_color3'),
$r('sys.color.ohos_id_color4'),
$r('sys.color.ohos_id_color5'),
$r('sys.color.ohos_id_color6')
]
build() {
// 配置不同断点下columns和gutter的取值
GridRow({columns: {sm: 4, md: 8, lg: 12},
gutter: {x: {sm: 8, md: 16, lg: 24}, y: {sm: 8, md: 16, lg: 24}}}) {
ForEach(this.bgColors, (bgColor:ResourceColor)=>{
GridCol({span: {sm: 2, md: 2, lg: 2}}) {
Row().backgroundColor(bgColor).height(30).width('100%')
}
})
}
}
}
栅格组件columns默认为12列,gutter(间距)默认为0。span是指在栅格中占据的列数。span为0,意味着该元素既不参与布局计算,也不会被渲染。当我们处于sm
断点时,columns是4,span是2,这意味着当前格栅组件有4列,其中每个元素占2列。
栅格组件还支持offset属性,意思是相对于前一个栅格子组件偏移的列数,举个例子。
js
class Obj {
public index: number = 1;
public color: Resource = $r('sys.color.ohos_id_color_palette_aux1')
}
@Entry
@Component
struct GridRowSample6 {
private elements: Obj[] = [
{index: 1, color: $r('sys.color.ohos_id_color1')},
{index: 2, color: $r('sys.color.ohos_id_color2')},
{index: 3, color: $r('sys.color.ohos_id_color3')},
{index: 4, color: $r('sys.color.ohos_id_color4')},
{index: 5, color: $r('sys.color.ohos_id_color5')},
{index: 6, color: $r('sys.color.ohos_id_color6')},
]
build() {
GridRow() {
ForEach(this.elements, (item:Obj)=>{
GridCol({span: {sm: 6, md: 4, lg: 3}, offset: {sm: 0, md: 2, lg: 1} }) {
Row() {
Text('' + item.index).fontSize(24)
}
.justifyContent(FlexAlign.Center)
.backgroundColor(item.color).height(30).width('100%')
}
})
}
}
}
这里没有设置columns,默认为12列。offset可以理解为空几列的意思。以md
断点为例,Span是4,offset是2,即一共12列,每个元素占4列,每个元素前空2列。