【harmonyOS】布局和样式适配不同设备

39 阅读4分钟

设备适配

记录下开发中使用的适配,保证一套代码在手机、折叠屏、平板上有不同的样式、布局。

断点

在应用启动时获取宽度,根据不同大小进行判断。区分不同设备使用的组件属性

实现方式


//EntryAbility.ets
export default class EntryAbility extends UIAbility {
   
    onWindowStageCreate(windowStage: window.WindowStage) {
    
        windowStage.getMainWindow((err, data) => {
            if (err.code) {
                return
            }
            // 启动应用和窗口大小变化时计算断点
            this.updateBreakpoint(data.getWindowProperties().windowRect.width)
            windowClass.on('windowSizeChange', (windowSize: window.Size) => {
              this.updateBreakpoint(windowSize.width)
            })

        })

    }
    
    // 根据宽度划分断点
    private updateBreakpoint(windowWidth: number): void {
      let windowWidthVp = px2vp(windowWidth)
      let curBp: string = ''
      const breakPoints = [320, 600, 840, 1440]
      if (windowWidthVp < breakPoints[0]) {
        curBp = 'xs'
      } else if (windowWidthVp >= breakPoints[0] && windowWidthVp < breakPoints[1]) {
        curBp = 'sm'
      } else if (windowWidthVp >= breakPoints[1] && windowWidthVp < breakPoints[2]) {
        curBp = 'md'
      } else if (windowWidthVp >= breakPoints[2] && windowWidthVp < breakPoints[3]) {
        curBp = 'lg'
      } else {
        curBp = 'xl'
      }
      AppStorage.setOrCreate('breakPoint', curBp)
    }

}

断点对应设备:

  • xs:穿戴式设备
  • sm:手机、折叠屏手机折叠态竖屏
  • md:手机、折叠屏手机横屏,折叠屏手机展开横竖屏
  • lg:pad横竖屏
  • xl:2in1设备

断点使用

1、tabs

平板和其它设备区分tabbar样式,平板中tabs在右侧

@StorageProp('breakPoint') currentPoint: string = 'sm'

Tabs({
  index: /*tabbar索引*/,
  barPosition: BarPosition.End
})
.vertical(this.breakPoint === 'lg')

2、swiper

竖屏一个item横向占满,其它设备展示第一item的右侧+完整的第二个item+第三个item的左侧

@StorageProp('breakPoint') currentPoint: string = 'sm'

// bannerElementData:swiper数据内容项
Swiper() {
    
}
.width(this.currentPoint === 'sm' ? '100%' : (this.bannerElementData.length > 2 ? '150%' : '100%'))
.displayCount(this.currentPoint == 'sm' ? 1 :
  (this.bannerElementData.length > 2 ? 3 : 2)) // 显示几页

3、分栏

平板设备在最左侧一部分屏展示负一屏内容

SideBarContainer(SideBarContainerType.Embed) {
    MinusScreenComponent()
    
    Navigation(this.navPathStack) {
    }
    .mode(NavigationMode.Stack)
    .hideTitleBar(true)
    .navDestination(/*配置的navDestination组件*/)

}.showSideBar(this.breakPoint === 'lg')
.showControlButton(false)

1、SideBarContainer组件只能有两个子组件,第一个子组件是侧栏第二个是内容区。
2、分栏效果也可以用Navigation实现,设置mode的值为NavigationMode.Split。

栅格布局

ArkUI提供的响应式组件,根据配置的断点自适应布局。和前端的响应式布局设计相同

栅格组件GridRow和其子组件GridCol

GridRow({breakpoints: {value: ['320vp', '600vp','840vp','1440vp'],reference: BreakpointsReference.WindowSize}}){
  GridCol({span: {xs: 12, sm: 6, md: 4, lg:12, xl: 12}}){
    Row().height(60).backgroundColor(Color.Red).width('100%')
  }
  GridCol({span: {xs: 12, sm: 6, md: 4, lg:12, xl: 12}}){
    Row().height(60).backgroundColor(Color.Black).width('100%')
  }
  GridCol({span: {xs: 12, sm: 6, md: 4, lg:12, xl: 12}}){
    Row().height(60).backgroundColor(Color.Blue).width('100%')
  }
}

GridRow:

  • breakpoints:
    • value:配置断点。例如['600vp','800vp']表示xs(0,600), sm[600,800), md[800,∞)
    • reference:断点生效根据窗口尺寸或当前GirdRow的父组件尺寸
  • columns:设置屏幕分多少份,默认任何大小的屏都是分12份,大屏每份更宽小屏每份更窄,也可单独为不同尺寸划分屏幕。栅格布局span的分配占用份数超出总份数(默认12)会自动换行。

GridCol:

根据GridRow划分的尺寸设置其子组件的占比。例如设备尺寸在(840,1440)区间内,对应的span设置为6,GridCol子组件宽度设置为100%也只能占6份,屏幕的一半。

value设置的断点和span对应的值并不是固定的,例如['840vp','1440vp']对应span是xs(0,840), sm[840,1440), md[1440,∞)

1、应用首页分区展示部分,手机和折叠屏手机折叠态每行展示一项,横向或双折叠展开每行展示两项,平板每行展示三项


@Builder
itemContent(param: string) {
  Row() {
    // 展示项内容...
  }.width('100%')
}

build() {
  GridRow({ breakpoints: { value: ['320vp', '600vp', '840vp'], reference: BreakpointsReference.WindowSize } }) {
    ForEach(this.dataList, (item: string) => {
      GridCol({
        span: {
          xs: 12,
          sm: 12,
          md: 6,
          lg: 4
        }
      }) {
        this.itemContent(item)
      }
    }, (item: string, index: number) => `${item}_${index}`)
  }
}

2、弹窗宽度适应设备,在平板上不能和其它设备相差太大,通过控制屏幕划分份数和偏移份数保证弹窗大小和位置居中。BreakPoints的value默认是["320vp", "600vp", "840vp"]

build() {
    // 默认的value有3项对应xs、sm、md、lg,这里没写的xs份数对应1
    GridRow({columns: {sm: 4, md: 8, lg: 12}}) {
        GridCol({span: 4, offset: {sm: 0, md: 2, lg: 4}}) {
            // 弹窗内容...
        }
    }
}