前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >鸿蒙-元服务-坚果派-第三章 布局基础(二)

鸿蒙-元服务-坚果派-第三章 布局基础(二)

作者头像
红目香薰
发布于 2025-02-19 23:46:58
发布于 2025-02-19 23:46:58
10501
代码可运行
举报
文章被收录于专栏:CSDNToQQCodeCSDNToQQCode
运行总次数:1
代码可运行

作者简介:大数据领域优质创作者、CSDN博客专家 、阿里云博客专家、华为云课堂认证讲师、华为云社区云享专家、坚果派社区成员、具有10余年横向开发经验,全国教师技能大赛获奖教师,现从事于大学计算机领域教育工作。 主要内容:人工智能与大数据、JavaPython、C#、PHP、ASP.NET、ArkTS、FAQ、简历模板、学习资料、面试题库、就业指导等。 初心目标:持续输出,为技术人创造更多的价值。

栅格布局(GridRow/GridCol)

栅格布局概述

栅格布局是一种通用的辅助定位工具,对移动设备的界面设计有较好的借鉴作用。主要优势包括:

  • 提供可循的规律:栅格布局可以为布局提供规律性的结构,解决多尺寸多设备的动态布局问题。通过将页面划分为等宽的列数和行数,可以方便地对页面元素进行定位和排版。
  • 统一的定位标注:栅格布局可以为系统提供一种统一的定位标注,保证不同设备上各个模块的布局一致性。这可以减少设计和开发的复杂度,提高工作效率。
  • 灵活的间距调整方法:栅格布局可以提供一种灵活的间距调整方法,满足特殊场景布局调整的需求。通过调整列与列之间和行与行之间的间距,可以控制整个页面的排版效果。
  • 自动换行和自适应:栅格布局可以完成一对多布局的自动换行和自适应。当页面元素的数量超出了一行或一列的容量时,他们会自动换到下一行或下一列,并且在不同的设备上自适应排版,使得页面布局更加灵活和适应性强。

GridRow为栅格容器组件,需与栅格子组件GridCol在栅格布局场景中联合使用。

栅格容器GridRow
栅格系统断点

栅格系统以设备的水平宽度(屏幕密度像素值,单位vp)作为断点依据,定义设备的宽度类型,形成了一套断点规则。开发者可根据需求在不同的断点区间实现不同的页面布局效果。

栅格系统默认断点将设备宽度分为xs、sm、md、lg四类,尺寸范围如下:

断点名称

取值范围(vp)

设备描述

xs

[0, 320)

最小宽度类型设备。

sm

[320, 520)

小宽度类型设备。

md

[520, 840)

中等宽度类型设备。

lg

[840, +∞)

大宽度类型设备。

在GridRow栅格组件中,允许开发者使用breakpoints自定义修改断点的取值范围,最多支持6个断点,除了默认的四个断点外,还可以启用xl,xxl两个断点,支持六种不同尺寸(xs, sm, md, lg, xl, xxl)设备的布局设置。

断点名称

设备描述

xs

最小宽度类型设备。

sm

小宽度类型设备。

md

中等宽度类型设备。

lg

大宽度类型设备。

xl

特大宽度类型设备。

xxl

超大宽度类型设备。

针对断点位置,开发者根据实际使用场景,通过一个单调递增数组设置。由于breakpoints最多支持六个断点,单调递增数组长度最大为5。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
breakpoints: {value: ['100vp', '200vp']}

表示启用xs、sm、md共3个断点,小于100vp为xs,100vp-200vp为sm,大于200vp为md。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
breakpoints: {value: ['320vp', '520vp', '840vp', '1080vp']}

表示启用xs、sm、md、lg、xl共5个断点,小于320vp为xs,320vp-520vp为sm,520vp-840vp为md,840vp-1080vp为lg,大于1080vp为xl。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Entry
@Component
struct ThirdPage {
  @State bgColors: Color[] =
    [Color.Red, Color.Orange, Color.Yellow, Color.Green, Color.Pink, Color.Grey, Color.Blue, Color.Brown];

  build() {
    GridRow({
      breakpoints: {
        value: ['200vp', '300vp', '400vp', '500vp', '600vp'],
        reference: BreakpointsReference.WindowSize
      }
    }) {
      ForEach(this.bgColors, (color: Color, index) => {
        GridCol({
          span: {
            xs: 2, // 在最小宽度类型设备上,栅格子组件占据的栅格容器2列。
            sm: 3, // 在小宽度类型设备上,栅格子组件占据的栅格容器3列。
            md: 4, // 在中等宽度类型设备上,栅格子组件占据的栅格容器4列。
            lg: 6, // 在大宽度类型设备上,栅格子组件占据的栅格容器6列。
            xl: 8, // 在特大宽度类型设备上,栅格子组件占据的栅格容器8列。
            xxl: 12 // 在超大宽度类型设备上,栅格子组件占据的栅格容器12列。
          }
        }) {
          Row() {
            Text(`${index}`)
          }.width("100%").height('50vp').justifyContent(FlexAlign.Center)
        }.backgroundColor(color)
      })
    }
  }
}
布局的总列数

GridRow中通过columns设置栅格布局的总列数。

columns默认值为12,即在未设置columns时,任何断点下,栅格布局被分成12列。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Entry
@Component
struct ThirdPage {
  @State bgColors: Color[] = [Color.Red, Color.Orange, Color.Yellow, Color.Green, Color.Pink, Color.Grey, Color.Blue, Color.Brown,Color.Red, Color.Orange, Color.Yellow, Color.Green];

  build() {
    GridRow() {
      ForEach(this.bgColors, (item:Color, index) => {
        GridCol() {
          Row() {
            Text(`${index}`)
          }.width('100%').height(150)
        }.backgroundColor(item)
      })
    }.height('100%').width('100%')
  }
}

当columns为自定义值,栅格布局在任何尺寸设备下都被分为columns列。下面分别设置栅格布局列数为4和8,子元素默认占一列,效果如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Entry
@Component
struct ThirdPage {
  @State bgColors: Color[] = [Color.Red, Color.Orange, Color.Yellow, Color.Green, Color.Pink, Color.Grey, Color.Blue, Color.Brown,Color.Red, Color.Orange, Color.Yellow, Color.Green];

  build() {
    GridRow({ columns: 3 }) {// 默认值1
      ForEach(this.bgColors, (item:Color, index) => {
        GridCol() {
          Row() {
            Text(`${index}`)
          }.height(150)
        }.backgroundColor(item)
      })
    }.height('100%').width('100%')
  }
}

当columns类型为GridRowColumnOption时,支持下面六种不同尺寸(xs, sm, md, lg, xl, xxl)设备的总列数设置,各个尺寸下数值可不同。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Entry
@Component
struct ThirdPage {
  @State bgColors: Color[] = [Color.Red, Color.Orange, Color.Yellow, Color.Green, Color.Pink, Color.Grey, Color.Blue, Color.Brown,Color.Red, Color.Orange, Color.Yellow, Color.Green];

  build() {
    GridRow({ columns: { xs:4,sm: 6, md: 3 } }) {
      ForEach(this.bgColors, (item:Color, index?:number|undefined) => {
        GridCol() {
          Row() {
            Text(`${index}`)
          }.width('100%').height('50')
        }.backgroundColor(item)
      })
    }
  }
}
排列方向

栅格布局中,可以通过设置GridRow的direction属性来指定栅格子组件在栅格容器中的排列方向。该属性可以设置为GridRowDirection.Row(从左往右排列)或GridRowDirection.RowReverse(从右往左排列),以满足不同的布局需求。通过合理的direction属性设置,可以使得页面布局更加灵活和符合设计要求。

子组件默认从左往右排列。

GridRow({ direction: GridRowDirection.Row }){}

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Entry
@Component
struct ThirdPage {
  @State bgColors: Color[] = [Color.Red, Color.Orange, Color.Yellow, Color.Green, Color.Pink, Color.Grey, Color.Blue, Color.Brown,Color.Red, Color.Orange, Color.Yellow, Color.Green];

  build() {
    GridRow({ direction: GridRowDirection.Row,columns:4 }){// 正向排序
      ForEach(this.bgColors, (item:Color, index?:number|undefined) => {
        GridCol() {
          Row() {
            Text(`${index}`)
          }.width('100%').height('50')
        }.backgroundColor(item)
      })
    }
  }
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Entry
@Component
struct ThirdPage {
  @State bgColors: Color[] = [Color.Red, Color.Orange, Color.Yellow, Color.Green, Color.Pink, Color.Grey, Color.Blue, Color.Brown,Color.Red, Color.Orange, Color.Yellow, Color.Green];

  build() {
    GridRow({ direction: GridRowDirection.RowReverse,columns:4 }){// 正向排序
      ForEach(this.bgColors, (item:Color, index?:number|undefined) => {
        GridCol() {
          Row() {
            Text(`${index}`)
          }.width('100%').height('50')
        }.backgroundColor(item)
      })
    }
  }
}
子组件间距

GridRow中通过gutter属性设置子元素在水平和垂直方向的间距。

gutter类型为number时,同时设置栅格子组件间水平和垂直方向边距且相等。下例中,设置子组件水平与垂直方向距离相邻元素的间距为10。

GridRow({ gutter: 10 }){}

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Entry
@Component
struct ThirdPage {
  @State bgColors: Color[] = [Color.Red, Color.Orange, Color.Yellow, Color.Green, Color.Pink, Color.Grey, Color.Blue, Color.Brown,Color.Red, Color.Orange, Color.Yellow, Color.Green];

  build() {
    GridRow({ gutter: 10,direction: GridRowDirection.Row,columns:4 }){// 正向排序
      ForEach(this.bgColors, (item:Color, index?:number|undefined) => {
        GridCol() {
          Row() {
            Text(`${index}`)
          }.width('100%').height('50')
        }.backgroundColor(item)
      })
    }
  }
}

可以很清楚的看到每个控件之间的距离。

当gutter类型为GutterOption时,单独设置栅格子组件水平垂直边距,x属性为水平方向间距,y为垂直方向间距。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Entry
@Component
struct ThirdPage {
  @State bgColors: Color[] = [Color.Red, Color.Orange, Color.Yellow, Color.Green, Color.Pink, Color.Grey, Color.Blue, Color.Brown,Color.Red, Color.Orange, Color.Yellow, Color.Green];

  build() {
    GridRow({ gutter: { x: 10, y: 100 },direction: GridRowDirection.Row,columns:4 }){// 正向排序
      ForEach(this.bgColors, (item:Color, index?:number|undefined) => {
        GridCol() {
          Row() {
            Text(`${index}`)
          }.width('100%').height('50')
        }.backgroundColor(item)
      })
    }
  }
}
栅格组件的嵌套使用

栅格组件也可以嵌套使用,完成一些复杂的布局。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * @author 坚果派-红目香薰
 * @date 2024年11月6日22:43:42
 */
@Entry
@Component
struct ThirdPage {
  build() {
    GridRow() {
      GridCol({ span: { sm: 12 } }) {
        Row() {
          Text('坚果派-红目香薰-元服务练习')
            .fontColor(Color.White)
            .fontSize(18)
            .width('100%')
            .height('100%')
            .textAlign(TextAlign.Start)
            .fontWeight(FontWeight.Bolder)
        }.width('100%').height('100%').backgroundColor(Color.Black)
      }.width('100%').height('10%')

      GridCol({ span: { sm: 12 } }) {
        GridRow() {
          GridCol({ span: { sm: 2 } }) {
            Row() {
              Text('左侧菜单').fontSize(24)
            }
            .height('100%')
          }.backgroundColor('#66CCFF')

          GridCol({ span: { sm: 10 } }) {
            Row() {
              Text('右侧正文').fontSize(24)
            }
            .height('100%')
          }.backgroundColor("#F2F2F2")
        }.width('100%').height('80%')
      }

      GridCol({ span: { sm: 12 } }) {
        Row() {
          Text('底部')
            .fontColor(Color.White)
            .fontSize(24)
            .width('100%')
            .height('100%')
            .textAlign(TextAlign.Center)
        }.width('100%').height('100%').backgroundColor(Color.Black)
      }.width('100%').height('10%')
    }
  }
}

栅格布局实战——四则计算器

具体流程请观看课件视频。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * @author 坚果派-红目香薰
 * @date 2024年11月7日00:55:37
 */
@Entry
@Component
struct CalcPage {
  @State x: number = 0;
  @State y: number = 0;
  @State operator: string = "";
  @State result: string = "";

  build() {
    GridRow({ gutter: { y: 10 } }) {
      GridCol({ span: 12 }) {
        TextInput({ placeholder: '0', text: this.result })
          .type(InputType.Normal)
          .width('100%')
          .height('10%')
          .backgroundColor(Color.White)
          .textAlign(TextAlign.End)
          .fontSize(36)
          .margin({ top: '15%' })
          .placeholderFont({ size: 36 })
      }.width('100%')

      GridCol({ span: 3 }) {
        Button('7')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.result += "7";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('8')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.result += "8";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('9')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.result += "9";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('÷')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.x = parseFloat(this.result);
            this.operator = "÷";
            this.result = ""
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('4')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.result += "4";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('5')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.result += "5";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('6')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.result += "6";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('×')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.x = parseFloat(this.result);
            this.operator = "×";
            this.result = ""
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('1')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.result += "1";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('2')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.result += "2";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('3')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.result += "3";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('-')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.x = parseFloat(this.result);
            this.operator = "-";
            this.result = ""
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('CE')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.x = 0;
            this.operator ='';
            this.y = 0;
            this.result = "";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('0')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.result += "0";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('.')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.result += ".";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('+')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.x = parseFloat(this.result);
            this.operator = "+";
            this.result = ""
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 12 }) {
        Button('=')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.y = parseFloat(this.result);
            switch (this.operator) {
              case '+':
                this.result = (this.x + this.y).toString();
                break;
              case '-':
                this.result = (this.x - this.y).toString();
                break;
              case '×':
                this.result = (this.x * this.y).toString();
                break;
              case '÷':
                this.result = (this.x / this.y).toString();
                break;
            }
          })
      }.height('12%').margin({ right: 5 })
    }.width('100%').height('100%').backgroundColor("#F2F2F2")
  }
}

沉浸式效果

典型应用全屏窗口UI元素包括状态栏、应用界面和底部导航条,其中状态栏和导航条,通常在沉浸式布局下称为避让区;避让区之外的区域称为安全区。开发应用沉浸式效果主要指通过调整状态栏、应用界面和导航条的显示效果来减少状态栏导航条等系统界面的突兀感,从而使用户获得最佳的UI体验。

窗口全屏布局方案
不隐藏避让区

可以通过调用窗口强制全屏布局接口setWindowLayoutFullScreen()实现界面元素延伸到状态栏和导航条;然后通过接口getWindowAvoidArea()和on('avoidAreaChange')获取并动态监听避让区域的变更信息,页面布局根据避让区域信息进行动态调整;设置状态栏或导航条的颜色等属性与界面元素进行匹配。

示例:setWindowLayoutFullScreen()接口设置窗口全屏

引包:【import { BusinessError } from '@kit.BasicServicesKit';】引包到【EntryAbility.est】中,下文图片的第四行即是。

完整的插入代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let windowClass: window.Window = windowStage.getMainWindowSync(); // 获取应用主窗口
// 1. 设置窗口全屏
let isLayoutFullScreen = true;
windowClass.setWindowLayoutFullScreen(isLayoutFullScreen).then(() => {
  console.info('Succeeded in setting the window layout to full-screen mode.');
}).catch((err: BusinessError) => {
  console.error('Failed to set the window layout to full-screen mode. Cause:' + JSON.stringify(err));
});

// EntryAbility.ets
// 2. 获取布局避让遮挡的区域
let type = window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR; // 以导航条避让为例
let avoidArea = windowClass.getWindowAvoidArea(type);
let bottomRectHeight = avoidArea.bottomRect.height; // 获取到导航条区域的高度
AppStorage.setOrCreate('bottomRectHeight', bottomRectHeight);

type = window.AvoidAreaType.TYPE_SYSTEM; // 以状态栏避让为例
avoidArea = windowClass.getWindowAvoidArea(type);
let topRectHeight = avoidArea.topRect.height; // 获取状态栏区域高度
AppStorage.setOrCreate('topRectHeight', topRectHeight);

// EntryAbility.ets
// 3. 注册监听函数,动态获取避让区域数据
windowClass.on('avoidAreaChange', (data) => {
  if (data.type === window.AvoidAreaType.TYPE_SYSTEM) {
    let topRectHeight = data.area.topRect.height;
    AppStorage.setOrCreate('topRectHeight', topRectHeight);
  } else if (data.type == window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR) {
    let bottomRectHeight = data.area.bottomRect.height;
    AppStorage.setOrCreate('bottomRectHeight', bottomRectHeight);
  }
});

页面中添加设置

参数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@StorageProp('bottomRectHeight')
bottomRectHeight: number = 0;
@StorageProp('topRectHeight')
topRectHeight: number = 0;

最外层添加属性:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// top数值与状态栏区域高度保持一致;bottom数值与导航条区域高度保持一致
.padding({ top: px2vp(this.topRectHeight), bottom: px2vp(this.bottomRectHeight) })

完整代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * @author 坚果派-红目香薰
 * @date 2024年11月8日13:17:50
 */
@Entry
@Component
struct CalcPage {
  @State x: number = 0;
  @State y: number = 0;
  @State operator: string = "";
  @State result: string = "";
  @StorageProp('bottomRectHeight')
  bottomRectHeight: number = 0;
  @StorageProp('topRectHeight')
  topRectHeight: number = 0;
  build() {
    GridRow({ gutter: { y: 10 } }) {
      GridCol({ span: 12 }) {
        TextInput({ placeholder: '0', text: this.result })
          .type(InputType.Normal)
          .width('100%')
          .height('10%')
          .backgroundColor(Color.White)
          .textAlign(TextAlign.End)
          .fontSize(36)
          .margin({ top: '15%' })
          .placeholderFont({ size: 36 })
      }.width('100%')

      GridCol({ span: 3 }) {
        Button('7')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.result += "7";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('8')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.result += "8";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('9')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.result += "9";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('÷')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.x = parseFloat(this.result);
            this.operator = "÷";
            this.result = ""
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('4')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.result += "4";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('5')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.result += "5";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('6')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.result += "6";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('×')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.x = parseFloat(this.result);
            this.operator = "×";
            this.result = ""
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('1')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.result += "1";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('2')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.result += "2";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('3')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.result += "3";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('-')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.x = parseFloat(this.result);
            this.operator = "-";
            this.result = ""
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('CE')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.x = 0;
            this.operator ='';
            this.y = 0;
            this.result = "";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('0')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.result += "0";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('.')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.result += ".";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('+')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.x = parseFloat(this.result);
            this.operator = "+";
            this.result = ""
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 12 }) {
        Button('=')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.y = parseFloat(this.result);
            switch (this.operator) {
              case '+':
                this.result = (this.x + this.y).toString();
                break;
              case '-':
                this.result = (this.x - this.y).toString();
                break;
              case '×':
                this.result = (this.x * this.y).toString();
                break;
              case '÷':
                this.result = (this.x / this.y).toString();
                break;
            }
          })
      }.height('12%').margin({ right: 5 })
    }.width('100%').height('100%').backgroundColor(Color.Red)
    // top数值与状态栏区域高度保持一致;bottom数值与导航条区域高度保持一致
    .padding({ top: px2vp(this.topRectHeight), bottom: px2vp(this.bottomRectHeight) })
  }
}
隐藏避让区

此场景下导航条会自动隐藏,适用于游戏、电影等应用场景。可以通过从底部上滑唤出导航条。

设置代码:修改上文中的设置位置即可,这里只有:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let windowClass: window.Window = windowStage.getMainWindowSync(); // 获取应用主窗口
// 1. 设置窗口全屏
let isLayoutFullScreen = true;
windowClass.setWindowLayoutFullScreen(isLayoutFullScreen).then(() => {
  console.info('Succeeded in setting the window layout to full-screen mode.');
}).catch((err: BusinessError) => {
  console.error(`Failed to set the window layout to full-screen mode. Code is ${err.code}, message is ${err.message}`);
});
// EntryAbility.ets
// 2. 设置状态栏隐藏
windowClass.setSpecificSystemBarEnabled('status', false).then(() => {
  console.info('Succeeded in setting the status bar to be invisible.');
}).catch((err: BusinessError) => {
  console.error(`Failed to set the status bar to be invisible. Code is ${err.code}, message is ${err.message}`);
});
// 3. 设置导航条隐藏
windowClass.setSpecificSystemBarEnabled('navigationIndicator', false).then(() => {
  console.info('Succeeded in setting the navigation indicator to be invisible.');
}).catch((err: BusinessError) => {
  console.error(`Failed to set the navigation indicator to be invisible. Code is ${err.code}, message is ${err.message}`);
});

可以看到上方的电池、时间等内容全部都隐藏了。

组件安全区方案

应用未使用setWindowLayoutFullScreen()接口设置窗口全屏布局时,默认使能组件安全区布局。

应用在默认情况下窗口背景绘制范围是全屏,但UI元素被限制在安全区内(自动排除状态栏和导航条)进行布局,来避免界面元素被状态栏和导航条遮盖。

针对状态栏和导航条颜色与界面元素颜色不匹配问题,可以通过如下实现沉浸式效果:

颜色向上TOP延伸,如果向下延伸则为【SafeAreaEdge.BOTTOM】:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP])

全窗颜色和应用元素颜色一致

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * @author 坚果派-红目香薰
 * @date 2024年11月8日13:17:50
 */
@Entry
@Component
struct CalcPage {
  @State x: number = 0;
  @State y: number = 0;
  @State operator: string = "";
  @State result: string = "";
  build() {
    GridRow({ gutter: { y: 10 } }) {
      GridCol({ span: 12 }) {
        TextInput({ placeholder: '0', text: this.result })
          .type(InputType.Normal)
          .width('100%')
          .height('10%')
          .backgroundColor(Color.White)
          .textAlign(TextAlign.End)
          .fontSize(36)
          .margin({ top: '15%' })
          .placeholderFont({ size: 36 })
      }.width('100%')

      GridCol({ span: 3 }) {
        Button('7')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.result += "7";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('8')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.result += "8";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('9')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.result += "9";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('÷')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.x = parseFloat(this.result);
            this.operator = "÷";
            this.result = ""
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('4')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.result += "4";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('5')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.result += "5";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('6')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.result += "6";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('×')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.x = parseFloat(this.result);
            this.operator = "×";
            this.result = ""
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('1')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.result += "1";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('2')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.result += "2";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('3')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.result += "3";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('-')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.x = parseFloat(this.result);
            this.operator = "-";
            this.result = ""
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('CE')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.x = 0;
            this.operator ='';
            this.y = 0;
            this.result = "";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('0')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.result += "0";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('.')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.result += ".";
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 3 }) {
        Button('+')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.x = parseFloat(this.result);
            this.operator = "+";
            this.result = ""
          })
      }.height('15%').margin({ right: 5 })

      GridCol({ span: 12 }) {
        Button('=')
          .type(ButtonType.Normal)
          .fontSize(36)
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.y = parseFloat(this.result);
            switch (this.operator) {
              case '+':
                this.result = (this.x + this.y).toString();
                break;
              case '-':
                this.result = (this.x - this.y).toString();
                break;
              case '×':
                this.result = (this.x * this.y).toString();
                break;
              case '÷':
                this.result = (this.x / this.y).toString();
                break;
            }
          })
      }.height('12%').margin({ right: 5 })
    }.width('100%').height('100%').backgroundColor('#FFAFCC').expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP])
  }
}

可以很清晰的看到向上与向下延伸的效果。

扩展安全区域属性原理

布局阶段按照安全区范围大小进行UI元素布局。 布局完成后查看设置了expandSafeArea的组件边界(不包括margin)是否和安全区边界相交。 如果设置了expandSafeArea的组件和安全区边界相交,根据expandSafeArea传递的属性则进一步扩大组件绘制区域大小覆盖状态栏、导航条这些非安全区域。 上述过程仅改变组件自身绘制大小,不进行二次布局,不影响子节点和兄弟节点的大小和位置。 子节点可以单独设置该属性,只需要自身边界和安全区域重合就可以延伸自身大小至非安全区域内,需要确保父组件未设置clip等裁切属性。 配置expandSafeArea属性组件进行绘制扩展时,需要关注组件不能配置固定宽高尺寸,百分比除外。

背景图和视频场景

设置背景图、视频控件大小为安全区域大小并配置expandSafeArea属性,这里可以向上向下同时延伸。

图文示例

图片与文字对应的组件进行延伸的示例。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Entry
@Component
struct Show_up_img {
  build() {
    RelativeContainer(){
      Column() {
        Image($r('app.media.random_1_1024x1024'))
          .height('50%').width('100%')
          // 设置图片延伸到状态栏
          .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP])
        Column() {
          Text('坚果派-红目香薰')
            .fontSize(32)
            .margin(30)
          Text('通过循序渐进的学习路径,无经验和有经验的开发者都可以掌握ArkTS语言声明式开发范式,体验更简洁、更友好的HarmonyOS-元服务应用开发旅程。')
            .fontSize(20).margin(20)
        }.height('50%').width('100%')
        .backgroundColor(Color.White)
        // 设置文本内容区背景延伸到导航栏
        .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM])
      }
    }
    .width('100%')
    .height('100%')
    // 关闭Swiper组件默认的裁切效果以便子节点可以绘制在Swiper外。
    .clip(false)
  }
}

总结

至此,我们对布局和沉浸式效果均有了一定的了解,我们是针对应用开发的实战,对于其它的布局方式并没有完整的阐述,有兴趣的可以继续深度挖掘,当前内容完全可以支持一些较为复杂的布局,希望学习完毕后大家主动联系联系,熟悉布局操作后对大家的这个页面操作都会有很大的帮扶作用。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-02-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
[剑指offer] 左旋转字符串
汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!
尾尾部落
2018/09/04
4250
剑指Offer-左旋转字符串
package String; /** * 左旋转字符串 * 汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。 * 对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。 * 例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它! */ public class Solution31 { public static void main(String[] a
武培轩
2018/04/18
5840
剑指Offer(四十三)--左旋转字符串
汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!
秦怀杂货店
2022/02/15
1840
每天一道剑指offer-左旋转字符串
,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。 剑指offer思路,先反转整个字符串,就是fedZYXcba,然后反转前6位XYZdef再反转后三位abc。那么就是XYZdefabc。
乔戈里
2019/01/23
4160
剑指offer——左旋转字符串
题目描述 汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!
AI那点小事
2020/04/18
2870
剑指Offer的学习笔记(C#篇)-- 左旋转字符串
举例子吧。ABC DEF -- -- DEF ABC (变前三位就这样了)
WeiMLing
2019/08/23
3700
Sword To Offer 043 - 左旋转字符串
汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!
Reck Zhang
2021/08/11
1950
左旋转字符串
汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!
名字是乱打的
2022/05/13
1880
剑指43-左旋转字符串
汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!
opencode
2022/12/26
1720
每日一刷《剑指offer》字符串篇之左旋转字符串
汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列  S ,请你把其循环左移 K 位后的序列输出。例如,字符序列 S = ”abcXYZdef” , 要求输出循环左移 3 位后的结果,即 “XYZdefabc”
终有救赎
2023/11/18
1740
每日一刷《剑指offer》字符串篇之左旋转字符串
[PHP] 算法-字符串的左循环的PHP实现
汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZde
唯一Chat
2019/09/10
5390
89 - Python一行代码实现循环移位
汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移k位后的序列输出。例如,字符序列 S = "adcXYZdef", 要求输出循环左移3位后的结果,即 "XYZdefabc" def LeftRotateString(s, k): return s[k:] + s[:k] print(LeftRotateString("abcXYZdef", 3)) XYZdefabc def rightRotateSt
ruochen
2021/06/24
7260
89 - Python一行代码实现循环移位
【剑指Offer】58.2 左旋转字符串
先将 “abc” 和 “XYZdef” 分别翻转,得到 “cbafedZYX”,然后再把整个字符串翻转得到 “XYZdefabc”。
瑞新
2020/12/07
3770
每日算法题:Day 21
输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。 输出描述: 对应每个测试案例,输出两个数,小的先输出。
算法工程师之路
2019/08/23
3300
剑指offer(41-50)题解
既然有了通项公式,那么其实我们也能推出这样一个结论,sum如果在n区间长的连续区间内满足,那么这个n区间长的区间是唯一的,不会存在第二个n长的连续区间满足,从上图我们可以看出。 其次假设刚好区间满足情况,那么区间的元素数是不是只有奇数个和偶数个这两种情况。
萌萌哒的瓤瓤
2020/08/26
4880
剑指offer(41-50)题解
剑指offer 第十天
37.数字在排序数组中出现的次数 统计一个数字在排序数组中出现的次数。 采用二分查找法 /* 方法一:时间复杂度O(n),不可选 */ public class Solution { public int GetNumberOfK(int [] array , int k) { if(array.length == 0) return 0; int count = 0; for(int i = 0 ; i < array.length ; i++)
10JQKA
2018/05/09
5810
刷题:剑指offer第四期(33-44)
33.把只包含因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含因子7。习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
前端迷
2020/02/19
3970
左旋转字符串_43
public String LeftRotateString(String str,int n) { //左移位数确定 int length=str.length(); if (length==n||n==0||length<1){ return str; } n%=length; //保留第n个字符到末尾再加上前n个字符 str+=str; retu
名字是乱打的
2021/12/23
2140
左旋转字符串_43
左旋转字符串
先将 “abc” 和 “XYZdef” 分别翻转,得到 “cbafedZYX”,然后再把整个字符串翻转得到 “XYZdefabc”。
MickyInvQ
2021/12/07
1350
剑指offer(41-53题)题解
思路: 这题几个要求:大于等于两个序列,正整数,连续。至于对list长度返回的要求直接重写个排序接口即可。
bigsai
2020/02/19
4540
相关推荐
[剑指offer] 左旋转字符串
更多 >
LV.1
腾讯算法工程师
目录
  • 栅格布局(GridRow/GridCol)
    • 栅格布局概述
    • 栅格容器GridRow
      • 栅格系统断点
      • 布局的总列数
      • 排列方向
      • 子组件间距
      • 栅格组件的嵌套使用
  • 栅格布局实战——四则计算器
  • 沉浸式效果
    • 窗口全屏布局方案
      • 不隐藏避让区
      • 隐藏避让区
    • 组件安全区方案
      • 扩展安全区域属性原理
    • 背景图和视频场景
    • 图文示例
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档