文档管理中心
您当前正在浏览HarmonyOS最新文档,覆盖已发布的所有API版本,可在API参考中筛选您使用的API版本。详细的版本配套关系请参考版本说明
API参考应用框架ArkUI(方舟UI框架)ArkTS组件行列与堆叠DynamicLayout

DynamicLayout

动态布局容器组件,支持在运行时动态切换不同的布局算法,不改变子组件的状态。使用DynamicLayout可以提升布局灵活性,简化界面适配和多视图切换的开发流程。适用于响应式布局(适配不同屏幕尺寸)、多视图模式切换(如列表/网格/瀑布流切换)、用户自定义布局等场景。

说明
  • 该组件从API version 24开始支持。后续版本的新增接口,采用上角标单独标记接口的起始版本。

  • 本模块接口仅可在Stage模型下使用。

子组件

PhonePC/2in1TabletTVWearable

可以包含子组件。

接口

PhonePC/2in1TabletTVWearable

DynamicLayout(algorithm: LayoutAlgorithm)

动态布局容器。

卡片能力: 从API version 24开始,该接口支持在ArkTS卡片中使用。

元服务API: 从API version 24开始,该接口支持在元服务中使用。

模型约束: 此接口仅可在Stage模型下使用。

系统能力: SystemCapability.ArkUI.ArkUI.Full

参数:

展开
参数名 类型 必填 说明
algorithm LayoutAlgorithm 指定动态布局容器的布局算法。支持使用RowLayoutAlgorithm(水平线性布局,适用于水平排列场景)、ColumnLayoutAlgorithm(垂直线性布局,适用于垂直排列场景)、StackLayoutAlgorithm(堆叠布局,适用于层叠覆盖场景)、GridLayoutAlgorithm(网格布局,适用于规整网格场景)和CustomLayoutAlgorithm(自定义布局,适用于复杂特殊布局场景)等布局算法实例,详见LayoutAlgorithm。取非法值(如null、undefined或无效的布局算法对象)时,按照StackLayoutAlgorithm布局子组件,子组件堆叠排列。

属性

PhonePC/2in1TabletTVWearable

支持通用属性

说明

事件

PhonePC/2in1TabletTVWearable

支持通用事件

示例

PhonePC/2in1TabletTVWearable

示例1(自定义布局算法实现瀑布流布局)

该示例展示如何重写onMeasureonLayout函数,实现瀑布流布局展示商品列表的功能。瀑布流布局通过测量阶段计算子组件高度并记录每列累计高度,在布局阶段将子组件分配到当前高度最小的列,实现自动填充效果。

从API version 24开始,新增onMeasure、onLayout。

收起
自动换行
深色代码主题
复制
  1. import { DynamicLayout, DynamicLayoutAttribute, CustomLayoutAlgorithm, LayoutAlgorithm, FrameNode, LayoutConstraint, Position } from '@kit.ArkUI';
  2. // 瀑布流布局算法
  3. class WaterfallLayout extends CustomLayoutAlgorithm {
  4. private columnCount: number = 2;
  5. private columnGap: number = 10;
  6. private rowGap: number = 10;
  7. onMeasure(self: FrameNode, constraint: LayoutConstraint): void {
  8. const childCount = self.getChildrenCount();
  9. const columnWidth = (constraint.maxSize.width - (this.columnCount - 1) * this.columnGap) / this.columnCount;
  10. // 记录每列的当前高度
  11. const columnHeights: number[] = new Array(this.columnCount).fill(0);
  12. for (let i = 0; i < childCount; i++) {
  13. const child = self.getChild(i);
  14. if (child) {
  15. // 通过将minSize和maxSize设置为相同值来约束子组件宽度
  16. const childConstraint: LayoutConstraint = {
  17. maxSize: {
  18. width: columnWidth,
  19. height: constraint.maxSize.height
  20. },
  21. minSize: {
  22. width: columnWidth,
  23. height: 0
  24. },
  25. percentReference: constraint.percentReference
  26. };
  27. child.measure(childConstraint);
  28. // 找到当前高度最小的列
  29. const minColumn = columnHeights.indexOf(Math.min(...columnHeights));
  30. columnHeights[minColumn] += child.getMeasuredSize().height + this.rowGap;
  31. }
  32. }
  33. const maxHeight = Math.max(...columnHeights);
  34. self.setMeasuredSize({
  35. width: constraint.maxSize.width,
  36. height: maxHeight
  37. });
  38. }
  39. onLayout(self: FrameNode, position: Position): void {
  40. const childCount = self.getChildrenCount();
  41. const measuredSize = self.getMeasuredSize();
  42. const columnWidth = (measuredSize.width - (this.columnCount - 1) * this.columnGap) / this.columnCount;
  43. // 记录每列的当前Y坐标
  44. const columnYs: number[] = new Array(this.columnCount).fill(0);
  45. for (let i = 0; i < childCount; i++) {
  46. const child = self.getChild(i);
  47. if (child) {
  48. const childSize = child.getMeasuredSize();
  49. // 找到当前Y坐标最小的列
  50. const minColumn = columnYs.indexOf(Math.min(...columnYs));
  51. const x = minColumn * (columnWidth + this.columnGap);
  52. const y = columnYs[minColumn];
  53. child.layout({ x, y });
  54. columnYs[minColumn] += childSize.height + this.rowGap;
  55. }
  56. }
  57. self.setLayoutPosition(position);
  58. }
  59. }
  60. @Entry
  61. @ComponentV2
  62. struct WaterfallLayoutExample {
  63. @Local algorithm: LayoutAlgorithm = new WaterfallLayout();
  64. // 商品数据
  65. private products: Product[] = [
  66. { id: '1', name: '时尚运动鞋', price: '¥399', height: 180, image: '商品图' },
  67. { id: '2', name: '休闲双肩包', price: '¥259', height: 220, image: '商品图' },
  68. { id: '3', name: '无线蓝牙耳机', price: '¥599', height: 150, image: '商品图' },
  69. { id: '4', name: '智能手表', price: '¥1299', height: 200, image: '商品图' },
  70. { id: '5', name: '太阳眼镜', price: '¥199', height: 130, image: '商品图' },
  71. { id: '6', name: '便携充电宝', price: '¥129', height: 170, image: '商品图' },
  72. { id: '7', name: '机械键盘', price: '¥459', height: 160, image: '商品图' },
  73. { id: '8', name: '游戏鼠标', price: '¥189', height: 140, image: '商品图' },
  74. { id: '9', name: '高清显示器', price: '¥1599', height: 210, image: '商品图' },
  75. { id: '10', name: '智能音箱', price: '¥299', height: 190, image: '商品图' }
  76. ];
  77. // 商品卡片组件
  78. @Builder ProductCard(product: Product) {
  79. Column() {
  80. Text(product.image)
  81. .fontSize(18)
  82. .margin({ bottom: 8 })
  83. Text(product.name)
  84. .fontSize(14)
  85. .fontWeight(FontWeight.Medium)
  86. .fontColor(0x333333)
  87. .margin({ bottom: 4 })
  88. .maxLines(1)
  89. .textOverflow({ overflow: TextOverflow.Ellipsis })
  90. Text(product.price)
  91. .fontSize(16)
  92. .fontColor(0xFF6B35)
  93. .fontWeight(FontWeight.Bold)
  94. }
  95. .width('100%')
  96. .padding(12)
  97. .backgroundColor(0xFAFAFA)
  98. .borderRadius(8)
  99. .border({ width: 1, color: 0xE0E0E0 })
  100. .height(product.height)
  101. .justifyContent(FlexAlign.Center)
  102. }
  103. build() {
  104. Column() {
  105. Text('商品列表 - 瀑布流布局')
  106. .fontSize(18)
  107. .fontWeight(FontWeight.Bold)
  108. .margin({ bottom: 20 })
  109. Scroll() {
  110. DynamicLayout(this.algorithm) {
  111. ForEach(this.products, (product: Product) => {
  112. this.ProductCard(product)
  113. })
  114. }
  115. .width('100%')
  116. .backgroundColor(0xEFEFEF)
  117. .borderRadius(12)
  118. .padding(10)
  119. }
  120. .scrollable(ScrollDirection.Vertical)
  121. .scrollBar(BarState.Auto)
  122. .edgeEffect(EdgeEffect.Spring)
  123. .width('100%')
  124. .layoutWeight(1)
  125. Text('商品卡片自动分配到高度最小的列')
  126. .fontSize(14)
  127. .fontColor(Color.Gray)
  128. .margin({ top: 12 })
  129. }
  130. .padding(20)
  131. .width('100%')
  132. .height('100%')
  133. }
  134. }
  135. // 商品数据模型
  136. interface Product {
  137. id: string;
  138. name: string;
  139. price: string;
  140. height: number;
  141. image: string;
  142. }

示例2(切换布局算法)

该示例通过改变@Local装饰的LayoutAlgorithm类型变量,实现动态切换DynamicLayout组件布局算法的功能。示例展示如何切换布局算法为RowLayoutAlgorithm(水平线性布局)、ColumnLayoutAlgorithm(垂直线性布局)、StackLayoutAlgorithm(堆叠布局)和GridLayoutAlgorithm(网格布局)。

说明

示例中预置的layoutGravity属性仅在Stack布局算法下生效,在Row/Column布局算法下该属性不生效。

从API version 24开始,新增RowLayoutAlgorithm、ColumnLayoutAlgorithm、StackLayoutAlgorithm、GridLayoutAlgorithm。

收起
自动换行
深色代码主题
复制
  1. import { DynamicLayout, DynamicLayoutAttribute, RowLayoutAlgorithm, ColumnLayoutAlgorithm, StackLayoutAlgorithm, GridLayoutAlgorithm, LayoutAlgorithm, LengthMetrics } from '@kit.ArkUI';
  2. @Entry
  3. @ComponentV2
  4. struct LayoutSwitchExample {
  5. @Local algorithm: LayoutAlgorithm = new RowLayoutAlgorithm({
  6. space: LengthMetrics.vp(10),
  7. alignItems: VerticalAlign.Center
  8. });
  9. @Local childWidth: string = '20%'
  10. @Local childHeight: string = '20%'
  11. build() {
  12. Column() {
  13. // 使用状态变量控制布局算法
  14. DynamicLayout(this.algorithm) {
  15. Text('Item 1')
  16. .width(this.childWidth)
  17. .height(this.childHeight)
  18. .fontSize(14)
  19. .textAlign(TextAlign.Center)
  20. .backgroundColor(0xF5DEB3)
  21. .borderRadius(8)
  22. .layoutGravity(LocalizedAlignment.TOP_START)
  23. Text('Item 2')
  24. .width(this.childWidth)
  25. .height(this.childHeight)
  26. .fontSize(14)
  27. .textAlign(TextAlign.Center)
  28. .backgroundColor(0xF5DEB3)
  29. .borderRadius(8)
  30. .layoutGravity(LocalizedAlignment.TOP_END)
  31. Text('Item 3')
  32. .width(this.childWidth)
  33. .height(this.childHeight)
  34. .fontSize(14)
  35. .textAlign(TextAlign.Center)
  36. .backgroundColor(0xF5DEB3)
  37. .borderRadius(8)
  38. .layoutGravity(LocalizedAlignment.BOTTOM_START)
  39. Text('Item 4')
  40. .width(this.childWidth)
  41. .height(this.childHeight)
  42. .fontSize(14)
  43. .textAlign(TextAlign.Center)
  44. .backgroundColor(0xF5DEB3)
  45. .borderRadius(8)
  46. .layoutGravity(LocalizedAlignment.BOTTOM_END)
  47. }
  48. .width(300)
  49. .height(280)
  50. .backgroundColor(0xEFEFEF)
  51. .borderRadius(12)
  52. .padding(10)
  53. Column({ space: 10 }) {
  54. Row({ space: 10 }) {
  55. Button('Row布局')
  56. .fontSize(14)
  57. .onClick(() => {
  58. this.algorithm = new RowLayoutAlgorithm({
  59. space: LengthMetrics.vp(10),
  60. alignItems: VerticalAlign.Center
  61. });
  62. this.childWidth = '20%'
  63. this.childHeight = '20%'
  64. })
  65. Button('Column布局')
  66. .fontSize(14)
  67. .onClick(() => {
  68. this.algorithm = new ColumnLayoutAlgorithm({
  69. space: LengthMetrics.vp(10),
  70. alignItems: HorizontalAlign.Center
  71. });
  72. this.childWidth = '20%'
  73. this.childHeight = '20%'
  74. })
  75. }
  76. Row({ space: 10 }) {
  77. Button('Stack布局')
  78. .fontSize(14)
  79. .onClick(() => {
  80. this.algorithm = new StackLayoutAlgorithm({
  81. alignContent: LocalizedAlignment.CENTER
  82. });
  83. this.childWidth = '20%'
  84. this.childHeight = '20%'
  85. })
  86. Button('Grid布局')
  87. .fontSize(14)
  88. .onClick(() => {
  89. this.algorithm = new GridLayoutAlgorithm({
  90. columnsTemplate: '1fr 1fr',
  91. rowsGap: LengthMetrics.vp(5),
  92. columnsGap: LengthMetrics.vp(5)
  93. });
  94. this.childWidth = '100%'
  95. this.childHeight = '50%'
  96. })
  97. }
  98. }
  99. .margin({ top: 20 })
  100. }
  101. .padding(20)
  102. }
  103. }

示例3(修改布局算法属性)

该示例通过修改RowLayoutAlgorithm的space和justifyContent属性,实现DynamicLayout组件布局效果刷新的功能。

从API version 24开始,新增space、justifyContent属性。

收起
自动换行
深色代码主题
复制
  1. import { DynamicLayout, DynamicLayoutAttribute, RowLayoutAlgorithm, LengthMetrics } from '@kit.ArkUI';
  2. @Entry
  3. @ComponentV2
  4. struct PropertyChangeExample {
  5. algorithm: RowLayoutAlgorithm = new RowLayoutAlgorithm({
  6. space: LengthMetrics.vp(10),
  7. justifyContent: FlexAlign.Start
  8. });
  9. build() {
  10. Column() {
  11. DynamicLayout(this.algorithm) {
  12. Text('Item 1')
  13. .width(60)
  14. .height(40)
  15. .fontSize(14)
  16. .backgroundColor(0xF5DEB3)
  17. Text('Item 2')
  18. .width(60)
  19. .height(40)
  20. .fontSize(14)
  21. .backgroundColor(0xD2B48C)
  22. Text('Item 3')
  23. .width(60)
  24. .height(40)
  25. .fontSize(14)
  26. .backgroundColor(0xF5DEB3)
  27. }
  28. .width('100%')
  29. .height(80)
  30. .backgroundColor(0xEFEFEF)
  31. Row({ space: 10 }) {
  32. Button('增加间距')
  33. .fontSize(14)
  34. .onClick(() => {
  35. // 修改space属性触发重新布局
  36. const currentSpace = this.algorithm.space?.value;
  37. this.algorithm.space = LengthMetrics.vp(currentSpace as number + 5);
  38. })
  39. Button('居中对齐')
  40. .fontSize(14)
  41. .onClick(() => {
  42. // 修改justifyContent属性触发重新布局
  43. this.algorithm.justifyContent = FlexAlign.Center;
  44. })
  45. Button('两端对齐')
  46. .fontSize(14)
  47. .onClick(() => {
  48. // 修改justifyContent属性为两端对齐
  49. this.algorithm.justifyContent = FlexAlign.SpaceBetween;
  50. })
  51. }
  52. .margin({ top: 20 })
  53. }
  54. .padding(20)
  55. }
  56. }

在 API参考 中进行搜索
请输入您想要搜索的关键词