鸿蒙一次开发,多端部署(七)响应式布局

自适应布局可以保证窗口尺寸在一定范围内变化时,页面的显示是正常的。但是将窗口尺寸变化较大时(如窗口宽度从400vp变化为1000vp),仅仅依靠自适应布局可能出现图片异常放大或页面内容稀疏、留白过多等问题,此时就需要借助响应式布局能力调整页面结构。

响应式布局是指页面内的元素可以根据特定的特征(如窗口宽度、屏幕方向等)自动变化以适应外部容器变化的布局能力。响应式布局中最常使用的特征是窗口宽度,可以将窗口宽度划分为不同的范围(下文中称为断点)。当窗口宽度从一个断点变化到另一个断点时,改变页面布局(如将页面内容从单列排布调整为双列排布甚至三列排布等)以获得更好的显示效果。

当前系统提供了如下三种响应式布局能力,后文中我们将依次展开介绍。

断点

断点以应用窗口宽度为切入点,将应用窗口在宽度维度上分成了几个不同的区间即不同的断点,在不同的区间下,开发者可根据需要实现不同的页面布局效果。具体的断点如下所示。

说明:

  • 以设备屏幕宽度作为参照物,也可以实现类似的效果。考虑到应用可能以非全屏窗口的形式显示,以应用窗口宽度为参照物更为通用。
  • 开发者可以根据实际使用场景决定适配哪些断点。如xs断点对应的一般是智能穿戴类设备,如果确定某页面不会在智能穿戴设备上显示,则可以不适配xs断点。
  • 可以根据实际需要在lg断点后面新增xl、xxl等断点,但注意新增断点会同时增加UX设计师及应用开发者的工作量,除非必要否则不建议盲目新增断点。

系统提供了多种方法,判断应用当前处于何种断点,进而可以调整应用的布局。常见的监听断点变化的方法如下所示:

  • 获取窗口对象并监听窗口尺寸变化
  • 通过媒体查询监听应用窗口尺寸变化
  • 借助栅格组件能力监听不同断点的变化

本小节中,先介绍如何通过窗口对象监听断点变化,后续的媒体查询及栅格章节中,将进一步展开介绍另外两种方法。

通过窗口对象监听断点变化的核心是获取窗口对象及注册窗口尺寸变化的回调函数。

  1. 在UIAbility的 onWindowStageCreate 生命周期回调中,通过 窗口 对象获取启动时的应用窗口宽度并注册回调函数监听窗口尺寸变化。将窗口尺寸的长度单位 由px换算为vp 后,即可基于前文中介绍的规则得到当前断点值,此时可以使用 状态变量 记录当前的断点值方便后续使用。
// MainAbility.ts
import window from '@ohos.window'
import display from '@ohos.display'
import UIAbility from '@ohos.app.ability.UIAbility'

export default class MainAbility extends UIAbility {
  private windowObj?: window.Window
  private curBp: string = ''
  //...
  // 根据当前窗口尺寸更新断点
  private updateBreakpoint(windowWidth: number) :void{
    // 将长度的单位由px换算为vp
    let windowWidthVp = windowWidth / display.getDefaultDisplaySync().densityPixels
    let newBp: string = ''
    if (windowWidthVp < 320) {
      newBp = 'xs'
    } else if (windowWidthVp < 600) {
      newBp = 'sm'
    } else if (windowWidthVp < 840) {
      newBp = 'md'
    } else {
      newBp = 'lg'
    }
    if (this.curBp !== newBp) {
      this.curBp = newBp
      // 使用状态变量记录当前断点值
      AppStorage.setOrCreate('currentBreakpoint', this.curBp)
    }
  }

  onWindowStageCreate(windowStage: window.WindowStage) :void{
    windowStage.getMainWindow().then((windowObj) => {
      this.windowObj = windowObj
      // 获取应用启动时的窗口尺寸
      this.updateBreakpoint(windowObj.getWindowProperties().windowRect.width)
      // 注册回调函数,监听窗口尺寸变化
      windowObj.on('windowSizeChange', (windowSize)=>{
        this.updateBreakpoint(windowSize.width)
      })
    });
   // ...
  }
    
  //...
}
  1. 在页面中,获取及使用当前的断点。
@Entry
@Component
struct Index {
  @StorageProp('currentBreakpoint') curBp: string = 'sm'

  build() {
    Flex({justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center}) {
      Text(this.curBp).fontSize(50).fontWeight(FontWeight.Medium)
    }
    .width('100%')
    .height('100%')
  }
}
  1. 运行及验证效果。

媒体查询

在实际应用开发过程中,开发者常常需要针对不同类型设备或同一类型设备的不同状态来修改应用的样式。媒体查询提供了丰富的媒体特征监听能力,可以监听应用显示区域变化、横竖屏、深浅色、设备类型等等,因此在应用开发过程中使用的非常广泛。

本小节仅介绍媒体查询跟断点的结合,即如何借助媒体查询能力,监听断点的变化,读者可以自行查阅官网中关于 媒体查询 的相关介绍了解更详细的用法。

说明: 类Web开发范式,支持在js文件和css文件中使用媒体查询,请查看 js媒体查询 了解详细用法。

示例:

通过媒体查询,监听应用窗口宽度变化,获取当前应用所处的断点值。

1.对通过媒体查询监听断点的功能做简单的封装,方便后续使用

// common/breakpointsystem.ets
import mediaQuery from '@ohos.mediaquery'

declare interface BreakPointTypeOption<T> {
  xs?: T
  sm?: T
  md?: T
  lg?: T
  xl?: T
  xxl?: T
}

export class BreakPointType<T> {
  options: BreakPointTypeOption<T>

  constructor(option: BreakPointTypeOption<T>) {
    this.options = option
  }

  getValue(currentBreakPoint: string) {
    if (currentBreakPoint === 'xs') {
      return this.options.xs
    } else if (currentBreakPoint === 'sm') {
      return this.options.sm
    } else if (currentBreakPoint === 'md') {
      return this.options.md
    } else if (currentBreakPoint === 'lg') {
      return this.options.lg
    } else if (currentBreakPoint === 'xl') {
      return this.options.xl
    } else if (currentBreakPoint === 'xxl') {
      return this.options.xxl
    } else {
      return undefined
    }
  }
}

interface Breakpoint {
  name: string
  size: number
  mediaQueryListener?: mediaQuery.MediaQueryListener
}

export class BreakpointSystem {
  private currentBreakpoint: string = 'md'
  private breakpoints: Breakpoint[] = [
    { name: 'xs', size: 0 }, { name: 'sm', size: 320 },
    { name: 'md', size: 600 }, { name: 'lg', size: 840 }
  ]

  private updateCurrentBreakpoint(breakpoint: string) {
    if (this.currentBreakpoint !== breakpoint) {
      this.currentBreakpoint = breakpoint
      AppStorage.Set<string>('currentBreakpoint', this.currentBreakpoint)
      console.log('on current breakpoint: ' + this.currentBreakpoint)
    }
  }

  public register() {
    this.breakpoints.forEach((breakpoint: Breakpoint, index) => {
      let condition:string
      if (index === this.breakpoints.length - 1) {
        condition = '(' + breakpoint.size + 'vp<=width' + ')'
      } else {
        condition = '(' + breakpoint.size + 'vp<=width<' + this.breakpoints[index + 1].size + 'vp)'
      }
      console.log(condition)
      breakpoint.mediaQueryListener = mediaQuery.matchMediaSync(condition)
      breakpoint.mediaQueryListener.on('change', (mediaQueryResult) => {
        if (mediaQueryResult.matches) {
          this.updateCurrentBreakpoint(breakpoint.name)
        }
      })
    })
  }

  public unregister() {
    this.breakpoints.forEach((breakpoint: Breakpoint) => {
      if(breakpoint.mediaQueryListener){
        breakpoint.mediaQueryListener.off('change')
      }
    })
  }
}

2.在页面中,通过媒体查询,监听应用窗口宽度变化,获取当前应用所处的断点值

// MediaQuerySample.ets
import { BreakpointSystem, BreakPointType } from 'common/breakpointsystem'

@Entry
@Component
struct MediaQuerySample {
  @StorageLink('currentBreakpoint') private currentBreakpoint: string = "md";
  @State private icon: Resource = $r('app.media.md')
  private breakpointSystem: BreakpointSystem = new BreakpointSystem()

  aboutToAppear() {
    this.breakpointSystem.register()
  }

  aboutToDisappear() {
    this.breakpointSystem.unregister()
  }
  build() {
    Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
      Image(new BreakPointType({sm:$r('app.media.sm'), md:$r('app.media.md'), lg:$r('app.media.lg')}).getValue(this.currentBreakpoint)!)
        .height(100)
        .width(100)
        .objectFit(ImageFit.Contain)

      Text(this.currentBreakpoint)
        .fontSize(24)
        .margin(10)
    }
    .width('100%')
    .height('100%')
  }
}

栅格布局

简介

栅格是多设备场景下通用的辅助定位工具,通过将空间分割为有规律的栅格。栅格可以显著降低适配不同屏幕尺寸的设计及开发成本,使得整体设计和开发流程更有秩序和节奏感,同时也保证多设备上应用显示的协调性和一致性,提升用户体验。

栅格的样式由Margin、Gutter、Columns三个属性决定。

  • Margin是相对应用窗口、父容器的左右边缘的距离,决定了内容可展示的整体宽度。

  • Gutter是相邻的两个Column之间的距离,决定内容间的紧密程度。

  • Columns是栅格中的列数,其数值决定了内容的布局复杂度。

单个Column的宽度是系统结合Margin、Gutter和Columns自动计算的,不需要也不允许开发者手动配置。

栅格布局就是栅格结合了断点,实现栅格布局能力的组件叫栅格组件。在实际使用场景中,可以根据需要配置不同断点下栅格组件中元素占据的列数,同时也可以调整Margin、Gutter、Columns的取值,从而实现不同的布局效果。

说明:

  • ArkUI在API 9对栅格组件做了重构,推出了新的栅格组件 GridRow ,同时原有的 GridContainer组件 及 栅格设置 已经废弃。
  • 本文中提到的栅格组件,如无特别说明,都是指GridRow和GridCol组件。

栅格组件的断点

栅格组件提供了丰富的断点定制能力。

(一)开发者可以修改断点的取值范围,支持启用最多6个断点。

  • 基于本文断点小节介绍的推荐值,栅格组件默认提供xs、sm、md、lg四个断点。

  • 栅格组件支持开发者修改断点的取值范围,除了默认的四个断点,还支持开发者启用xl和xxl两个额外的断点。

说明: 断点并非越多越好,通常每个断点都需要开发者“精心适配”以达到最佳显示效果。

示例1:

修改默认的断点范围,同时启用xl和xxl断点。

图片右下角显示了当前设备屏幕的尺寸(即应用窗口尺寸),可以看到随着窗口尺寸发生变化,栅格的断点也相应发生了改变。(为了便于理解,下图中将设备的DPI设置为160,此时1vp=1px)

@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
    })
  }
}

(二)栅格断点默认以窗口宽度为参照物,同时还允许开发者配置为以栅格组件本身的宽度为参照物。

栅格既可以用于页面整体布局的场景,也可以用于页面局部布局的场景。考虑到在实际场景中,存在应用窗口尺寸不变但是局部区域尺寸发生了变化的情况,栅格组件支持以自身宽度为参照物响应断点变化具有更大的灵活性。

示例2:

以栅格组件宽度为参考物响应断点变化。满足窗口尺寸不变,而部分内容区需要做响应式变化的场景。

为了便于理解,下图中自定义预览器的设备屏幕宽度设置为650vp。示例代码中将侧边栏的变化范围控制在[100vp, 600vp],那么右侧的栅格组件宽度相对应在[550vp, 50vp]之间变化。根据代码中对栅格断点的配置,栅格组件宽度发生变化时,其断点相应的发生改变。

@Entry
@Component
struct GridRowSample2 {
  @State private currentBreakpoint: string = 'unknown';
  build() {
    // 用户可以通过拖拽侧边栏组件中的分隔线,调整侧边栏和内容区的宽度。
    SideBarContainer(SideBarContainerType.Embed)
    {
      // 侧边栏,尺寸变化范围 [100vp, 600vp]
      Column(){}.width('100%').backgroundColor('#19000000')

      // 内容区,尺寸变化范围 [550vp, 50vp]
      GridRow({breakpoints: {value: ['100vp', '200vp', '300vp', '400vp', '500vp'],
        reference: BreakpointsReference.ComponentSize}}) {
        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;
      }).width('100%')
    }
    // 侧边栏拖拽到最小宽度时,不自动隐藏
    .autoHide(false)
    .sideBarWidth(100)
    // 侧边栏的最小宽度
    .minSideBarWidth(100)
    // 侧边栏的最大宽度
    .maxSideBarWidth(600)
  }
}

(三)栅格组件的断点发生变化时,会通过onBreakPointChange事件通知开发者。

在之前的两个例子中,已经演示了onBreakpointChange事件的用法,此处不再赘述。

栅格组件的columns、gutter和margin

栅格组件columns默认为12列,gutter默认为0,同时支持开发者根据实际需要定义不同断点下的columns数量以及gutter长度。特别的,在栅格组件实际使用过程中,常常会发生多个元素占据的列数相加超过总列数而折行的场景。栅格组件还允许开发者分别定义水平方向的gutter(相邻两列之间的间距)和垂直方向的gutter(折行时相邻两行之间的间距)。

考虑到 组件通用属性 中已经有margin和padding,栅格组件不再单独提供额外的margin属性,直接使用通用属性即可。借助margin或者padding属性,均可以控制栅格组件与父容器左右边缘的距离,但是二者也存在一些差异:

  • margin区域在栅格组件的边界外,padding区域在栅格组件的边界内。

  • 栅格组件的backgroundColor会影响padding区域,但不会影响margin区域。

总的来讲,margin在组件外而padding在组件内,开发者可以根据实际需要进行选择及实现目标效果。

示例3:

不同断点下,定义不同的columns和gutter。

@Entry
@Component
struct GridRowSample3 {
  private bgColors: ResourceColor[] = [
     $r('sys.color.ohos_id_color_palette_aux1'),
     $r('sys.color.ohos_id_color_palette_aux2'),
     $r('sys.color.ohos_id_color_palette_aux3'),
     $r('sys.color.ohos_id_color_palette_aux4'),
     $r('sys.color.ohos_id_color_palette_aux5'),
     $r('sys.color.ohos_id_color_palette_aux6')
  ]
  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%')
        }
      })
    }
  }
}

示例4:

通过通用属性margin或者padding,均可以控制栅格组件与其父容器左右两侧的距离,但padding区域计算在栅格组件内而margin区域计算在栅格组件外。此外,借助onBreakpointChange事件,还可以改变不同断点下margin或padding值。

@Entry
@Component
struct GridRowSample4 {
  @State private gridMargin: number = 0
  build() {
    Column() {
      Row().width('100%').height(30)

      // 使用padding控制栅格左右间距
      GridRow() {
        GridCol({span:{xs: 12, sm: 12, md: 12, lg:12}}) {
          Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
            Text("padding").fontSize(24).fontWeight(FontWeight.Medium)
          }.backgroundColor('#19000000').width('100%')
        }
      }
      .height(50)
      .borderWidth(2)
      .borderColor('#F1CCB8')
      .padding({left: this.gridMargin, right: this.gridMargin})
      // 借助断点变化事件配置不同断点下栅格组件的左右间距值
      .onBreakpointChange((currentBreakpoint: string) => {
        if (currentBreakpoint === 'lg' || currentBreakpoint === 'md') {
          this.gridMargin = 24
        } else {
          this.gridMargin = 12
        }
      })

      Row().width('100%').height(30)

      // 使用margin控制栅格左右间距
      GridRow() {
        GridCol({span:{xs: 12, sm: 12, md: 12, lg:12}}) {
          Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
            Text("margin").fontSize(24).fontWeight(FontWeight.Medium)
          }.backgroundColor('#19000000').width('100%')
        }
      }
      .height(50)
      .borderWidth(2)
      .borderColor('#F1CCB8')
      .margin({left: this.gridMargin, right: this.gridMargin})
    }
  }
}

栅格组件的span、offset和order

栅格组件(GridRow)的直接孩子节点只可以是栅格子组件(GridCol),GridCol组件支持配置span、offset和order三个参数。这三个参数的取值按照"xs -> sm -> md -> lg -> xl -> xxl"的向后方向具有继承性(不支持向前方向的继承性),例如将sm断点下span的值配置为3,不配置md断点下span的值,则md断点下span的取值也是3。

示例5:

通过span参数配置GridCol在不同断点下占据不同的列数。特别的,将md断点下和6的span配置为0,这样在md断点下3和6不会渲染和显示。

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_color_palette_aux1')},
    {index: 2, color: $r('sys.color.ohos_id_color_palette_aux2')},
    {index: 3, color: $r('sys.color.ohos_id_color_palette_aux3')},
    {index: 4, color: $r('sys.color.ohos_id_color_palette_aux4')},
    {index: 5, color: $r('sys.color.ohos_id_color_palette_aux5')},
    {index: 6, color: $r('sys.color.ohos_id_color_palette_aux6')},
  ]

  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%')
        }
      })
    }
  }
}

示例6:

通过offset参数,配置GridCol相对其前一个兄弟间隔的列数。

class Obj {
  public index: number = 1;
  public color: Resource = $r('sys.color.ohos_id_color_palette_aux1')
}
@Entry
@Component
struct GridRowSample7 {
  private elements: Obj[] = [
    {index: 1, color: $r('sys.color.ohos_id_color_palette_aux1')},
    {index: 2, color: $r('sys.color.ohos_id_color_palette_aux2')},
    {index: 3, color: $r('sys.color.ohos_id_color_palette_aux3')},
    {index: 4, color: $r('sys.color.ohos_id_color_palette_aux4')},
    {index: 5, color: $r('sys.color.ohos_id_color_palette_aux5')},
    {index: 6, color: $r('sys.color.ohos_id_color_palette_aux6')},
  ]

  build() {
    GridRow() {
      ForEach(this.elements, (item:Obj)=>{
        GridCol({span: {sm: 6, md: 4, lg: 3}, order: {lg: (6-item.index)}}) {
          Row() {
            Text('' + item.index).fontSize(24)
          }
          .justifyContent(FlexAlign.Center)
          .backgroundColor(item.color).height(30).width('100%')
        }
      })
    }
  }
}

示例7:

通过order属性,控制GridCol的顺序。在sm和md断点下,按照1至6的顺序排列显示;在lg断点下,按照6至1的顺序排列显示。

class Obj {
  public index: number = 1;
  public color: Resource = $r('sys.color.ohos_id_color_palette_aux1')
}
@Entry
@Component
struct GridRowSample7 {
  private elements: Obj[] = [
    {index: 1, color: $r('sys.color.ohos_id_color_palette_aux1')},
    {index: 2, color: $r('sys.color.ohos_id_color_palette_aux2')},
    {index: 3, color: $r('sys.color.ohos_id_color_palette_aux3')},
    {index: 4, color: $r('sys.color.ohos_id_color_palette_aux4')},
    {index: 5, color: $r('sys.color.ohos_id_color_palette_aux5')},
    {index: 6, color: $r('sys.color.ohos_id_color_palette_aux6')},
  ]

  build() {
    GridRow() {
      ForEach(this.elements, (item:Obj)=>{
        GridCol({span: {sm: 6, md: 4, lg: 3}, order: {lg: (6-item.index)}}) {
          Row() {
            Text('' + item.index).fontSize(24)
          }
          .justifyContent(FlexAlign.Center)
          .backgroundColor(item.color).height(30).width('100%')
        }
      })
    }
  }
}

示例8:

仅配置sm和lg断点下span、offset和order参数的值,则md断点下这三个参数的取值与sm断点相同(按照“sm->md->lg”的向后方向继承)。

class Obj {
  public index: number = 1;
  public color: Resource = $r('sys.color.ohos_id_color_palette_aux1')
}
@Entry
@Component
struct GridRowSample8 {
  private elements: Obj[] = [
    {index: 1, color: $r('sys.color.ohos_id_color_palette_aux1')},
    {index: 2, color: $r('sys.color.ohos_id_color_palette_aux2')},
    {index: 3, color: $r('sys.color.ohos_id_color_palette_aux3')},
    {index: 4, color: $r('sys.color.ohos_id_color_palette_aux4')},
    {index: 5, color: $r('sys.color.ohos_id_color_palette_aux5')},
    {index: 6, color: $r('sys.color.ohos_id_color_palette_aux6')},
  ]
  build() {
    GridRow() {
      ForEach(this.elements, (item:Obj)=>{
        // 不配置md断点下三个参数的值,则其取值与sm断点相同
        GridCol({span: {sm:4, lg: 3}, offset: {sm: 2, lg: 1},
          order: {sm: (6-item.index), lg: item.index}}) {
          Row() {
            Text('' + item.index).fontSize(24)
          }
          .justifyContent(FlexAlign.Center)
          .backgroundColor(item.color).height(30).width('100%')
        }
      })
    }
  }
}

栅格组件的嵌套使用

栅格组件可以嵌套使用以满足复杂场景的需要。

示例9:

class Obj {
  public index: number = 1;
  public color: Resource = $r('sys.color.ohos_id_color_palette_aux1')
}
@Entry
@Component
struct GridRowSample9 {
  private elements: Obj[] = [
    {index: 1, color: $r('sys.color.ohos_id_color_palette_aux1')},
    {index: 2, color: $r('sys.color.ohos_id_color_palette_aux2')},
    {index: 3, color: $r('sys.color.ohos_id_color_palette_aux3')},
    {index: 4, color: $r('sys.color.ohos_id_color_palette_aux4')},
    {index: 5, color: $r('sys.color.ohos_id_color_palette_aux5')},
    {index: 6, color: $r('sys.color.ohos_id_color_palette_aux6')},
  ]
  build() {
    GridRow() {
      GridCol({span: {sm: 12, md: 10, lg: 8}, offset: {sm: 0, md: 1, lg: 2}}) {
        GridRow() {
          ForEach(this.elements, (item:Obj)=>{
            GridCol({span: {sm: 6, md: 4, lg: 3}}) {
              Row() {
                Text('' + item.index).fontSize(24)
              }
              .justifyContent(FlexAlign.Center)
              .backgroundColor(item.color).height(30).width('100%')
            }
          })
        }
        .backgroundColor('#19000000')
        .height('100%')
      }
    }
  }
}

总结

如前所述,栅格组件提供了丰富的自定义能力,功能异常灵活和强大。只需要明确栅格在不同断点下的Columns、Margin、Gutter及span等参数,即可确定最终布局,无需关心具体的设备类型及设备状态(如横竖屏)等。栅格可以节约设计团队与开发团队的沟通成本,提升整体开发效率。

为了能让大家更好的学习鸿蒙(HarmonyOS NEXT)开发技术,这边特意整理了《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙开发学习手册》:

如何快速入门:https://qr21.cn/FV7h05

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. ……

开发基础知识:https://qr21.cn/FV7h05

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

基于ArkTS 开发:https://qr21.cn/FV7h05

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. ……

鸿蒙开发面试真题(含参考答案):https://qr18.cn/F781PH

鸿蒙开发面试大盘集篇(共计319页):https://qr18.cn/F781PH

1.项目开发必备面试题
2.性能优化方向
3.架构方向
4.鸿蒙开发系统底层方向
5.鸿蒙音视频开发方向
6.鸿蒙车载开发方向
7.鸿蒙南向开发方向

为了能让大家更好的学习鸿蒙(HarmonyOS NEXT)开发技术,这边特意整理了《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙开发学习手册》:

如何快速入门:https://qr21.cn/FV7h05

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. ……

开发基础知识:https://qr21.cn/FV7h05

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

基于ArkTS 开发:https://qr21.cn/FV7h05

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. ……

鸿蒙开发面试真题(含参考答案):https://qr18.cn/F781PH

鸿蒙开发面试大盘集篇(共计319页):https://qr18.cn/F781PH

1.项目开发必备面试题
2.性能优化方向
3.架构方向
4.鸿蒙开发系统底层方向
5.鸿蒙音视频开发方向
6.鸿蒙车载开发方向
7.鸿蒙南向开发方向

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/477139.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Blender小:图片转mesh,并展UV

其实就这么一个插件&#xff1a;Image2Mesh https://github.com/kedepot/i2m-std blender加载完图片之后点这个就行 到了这一步还不行如果直接放到unity中使用的话&#xff0c;添加材质和贴图之后每一个小块都是独立的区域来展示贴图&#xff0c;所以需要展UV

UE5 C++增强输入

一.创建charactor&#xff0c;并且包含增强输入相关的头文件 1.项目名.build.cs。添加模块“EnhancedInput”&#xff0c;方便找到头文件和映射的一些文件。 PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine&q…

kafka学习笔记01(小滴课堂)

介绍分布式流处理平台kafka快速认知 介绍分布式流处理平台kafka核心概念解释 急速部署-Kafka相关环境准备和安装 Linux环境下Zookeeper和Kafka安装启动 解压两个软件的压缩包&#xff1a; tar -zxvf 启动zk: 去log目录进行查看&#xff1a; 查看一下2181端口是否被占用: 安装…

XML Data – Semi-Structured Data XML 数据 - 半结构化数据

Outline • Structured, Semistructured, and Unstructured Data • XML Hierarchical (Tree) Data Model • Extracting XML Documents from Relational Databases • XML Documents, DTD, and XML Schema • XML Languages 结构化、半结构化和非结构化数据 - XML 层次&#x…

linux之shell脚本基础

1.构建基础脚本 1.1 创建shell脚本 1.1.1 第一行需要指定使用的shell # 用作注释行.shell并不会处理脚本中的注释行,但是第一行的注释,会告诉shell使用哪个shell来运行脚本. #!/bin/bash 1.1.2 让shell找到你的脚本 直接运行脚本会提示-bash: a.sh: command not found.因…

Docker部署Alist全平台网盘神器结合内网穿透实现无公网IP访问云盘资源

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法|MySQL| ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-oZuxWTWUiXLx3aQO {font-family:"trebuchet ms",verdana,arial,sans-serif;f…

拌合楼管理系统(九) 地磅称重作弊的方式防范的方法

前言&#xff1a;不谈技术谈谈管理 大宗物资的行业离不开地磅&#xff0c;称重过磅作为运输货物的核心一环&#xff0c;往往很多出现货物损失的情况&#xff0c;都出现在称重作弊&#xff0c;一次作弊就可以导致企业直接的经济损失&#xff0c;让我想起一首古诗硕鼠硕鼠&#x…

LeetCode 21 / 100

目录 矩阵矩阵置零螺旋矩阵旋转图像搜索二维矩阵 II LeetCode 73. 矩阵置零 LeetCode 54. 螺旋矩阵 LeetCode 48. 旋转图像 LeetCode 240. 搜索二维矩阵 II 矩阵 矩阵置零 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为…

K8s-网络原理-中篇

引言 本文是《深入剖析 K8s》的学习笔记&#xff0c;相关图片和案例可从https://github.com/WeiXiao-Hyy/k8s_example中获取&#xff0c;欢迎 ⭐️! 上篇主要介绍了 Flannel 插件为例&#xff0c;讲解了 K8s 里容器网络和 CNI 插件的主要工作原理。还有一种“纯三层”的网络方…

【HM】STM32F407 HAL库 定时器

基本概念 兆赫兹 1MHZ&#xff08;兆赫兹&#xff09;是频率的单位&#xff0c;表示每秒周期性震动1,000,000次。 预分频器 不分频 2分频&#xff0c;两个脉冲输出一次 三分频 自动重装载寄存器 当计时器里的计数器自动重装载寄存器值&#xff0c;计数器清零 定时器分类 …

【ESP32 IDF】pwm脉宽调制技术

文章目录 前言一、PWM脉宽调制技术介绍二、pwm的使用2.1 LEDC定时器结构体结构体介绍配置定时器 2.2 配置LEDC通道结构体介绍初始化pwm 2.3 设置占空比设置占空比更新占空比 三、示例代码总结 前言 PWM&#xff08;Pulse Width Modulation&#xff0c;脉宽调制&#xff09;是一…

基于yolov5的单目测距实现与总结+相机模型+标定

写这篇文章的目的是为了总结我之前看的标定&#xff0c;相机模型以及单目测距的内容&#xff0c;如果有错误&#xff0c;还请不吝赐教。 参考链接&#xff1a; 相机模型、相机标定及基于yolov5的单目测距实现 深度学习目标检测目标追踪单目测距 单目测距代码部署&#xff08;目…

Linux的基本使用

1.Linux的背景 1.1什么Linux Linux是⼀个操作系统.和Windows是"并列"的关系. 1.2Linux系统的优势 1. 开源(意味着免费,便宜) 2. 稳定(Linux可以运⾏很多年,都不会发⽣重⼤问题) 3. 安全(Linux只有管理员或者特定⽤⼾才能访问Linux内核) 4. ⾃由(不会被强加商业产品和…

点对点协议PPP(数据链路层)

目录 一、点对点协议PPP的特点 二、PPP协议的基本要求 三、PPP协议应满足的需求 四、PPP协议的组成 五、PPP同步传输和异步传输 六、PPP同步传输和异步传输 七、可靠传输问题 八、PPP协议的工作状态&#xff08;同步&#xff09; 九、小结 一、点对点协议PPP的特点 •…

Github 2024-03-21 Go开源项目日报 Top10

根据Github Trendings的统计,今日(2024-03-21统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Go项目10HTML项目1Milvus: 云原生向量数据库与嵌入式相似性搜索 创建周期:1620 天开发语言:Go协议类型:Apache License 2.0Star数量:25568 …

2024.4.11-12中国汽车网络安全及数据安全合规峰

本次安策将在2024年4月11日-12日谈思AutoSec 8周年年会暨中国汽车网络安全及数据安全合规峰会现场展示相关产品&#xff0c;展位号A8&#xff0c;欢迎莅临参观交流。本次会议安策将带给大家汽车行业数据安全合规的最新应用案例。 汽车行业的数字化革命 为推动这场革命&#xff…

Redis进阶(持久化、复制、集群、多线程、缓存)

Redis进阶 1.Redis持久化1.1 什么是Redis持久化&#xff1f;为什么需要持久化&#xff1f;1.2 Redis持久化方式——RDB(Redis DataBase)1.2.1 什么是RDB&#xff1f;1.2.2 备份文件位置1.2.3 触发RDB的方式1.2.3.1 自动触发1.2.3.2 手动触发1.2.3.3 其他触发方式 1.2.4 RDB优缺…

DataEase大屏iframe嵌入自建网站(React)

1、修改dataease 所在的服务器nginx配置 server {listen 80;server_name dataease.ibaiqiu.cn;return 307 https://$host$request_uri; } server {listen 443 ssl;server_name dataease.ibaiqiu.cn;client_max_body_size 30M;ssl_certificate /usr/local/nginx/co…

旅游小程序开发的费用及功能

随着科技的发展和智能手机的普及&#xff0c;越来越多的行业开始利用小程序来进行线上服务。旅游业作为一个重要的服务业&#xff0c;也纷纷推出了自己的旅游小程序&#xff0c;以方便游客在线预订、查询景点信息等。那么&#xff0c;旅游小程序开发的费用是多少&#xff1f;功…

Linux系统编程(笔记)

1、认识计算机系统&#xff08;上&#xff09; 1.1、计算机系统由软硬件构成 1.2、总线 1.3、I/O设备 1.4、内存 1.5、处理器 1.6、计算机硬件组成 2、认识计算机系统&#xff08;下&#xff09; 2.1、什么是操作系统 2.2、Linux内核模块 2.3、操作系统管理硬件&#xff08;职…