HarmonyOS Next 中的容器组件——构建灵活页面布局 原创

SameX
发布于 2025-2-25 10:07
1.8w浏览
0收藏

本文旨在深入探讨华为鸿蒙HarmonyOS Next系统的技术细节,基于实际开发实践进行总结。主要作为技术分享与交流载体,难免错漏,欢迎各位同仁提出宝贵意见和问题,以便共同进步。本文为原创内容,任何形式的转载必须注明出处及原作者。

在HarmonyOS Next的开发领域,容器组件堪称构建灵活页面布局的“得力助手”。它们如同不同规格的建筑模块,各自具备独特的功能,帮助开发者将页面元素巧妙组合,呈现出丰富多样且适配各类设备的界面效果。接下来,让我们一同深入了解Row、Column、Flex这些容器组件的奥秘。

容器组件概览

Row组件

Row组件就像是一个水平排列的“收纳盒”,所有子组件在其中从左至右依次排开,如同书架上横向摆放的书籍。它适用于需要将元素水平展示的场景,例如导航栏中的各个选项、一排图标等。使用时,通过设置子组件的相关属性,能够精准控制它们在水平方向上的排列样式、尺寸大小以及相互间距。比如,将justifyContent属性设为FlexAlign.SpaceEvenly,子组件就能在Row中均匀分布。

Column组件

与Row组件相反,Column组件是垂直方向的“收纳容器”,子组件会按照从上到下的顺序依次堆叠,类似多层书架上每层放置一个物品。在实际应用中,当需要将相关元素垂直排列时,Column组件就派上了用场,像列表中的各个列表项、表单里的输入框与标签等场景。通过调整子组件属性,可实现垂直方向上的布局优化。

Flex组件

Flex组件是一个极其灵活的“万能模块”,它既可以像Row组件一样在水平方向布局子组件,也能如Column组件般在垂直方向进行排列,具体方向由direction属性决定 。Flex组件凭借flexGrowflexShrinkflexBasis等属性,能够精细地控制子组件的拉伸、收缩和基准尺寸,在复杂的布局场景中表现卓越。例如在一个图文混排的卡片布局里,Flex组件可轻松实现图片和文字的完美排版。

下面用表格来总结它们的区别与常见使用场景:

容器组件 布局方向 特点 常见使用场景
Row 水平方向 子组件水平排列 导航栏、一排图标、水平菜单
Column 垂直方向 子组件垂直排列 列表、表单、垂直菜单
Flex 可水平或垂直 灵活控制子组件拉伸、收缩和基准尺寸 复杂卡片布局、混合排列元素

如何利用容器组件实现自适应布局

容器组件在实现自适应布局过程中扮演着至关重要的角色。下面通过表格结合示例代码,详细为大家介绍:

自适应布局能力 实现方式 示例代码 说明
拉伸能力 Flex组件的flexGrowflexShrink属性,或使用Blank组件 ```arkts
@Entry
@Component
struct StretchLayout {
    @State widthValue: number = 0.5
    @Builder slider() {
        Slider({ value: this.widthValue * 100, min: 30, max: 80, style: SliderStyle.OutSet })
          .blockColor(Color.White)
          .width('60%')
          .onChange((value: number) => {
                this.widthValue = value / 100
            })
          .position({ x: '20%', y: '80%' })
    }
    build() {
        Column() {
            Row() {
                Text('固定宽度').width(100).height(50).backgroundColor('#FFD700')
                Blank().flexGrow(1)
                Toggle({ type: ToggleType.Switch }).width(36).height(20)
            }
              .width(this.widthValue * 100 + '%')
              .height(50)
              .backgroundColor('#FFFFFF')
            this.slider()
        }
          .width('100%')
          .height('100%')
          .backgroundColor('#F1F3F5')
    }
}
```

|当父容器尺寸变化时,`Blank`组件会依据`flexGrow`属性拉伸,分配剩余空间;若父容器尺寸不足,子组件会根据`flexShrink`属性收缩|
|均分能力|将Row、Column或Flex组件的`justifyContent`属性设置为`FlexAlign.SpaceEvenly`|```arkts
@Entry
@Component
struct EqualLayout {
    @State widthValue: number = 0.6
    @Builder slider() {
        Slider({ value: this.widthValue * 100, min: 30, max: 60, style: SliderStyle.OutSet })
          .blockColor(Color.White)
          .width('60%')
          .onChange((value: number) => {
                this.widthValue = value / 100
            })
          .position({ x: '20%', y: '80%' })
    }
    build() {
        Column() {
            Row() {
                ForEach([1, 2, 3], (item) => {
                    Column() {
                        Image($r('app.media.icon')).width(48).height(48)
                        Text('选项' + item).width(64).height(30).fontSize(12).textAlign(TextAlign.Center)
                    }
                      .width(80)
                      .height(102)
                      .flexShrink(1)
                })
            }
              .width(this.widthValue * 100 + '%')
              .justifyContent(FlexAlign.SpaceEvenly)
            this.slider()
        }
          .width('100%')
          .height('100%')
          .backgroundColor('#F1F3F5')
    }
}
```|子组件在父容器主轴方向等间距布局,相邻元素之间、第一个元素与行首、最后一个元素到行尾的间距都相同|
|占比能力|子组件宽高设为父组件宽高的百分比,或使用`layoutWeight`属性(仅在父容器为Row、Column或Flex时生效)|```arkts
@Entry
@Component
struct ProportionLayout {
    @State widthValue: number = 0.5
    @Builder slider() {
        Slider({ value: this.widthValue * 100, min: 30, max: 70, style: SliderStyle.OutSet })
          .blockColor(Color.White)
          .width('60%')
          .onChange((value: number) => {
                this.widthValue = value / 100
            })
          .position({ x: '20%', y: '80%' })
    }
    build() {
        Column() {
            Row() {
                Column() {
                    Text('占比1').width('30%').height(50).backgroundColor('#FFD700')
                }
                Column() {
                    Text('占比2').width('40%').height(50).backgroundColor('#ADD8E6')
                }
                Column() {
                    Text('占比3').width('30%').height(50).backgroundColor('#90EE90')
                }
            }
              .width(this.widthValue * 100 + '%')
              .height(50)
              .backgroundColor('#FFFFFF')
            this.slider()
        }
          .width('100%')
          .height('100%')
          .backgroundColor('#F1F3F5')
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.

|子组件按设定比例占据父容器空间,可通过百分比或layoutWeight属性实现|

响应式布局中的容器组合技巧

在响应式布局的实际应用中,巧妙组合Row、Column和Flex组件,能够创造出各式各样的布局效果。以一个新闻应用的页面布局为例:

@Entry
@Component
struct ResponsiveLayout {
    @State currentBreakpoint: string ='sm'
    build() {
        GridRow({ breakpoints: { value: ['600vp'], reference: BreakpointsReference.WindowSize } }) {
            GridCol({ span: { sm: 12, md: 8 } }) {
                Column() {
                    // 头条新闻区域,使用Flex组件实现图片和文字混合排版
                    Flex({ direction: FlexDirection.Row }) {
                        Image($r('app.media.headlineImg')).width(120).height(80).objectFit(ImageFit.Contain)
                        Column() {
                            Text('头条新闻标题').fontSize(18).fontWeight(500).width('100%')
                            Text('简短描述...').fontSize(14).opacity(0.6).width('100%')
                        }
                          .flexGrow(1)
                          .paddingStart(10)
                    }
                    // 其他新闻列表,使用Row和Column组件实现多行多列布局
                    ForEach([1, 2, 3], (item) => {
                        Row() {
                            Column() {
                                Image($r('app.media.newsImg')).width(60).height(60).objectFit(ImageFit.Contain)
                            }
                              .width(60)
                              .height(60)
                            Column() {
                                Text('新闻标题' + item).fontSize(16).width('100%')
                                Text('简要内容...').fontSize(12).opacity(0.6).width('100%')
                            }
                              .flexGrow(1)
                              .paddingStart(10)
                        }
                          .paddingVertical(10)
                    })
                }
            }
            GridCol({ span: { sm: 12, md: 4 } }) {
                // 热门推荐区域,使用Column组件垂直排列推荐内容
                Column() {
                    ForEach([1, 2, 3], (item) => {
                        Text('热门推荐' + item).fontSize(16).width('100%').paddingVertical(10)
                    })
                }
            }
        }
          .onBreakpointChange((breakpoint: string) => {
                this.currentBreakpoint = breakpoint
            })
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.

在小屏幕(sm断点)状态下,GridCol组件会占据全部宽度,头条新闻和其他新闻列表呈垂直排列;而在大屏幕(md断点)下,头条新闻和其他新闻列表位于左侧,占据8列空间,右侧4列则用于展示热门推荐内容。通过这种容器组件的巧妙组合,成功实现了页面在不同断点下的合理布局与自适应调整。

HarmonyOS Next中的Row、Column和Flex容器组件为开发者提供了强大的布局功能。无论是打造自适应布局,还是实现响应式布局,深入理解并灵活运用这些容器组件,都是构建优秀应用界面的关键所在。希望通过本文的分享,大家在实际开发中能够更加熟练地运用它们,创造出更出色的应用界面。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
分类
标签
收藏
回复
举报


回复
    相关推荐