1# 自定义组件的自定义布局
2
3自定义组件的自定义布局用于通过数据计算的方式布局自定义组件内的子组件。
4
5> **说明:**
6>
7> 本模块首批接口从API version 9开始支持,后续版本的新增接口,采用上角标单独标记接口的起始版本。
8
9## onPlaceChildren<sup>10+</sup>
10
11onPlaceChildren?(selfLayoutInfo: GeometryInfo, children: Array&lt;Layoutable&gt;, constraint: ConstraintSizeOptions):void
12
13ArkUI框架会在自定义组件布局时,将该自定义组件的子节点自身的尺寸范围通过onPlaceChildren传递给该自定义组件。不允许在onPlaceChildren函数中改变状态变量。
14
15**系统能力:** SystemCapability.ArkUI.ArkUI.Full
16
17**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
18
19**参数:**
20
21| 参数名            | 类型                                                         | 说明               |
22|----------------|------------------------------------------------------------|------------------|
23| selfLayoutInfo | [GeometryInfo](#geometryinfo10)                            | 父组件布局信息。         |
24| children       | Array&lt;[Layoutable](#layoutable10)&gt;                   | 子组件布局信息。         |
25| constraint     | [ConstraintSizeOptions](ts-types.md#constraintsizeoptions) | 父组件constraint信息。 |
26
27**示例:**
28
29示例请参考[自定义布局代码示例](#onmeasuresize10)。
30
31## onMeasureSize<sup>10+</sup>
32
33onMeasureSize?(selfLayoutInfo: GeometryInfo, children: Array&lt;Measurable&gt;, constraint: ConstraintSizeOptions): SizeResult
34
35ArkUI框架会在自定义组件确定尺寸时,将该自定义组件的节点信息和尺寸范围通过onMeasureSize传递给该开发者。不允许在onMeasureSize函数中改变状态变量。
36
37**系统能力:** SystemCapability.ArkUI.ArkUI.Full
38
39**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
40
41**参数:**
42
43| 参数名         | 类型                                                       | 说明                                                         |
44| -------------- | ---------------------------------------------------------- | ------------------------------------------------------------ |
45| selfLayoutInfo | [GeometryInfo](#geometryinfo10)                            | 父组件布局信息。                                             |
46| children       | Array&lt;[Measurable](#measurable10)&gt;                   | 子组件布局信息。<br/>**说明:** <br/>如果没有设置子组件的布局信息,子组件会维持上一次的布局信息,当子组件从来没有设置过尺寸时,尺寸默认为0。 |
47| constraint     | [ConstraintSizeOptions](ts-types.md#constraintsizeoptions) | 父组件constraint信息。                                       |
48
49**返回值:**
50
51| 类型                        | 描述           |
52| --------------------------- | -------------- |
53| [SizeResult](#sizeresult10) | 组件尺寸信息。 |
54
55**示例一:**
56自定义布局代码示例。
57```
58// xxx.ets
59@Entry
60@Component
61struct Index {
62  build() {
63    Column() {
64      CustomLayout({ builder: ColumnChildren })
65    }
66  }
67}
68
69@Builder
70function ColumnChildren() {
71  ForEach([1, 2, 3], (index: number) => { //暂不支持lazyForEach的写法
72    Text('S' + index)
73      .fontSize(30)
74      .width(100)
75      .height(100)
76      .borderWidth(2)
77      .offset({ x: 10, y: 20 })
78  })
79}
80
81@Component
82struct CustomLayout {
83  @Builder
84  doNothingBuilder() {
85  };
86
87  @BuilderParam builder: () => void = this.doNothingBuilder;
88  @State startSize: number = 100;
89  result: SizeResult = {
90    width: 0,
91    height: 0
92  };
93
94  onPlaceChildren(selfLayoutInfo: GeometryInfo, children: Array<Layoutable>, constraint: ConstraintSizeOptions) {
95    let startPos = 300;
96    children.forEach((child) => {
97      let pos = startPos - child.measureResult.height;
98      child.layout({ x: pos, y: pos })
99    })
100  }
101
102  onMeasureSize(selfLayoutInfo: GeometryInfo, children: Array<Measurable>, constraint: ConstraintSizeOptions) {
103    let size = 100;
104    children.forEach((child) => {
105      let result: MeasureResult = child.measure({ minHeight: size, minWidth: size, maxWidth: size, maxHeight: size })
106      size += result.width / 2
107      ;
108    })
109    this.result.width = 100;
110    this.result.height = 400;
111    return this.result;
112  }
113
114  build() {
115    this.builder()
116  }
117}
118```
119
120![custom_layout10.png](figures/custom_layout10.png)
121
122**示例二:**
123通过组件的位置灵活判断是否参与布局计算。
124```
125// xxx.ets
126@Entry
127@Component
128struct Index {
129  build() {
130    Column() {
131      CustomLayout({ builder: ColumnChildren })
132    }
133    .justifyContent(FlexAlign.Center)
134    .width("100%")
135    .height("100%")
136  }
137}
138
139@Builder
140function ColumnChildren() {
141  ForEach([1, 2, 3], (item: number, index: number) => { //暂不支持lazyForEach的写法
142    Text('S' + item)
143      .fontSize(20)
144      .width(60 + 10 * index)
145      .height(100)
146      .borderWidth(2)
147      .margin({ left:10 })
148      .padding(10)
149  })
150}
151
152@Component
153struct CustomLayout {
154  // 只布局一行,如果布局空间不够的子组件不显示的demo
155  @Builder
156  doNothingBuilder() {
157  };
158
159  @BuilderParam builder: () => void = this.doNothingBuilder;
160  result: SizeResult = {
161    width: 0,
162    height: 0
163  };
164  overFlowIndex: number = -1;
165
166  onPlaceChildren(selfLayoutInfo: GeometryInfo, children: Array<Layoutable>, constraint: ConstraintSizeOptions) {
167    let currentX = 0;
168    let infinity = 100000;
169    if (this.overFlowIndex == -1) {
170      this.overFlowIndex = children.length;
171    }
172    for (let index = 0; index < children.length; ++index) {
173      let child = children[index];
174      if (index >= this.overFlowIndex) {
175        // 如果子组件超出父组件范围,将它布局到较偏的位置,达到不显示的目的
176        child.layout({x: infinity, y: 0});
177        continue;
178      }
179      child.layout({ x: currentX, y: 0 })
180      let margin = child.getMargin();
181      currentX += child.measureResult.width + margin.start + margin.end;
182    }
183  }
184
185  onMeasureSize(selfLayoutInfo: GeometryInfo, children: Array<Measurable>, constraint: ConstraintSizeOptions) {
186    let width = 0;
187    let height = 0;
188    this.overFlowIndex = -1;
189    // 假定该组件的宽度不能超过200vp,也不能超过最大约束
190    let maxWidth = Math.min(200, constraint.maxWidth as number);
191    for (let index = 0; index < children.length; ++index) {
192      let child = children[index];
193      let childResult: MeasureResult = child.measure({
194          minHeight: constraint.minHeight,
195          minWidth: constraint.minWidth,
196          maxWidth: constraint.maxWidth,
197          maxHeight: constraint.maxHeight
198      })
199      let margin = child.getMargin();
200      let newWidth = width + childResult.width + margin.start + margin.end;
201      if (newWidth > maxWidth) {
202        // 记录不该布局的组件的下标
203        this.overFlowIndex = index;
204        break;
205      }
206      // 累积父组件的宽度和高度
207      width = newWidth;
208      height = Math.max(height, childResult.height + margin.top + margin.bottom);
209    }
210    this.result.width = width;
211    this.result.height = height;
212    return this.result;
213  }
214
215  build() {
216    this.builder()
217  }
218}
219```
220
221![custom_layout_demo2.png](figures/custom_layout_demo2.png)
222
223## GeometryInfo<sup>10+</sup>
224
225父组件布局信息,继承自[SizeResult](#sizeresult10)。
226
227**系统能力:** SystemCapability.ArkUI.ArkUI.Full
228
229**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
230
231| 属性          | 类型      | 说明                  |
232|-------------|-----------|---------------------|
233| borderWidth | [EdgeWidth](ts-types.md#edgewidths9) | 父组件边框宽度。<br>单位:vp            |
234| margin      | [Margin](ts-types.md#margin)       | 父组件margin信息。 <br>单位:vp       |
235| padding     | [Padding](ts-types.md#padding)   | 父组件padding信息。<br>单位:vp |
236| width  | number | 测量后的宽。<br>单位:vp<br> **说明:** <br>若值为空时,则返回组件的百分比宽。 |
237| height | number | 测量后的高。<br>单位:vp<br> **说明:** <br>若值为空时,则返回组件的百分比高。 |
238
239
240## Layoutable<sup>10+</sup>
241
242子组件布局信息。
243
244**系统能力:** SystemCapability.ArkUI.ArkUI.Full
245
246### 属性
247
248| 属性         | 类型       | 必填      |  说明                                                      |
249|--------------|---------------------------------- | -----------------------------------------------|---------------------|
250| measureResult| [MeasureResult](#measureresult10)      |   是| 子组件测量后的尺寸信息,继承自[SizeResult](#sizeresult10)<br/>**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。<br>单位:vp     |
251
252
253### layout
254
255layout(position: Position)
256
257调用此方法对子组件的位置信息进行限制。
258
259**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
260
261**系统能力:** SystemCapability.ArkUI.ArkUI.Full
262
263**参数:**
264
265| 参数名         | 类型                                                    | 必填                 |说明         |
266|-----------------|---------------------------------------------------------|---------------------|-------------|
267|   position      | [Position](ts-types.md#position)                        | 是                  |   位置。   |
268
269### getMargin<sup>12+</sup>
270
271getMargin() : DirectionalEdgesT\<number>
272
273调用此方法获得子组件的margin信息。
274
275**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
276
277**返回值:**
278
279| 类型                          | 说明                                        |
280|------------------------------------|---------------------------------------------|
281| [DirectionalEdgesT&lt;number&gt;](#directionaledgestt12)  |  子组件的margin信息。   |
282
283 ### getPadding<sup>12+</sup>
284
285getPadding() : DirectionalEdgesT\<number>
286
287 调用此方法获得子组件的padding信息。
288
289**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
290
291 **返回值:**
292
293| 类型                          | 说明                                        |
294|------------------------------------|---------------------------------------------|
295| [DirectionalEdgesT&lt;number&gt;](#directionaledgestt12)  |  子组件的padding信息。  |
296
297### getBorderWidth<sup>12+</sup>
298
299getBorderWidth() : DirectionalEdgesT\<number>
300
301调用此方法获得子组件的borderWidth信息。
302
303**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
304
305**返回值:**
306
307| 类型                          | 说明                                        |
308|------------------------------------|---------------------------------------------|
309| [DirectionalEdgesT&lt;number&gt;](#directionaledgestt12)  |  子组件的borderWidth信息。  |
310
311## Measurable<sup>10+</sup>
312
313子组件位置信息。
314
315**系统能力:** SystemCapability.ArkUI.ArkUI.Full
316
317**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
318
319### measure
320
321 measure(constraint: ConstraintSizeOptions) : MeasureResult
322
323 调用此方法对子组件的尺寸范围进行限制。
324
325 **系统能力:** SystemCapability.ArkUI.ArkUI.Full
326
327**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
328
329**参数:**
330
331| 参数名         | 类型                                                    | 必填                 |说明         |
332|-----------------|---------------------------------------------------------|---------------------|-------------|
333|   constraint    | [ConstraintSizeOptions](ts-types.md#constraintsizeoptions)  | 是            |   约束尺寸。  |
334
335**返回值:**
336
337 | 类型                               | 说明                     |
338 |------------------------------------|-------------------------|
339 |[MeasureResult](#measureresult10)   | 测量后的组件布局信息。   |
340
341 ### getMargin<sup>12+</sup>
342
343 getMargin() : DirectionalEdgesT\<number\>
344
345 获取子组件的margin信息。
346
347**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
348
349**系统能力:** SystemCapability.ArkUI.ArkUI.Full
350
351**返回值:**
352
353 | 类型                               | 说明                     |
354 |------------------------------------|-------------------------|
355 |[DirectionalEdgesT&lt;number&gt;](#directionaledgestt12)  | 子组件的margin信息。   |
356
357### getPadding<sup>12+</sup>
358
359getPadding() : DirectionalEdgesT\<number\>
360
361获取子组件的padding信息。
362
363**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
364
365**系统能力:** SystemCapability.ArkUI.ArkUI.Full
366
367**返回值:**
368
369 | 类型                               | 说明                     |
370 |------------------------------------|-------------------------|
371 |[DirectionalEdgesT&lt;number&gt;](#directionaledgestt12)  | 子组件的padding信息。   |
372
373 ### getBorderWidth<sup>12+</sup>
374
375getBorderWidth() : DirectionalEdgesT\<number\>
376
377获取子组件的borderWidth信息。
378
379**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
380
381**系统能力:** SystemCapability.ArkUI.ArkUI.Full
382
383**返回值:**
384
385 | 类型                               | 说明                     |
386 |------------------------------------|-------------------------|
387 |[DirectionalEdgesT&lt;number&gt;](#directionaledgestt12)  | 子组件的borderWidth信息。|
388
389
390## MeasureResult<sup>10+</sup>
391
392测量后的组件布局信息。
393
394**系统能力:** SystemCapability.ArkUI.ArkUI.Full
395
396**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
397
398| 属性     | 类型   | 说明    |
399|--------|--------|-------|
400| width  | number | 测量后的宽。<br>单位:vp |
401| height | number | 测量后的高。<br>单位:vp |
402
403
404## SizeResult<sup>10+</sup>
405
406组件尺寸信息。
407
408**系统能力:** SystemCapability.ArkUI.ArkUI.Full
409
410**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
411
412| 属性     | 类型   | 说明    |
413|--------|--------|-------|
414| width  | number | 测量后的宽。<br>单位:vp |
415| height | number | 测量后的高。<br>单位:vp |
416
417## DirectionalEdgesT\<T><sup>12+</sup>
418
419全球化的边缘属性。
420
421**系统能力:** SystemCapability.ArkUI.ArkUI.Full
422
423**卡片能力:** 从API version 12开始,该接口支持在ArkTS卡片中使用。
424
425| 属性   | 类型 | 说明             |
426| ------ | ---- | ---------------- |
427| start   | T    | 起始边缘的属性。在LTR的方向下,为左边缘,在RTL的方向下,为右边缘。 |
428| end    | T    | 终止边缘的属性。在LTR的方向下,为右边缘,在RTL的方向下,为左边缘。 |
429| top  | T    | 顶部边缘的属性。 |
430| bottom | T    | 底部边缘的属性。 |
431
432> **说明:**
433>
434>- 自定义布局暂不支持LazyForEach写法。
435>- 使用builder形式的自定义布局创建,自定义组件的build()方法内只允许存在this.builder(),即示例的推荐用法。
436>- 父容器(自定义组件)上设置的尺寸信息,除aspectRatio之外,优先级小于onMeasureSize设置的尺寸信息。
437>- 子组件设置的位置信息,offset、position、markAnchor优先级大于onPlaceChildren设置的位置信息,其他位置设置属性不生效。
438>- 使用自定义布局方法时,需要同时调用onMeasureSize和onPlaceChildren方法,否则可能出现布局异常。
439
440## onLayout<sup>(deprecated)</sup>
441
442onLayout?(children: Array&lt;LayoutChild&gt;, constraint: ConstraintSizeOptions): void
443
444ArkUI框架会在自定义组件布局时,将该自定义组件的子节点信息和自身的尺寸范围通过onLayout传递给该自定义组件。不允许在onLayout函数中改变状态变量。
445
446该接口从API version 9开始支持,从API version 10开始废弃,推荐使用[onPlaceChildren](#onplacechildren10)替代。
447
448**参数:**
449
450| 参数名        | 类型                                                         | 说明               |
451|------------|------------------------------------------------------------|------------------|
452| children   | Array&lt;[LayoutChild](#layoutchilddeprecated)&gt;                  | 子组件布局信息。         |
453| constraint | [ConstraintSizeOptions](ts-types.md#constraintsizeoptions) | 父组件constraint信息。 |
454
455## onMeasure<sup>(deprecated)</sup>
456
457onMeasure?(children: Array&lt;LayoutChild&gt;, constraint: ConstraintSizeOptions): void
458
459ArkUI框架会在自定义组件确定尺寸时,将该自定义组件的子节点信息和自身的尺寸范围通过onMeasure传递给该自定义组件。不允许在onMeasure函数中改变状态变量。
460
461该接口从API version 9开始支持,从API version 10开始废弃,推荐使用[onMeasureSize](#onmeasuresize10)替代。
462
463**参数:**
464
465| 参数名        | 类型                                                         | 说明               |
466|------------|------------------------------------------------------------|------------------|
467| children   | Array&lt;[LayoutChild](#layoutchilddeprecated)&gt;                  | 子组件布局信息。         |
468| constraint | [ConstraintSizeOptions](ts-types.md#constraintsizeoptions) | 父组件constraint信息。 |
469
470## LayoutChild<sup>(deprecated)</sup>
471
472子组件布局信息。
473
474从API version 9开始,从API version 10开始废弃,该接口支持在ArkTS卡片中使用。
475
476
477| 属性       | 类型                                                     | 说明                                   |
478| ---------- | ------------------------------------------------------------ | -------------------------------------- |
479| name       | string                                                       | 子组件名称。                           |
480| id         | string                                                       | 子组件id。                             |
481| constraint | [ConstraintSizeOptions](ts-types.md#constraintsizeoptions)   | 子组件约束尺寸。                       |
482| borderInfo | [LayoutBorderInfo](#layoutborderinfodeprecated)              | 子组件border信息。                     |
483| position   | [Position](ts-types.md#position)                             | 子组件位置坐标。                       |
484| measure    | (childConstraint: [ConstraintSizeOptions](ts-types.md#constraintsizeoptions))&nbsp;=&gt;&nbsp;void | 调用此方法对子组件的尺寸范围进行限制。 |
485| layout     | (LayoutInfo: [LayoutInfo](#layoutinfodeprecated))&nbsp;=&gt;&nbsp;void | 调用此方法对子组件的位置信息进行限制。 |
486
487## LayoutBorderInfo<sup>(deprecated)</sup>
488
489子组件border信息。
490
491从API version 9开始,从API version 10开始废弃,该接口支持在ArkTS卡片中使用。
492
493| 属性          | 类型                                 | 描述                      |
494|-------------|--------------------------------------|-------------------------|
495| borderWidth | [EdgeWidths](ts-types.md#edgewidths9) | 边框宽度类型,用于描述组件边框不同方向的宽度。 |
496| margin      | [Margin](ts-types.md#margin)         | 外边距类型,用于描述组件不同方向的外边距。   |
497| padding     | [Padding](ts-types.md#padding)       | 内边距类型,用于描述组件不同方向的内边距。   |
498
499## LayoutInfo<sup>(deprecated)</sup>
500
501子组件layout信息。
502
503从API version 9开始,从API version 10开始废弃,该接口支持在ArkTS卡片中使用。
504
505| 属性       | 类型                                                   | 说明             |
506| ---------- | ---------------------------------------------------------- | ---------------- |
507| position   | [Position](ts-types.md#position)                           | 子组件位置坐标。 |
508| constraint | [ConstraintSizeOptions](ts-types.md#constraintsizeoptions) | 子组件约束尺寸。 |
509
510通过layout修改布局。
511```ts
512// xxx.ets
513@Entry
514@Component
515struct Index {
516  build() {
517    Column() {
518      CustomLayout() {
519        ForEach([1, 2, 3], (index: number) => {
520          Text('Sub' + index)
521            .fontSize(30)
522            .borderWidth(2)
523        })
524      }
525    }
526  }
527}
528
529
530@Component
531struct CustomLayout {
532  @Builder
533  doNothingBuilder() {
534  };
535
536  @BuilderParam builder: () => void = this.doNothingBuilder;
537
538  onLayout(children: Array<LayoutChild>, constraint: ConstraintSizeOptions) {
539    let pos = 0;
540    children.forEach((child) => {
541      child.layout({ position: { x: pos, y: pos }, constraint: constraint })
542      pos += 70;
543    })
544  }
545
546  onMeasure(children: Array<LayoutChild>, constraint: ConstraintSizeOptions) {
547    let size = 100;
548    children.forEach((child) => {
549      child.measure({ minHeight: size, minWidth: size, maxWidth: size, maxHeight: size })
550      size += 50;
551    })
552  }
553
554  build() {
555    this.builder()
556  }
557}
558```
559
560![zh-cn_image_0000001511900496](figures/zh-cn_image_0000001511900496.png)