“ca535d18ab808785cc969c8c8f96413536cd7926”上不存在“...scripts/git@gitcode.net:s920243400/PaddleDetection.git”
提交 8daeff51 编写于 作者: E ester.zhou

Update docs (17149)

Signed-off-by: Nester.zhou <ester.zhou@huawei.com>
上级 ff6bf233
......@@ -117,12 +117,12 @@ To fully understand the preceding example, a knowledge of the following concepts
## Member Functions/Variables
In addition to the mandatory** build()** function, a custom component may implement other member functions with the following restrictions:
In addition to the mandatory **build()** function, a custom component may implement other member functions with the following restrictions:
- Static functions are not supported.
- Access to the member functions is always private. Defining **private** access is optional. Defining access other than **private** is a syntax error.
- Access to the member functions is always private.
A custom component can also implement member variables with the following restrictions:
......@@ -130,7 +130,7 @@ A custom component can also implement member variables with the following restri
- Static member variables are not supported.
- Access to the member variables is always private.The access rules of member variables are the same as those of member functions.
- Access to the member variables is always private. The access rules of member variables are the same as those of member functions.
- Local initialization is optional for some member variables and mandatory for others. For details about whether local initialization or initialization from the parent component is required, see [State Management](arkts-state-management-overview.md).
......
......@@ -129,12 +129,12 @@ struct MyComponent {
build() {
Column() {
// When this.showChild is true, the Child child component is created, and Child aboutToAppear is invoked.
// When this.showChild is true, the Child component is created, and Child aboutToAppear is invoked.
if (this.showChild) {
Child()
}
// When this.showChild is false, the Child child component is deleted, and Child aboutToDisappear is invoked.
Button('create or delete Child').onClick(() => {
// When this.showChild is false, the Child component is deleted, and Child aboutToDisappear is invoked.
Button('delete Child').onClick(() => {
this.showChild = false;
})
// Because of the pushing from the current page to Page2, onPageHide is invoked.
......
......@@ -275,7 +275,7 @@ struct ReaderComp {
build() {
Row() {
Text(this.title)
Text(`... ${this.readIt ? 'I have read' : 'I have bot read it'}`)
Text(`... ${this.readIt ? 'I have read' : 'I have not read it'}`)
.onClick(() => this.readIt = true)
}
}
......@@ -350,18 +350,13 @@ struct MainProgram {
}
Row() {
Column(
Column()
// customCounter must be initialized from the parent component due to lack of local initialization. Here, customCounter2 does not need to be initialized.
MyComponent({ customCounter: this.mainCounter })
// customCounter2 of the child component can also be initialized from the parent component. The value from the parent component overwrites the locally assigned value of customCounter2 during initialization.
MyComponent({ customCounter: this.mainCounter, customCounter2: this.mainCounter })
}.width('40%')
}
Row() {
Text('').width(480).height(10)
}
}
}
}
```
......@@ -235,7 +235,7 @@ struct MyComponent {
})
Button(`Click to increase count=${this.count}`).onClick(() => {
// The update of the @State decorated variable triggers the update of the <Text> component.
// The update of the @State decorated variable triggers the update of the <Button> component.
this.count += this.increaseBy;
})
}
......
......@@ -161,6 +161,7 @@
- [Time Picker Dialog Box](ts-methods-timepicker-dialog.md)
- [Text Picker Dialog Box](ts-methods-textpicker-dialog.md)
- [Menu](ts-methods-menu.md)
- [Custom Component Lifecycle](ts-custom-component-lifecycle.md)
- [State Management with Application-level Variables](ts-state-management.md)
- [Pixel Units](ts-pixel-units.md)
- [Enums](ts-appendix-enums.md)
......
......@@ -12,6 +12,8 @@ Use **RenderingContext** to draw rectangles, text, images, and other objects on
CanvasRenderingContext2D(setting: RenderingContextSetting)
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Description |
......@@ -25,34 +27,36 @@ RenderingContextSettings(antialias?: boolean)
Configures the settings of a **CanvasRenderingContext2D** object, including whether to enable antialiasing.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Description |
| --------- | ------- | ---- | ----------------------------- |
| antialias | boolean | No | Whether to enable antialiasing.<br>Default value: **false** |
| antialias | boolean | No | Whether to enable antialiasing.<br>Default value: **false**|
## Attributes
| Name | Type | Description |
| ----------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
| [fillStyle](#fillstyle) | string \| [CanvasGradient](ts-components-canvas-canvasgradient.md) \| [CanvasPattern](#canvaspattern) | Style to fill an area.<br>- When the type is string, this attribute indicates the color of the fill area.<br>- When the type is **CanvasGradient**, this attribute indicates a gradient object, which is created using the **[createLinearGradient](#createlineargradient)** API.<br>- When the type is **CanvasPattern**, this attribute indicates a pattern, which is created using the **[createPattern](#createpattern)** API.|
| [fillStyle](#fillstyle) | string \|[CanvasGradient](ts-components-canvas-canvasgradient.md) \| [CanvasPattern](#canvaspattern) | Style to fill an area.<br>- When the type is string, this attribute indicates the color of the fill area.<br>- When the type is number, this attribute indicates the color of the fill area.<br>- When the type is **CanvasGradient**, this attribute indicates a gradient object, which is created using the **[createLinearGradient](#createlineargradient)** API.<br>- When the type is **CanvasPattern**, this attribute indicates a pattern, which is created using the **[createPattern](#createpattern)** API.<br>Since API version 9, this API is supported in ArkTS widgets. |
| [lineWidth](#linewidth) | number | Line width. |
| [strokeStyle](#strokestyle) | string \| [CanvasGradient](ts-components-canvas-canvasgradient.md) \| [CanvasPattern](#canvaspattern) | Stroke style.<br>- When the type is string, this attribute indicates the stroke color.<br>- When the type is **CanvasGradient**, this attribute indicates a gradient object, which is created using the **[createLinearGradient](#createlineargradient)** API.<br>- When the type is **CanvasPattern**, this attribute indicates a pattern, which is created using the **[createPattern](#createpattern)** API.|
| [lineCap](#linecap) | CanvasLineCap | Style of the line endpoints. The options are as follows:<br>- **'butt'**: The endpoints of the line are squared off.<br>- **'round'**: The endpoints of the line are rounded.<br>- **'square'**: The endpoints of the line are squared off by adding a box with an equal width and half the height of the line's thickness.<br>Default value: **'butt'**|
| [lineJoin](#linejoin) | CanvasLineJoin | Style of the shape used to join line segments. The options are as follows:<br>- **'round'**: The shape used to join line segments is a sector, whose radius at the rounded corner is equal to the line width.<br>- **'bevel'**: The shape used to join line segments is a triangle. The rectangular corner of each line is independent.<br>- **'miter'**: The shape used to join line segments has a mitered corner by extending the outside edges of the lines until they meet. You can view the effect of this attribute in **miterLimit**.<br>Default value: **'miter'**|
| [miterLimit](#miterlimit) | number | Maximum miter length. The miter length is the distance between the inner corner and the outer corner where two lines meet.<br>Default value: **10**|
| [font](#font) | string | Font style.<br>Syntax: ctx.font='font-size font-family'<br>- (Optional) **font-size**: font size and row height. The unit can only be pixels.<br>- (Optional) **font-family**: font family.<br>Syntax: ctx.font='font-style font-weight font-size font-family'<br>- (Optional) **font-style**: font style. Available values are **'normal'** and **'italic'**.<br>- (Optional) **font-weight**: font weight. Available values are as follows: **'normal'**, **'bold'**, **'bolder'**, **'lighter'**, **'100'**, **'200'**, **'300'**, **'400'**, **'500'**, **'600'**, **'700'**, **'800'**, **'900'**.<br>- (Optional) **font-size**: font size and row height. The unit can only be pixels.<br>- (Optional) **font-family**: font family. Available values are **'sans-serif'**, **'serif'**, and **'monospace'**.<br>Default value: **'normal normal 14px sans-serif'**|
| [textAlign](#textalign) | CanvasTextAlign | Text alignment mode. Available values are as follows:<br>- **'left'**: The text is left-aligned.<br>- **'right'**: The text is right-aligned.<br>- **'center'**: The text is center-aligned.<br>- **'start'**: The text is aligned with the start bound.<br>- **'end'**: The text is aligned with the end bound.<br>In the **ltr** layout mode, the value **'start'** equals **'left'**. In the **rtl** layout mode, the value **'start'** equals **'right'**.<br>Default value: **'left'**|
| [textBaseline](#textbaseline) | CanvasTextBaseline | Horizontal alignment mode of text. Available values are as follows:<br>- **'alphabetic'**: The text baseline is the normal alphabetic baseline.<br>- **'top'**: The text baseline is on the top of the text bounding box.<br>- **'hanging'**: The text baseline is a hanging baseline over the text.<br>- **'middle'**: The text baseline is in the middle of the text bounding box.<br>**'ideographic'**: The text baseline is the ideographic baseline. If a character exceeds the alphabetic baseline, the ideographic baseline is located at the bottom of the excess character.<br>- **'bottom'**: The text baseline is at the bottom of the text bounding box. Its difference from the ideographic baseline is that the ideographic baseline does not consider letters in the next line.<br>Default value: **'alphabetic'**|
| [globalAlpha](#globalalpha) | number | Opacity.<br>**0.0**: completely transparent.<br>**1.0**: completely opaque. |
| [lineDashOffset](#linedashoffset) | number | Offset of the dashed line. The precision is float.<br>Default value: **0.0** |
| [globalCompositeOperation](#globalcompositeoperation) | string | Composition operation type. Available values are as follows: **'source-over'**, **'source-atop'**, **'source-in'**, **'source-out'**, **'destination-over'**, **'destination-atop'**, **'destination-in'**, **'destination-out'**, **'lighter'**, **'copy'**, and **'xor'**.<br>Default value: **'source-over'**|
| [shadowBlur](#shadowblur) | number | Blur level during shadow drawing. A larger value indicates a more blurred effect. The precision is float.<br>Default value: **0.0**|
| [shadowColor](#shadowcolor) | string | Shadow color. |
| [shadowOffsetX](#shadowoffsetx) | number | X-axis shadow offset relative to the original object. |
| [shadowOffsetY](#shadowoffsety) | number | Y-axis shadow offset relative to the original object. |
| [imageSmoothingEnabled](#imagesmoothingenabled) | boolean | Whether to adjust the image smoothness during image drawing. The value **true** means to enable this feature, and **false** means the opposite.<br>Default value: **true**|
| [strokeStyle](#strokestyle) | string \|[CanvasGradient](ts-components-canvas-canvasgradient.md) \| [CanvasPattern](#canvaspattern) | Stroke style.<br>- When the type is string, this attribute indicates the stroke color.<br>- When the type is number, this attribute indicates the stroke color.<br>- When the type is **CanvasGradient**, this attribute indicates a gradient object, which is created using the **[createLinearGradient](#createlineargradient)** API.<br>- When the type is **CanvasPattern**, this attribute indicates a pattern, which is created using the **[createPattern](#createpattern)** API.<br>Since API version 9, this API is supported in ArkTS widgets. |
| [lineCap](#linecap) | CanvasLineCap | Style of the line endpoints. The options are as follows:<br>- **'butt'**: The endpoints of the line are squared off.<br>- **'round'**: The endpoints of the line are rounded.<br>- **'square'**: The endpoints of the line are squared off by adding a box with an equal width and half the height of the line's thickness.<br>Default value: **'butt'**<br>Since API version 9, this API is supported in ArkTS widgets. |
| [lineJoin](#linejoin) | CanvasLineJoin | Style of the shape used to join line segments. The options are as follows:<br>- **'round'**: The shape used to join line segments is a sector, whose radius at the rounded corner is equal to the line width.<br>- **'bevel'**: The shape used to join line segments is a triangle. The rectangular corner of each line is independent.<br>- **'miter'**: The shape used to join line segments has a mitered corner by extending the outside edges of the lines until they meet. You can view the effect of this attribute in **miterLimit**.<br>Default value: **'miter'**<br>Since API version 9, this API is supported in ArkTS widgets. |
| [miterLimit](#miterlimit) | number | Maximum miter length. The miter length is the distance between the inner corner and the outer corner where two lines meet.<br>Default value: **10**<br>Since API version 9, this API is supported in ArkTS widgets. |
| [font](#font) | string | Font style.<br>Syntax: ctx.font='font-size font-family'<br>- (Optional) **font-size**: font size and row height. The unit can only be pixels.<br>- (Optional) **font-family**: font family.<br>Syntax: ctx.font='font-style font-weight font-size font-family'<br>- (Optional) **font-style**: font style. Available values are **'normal'** and **'italic'**.<br>- (Optional) **font-weight**: font weight. Available values are as follows: **'normal'**, **'bold'**, **'bolder'**, **'lighter'**, **'100'**, **'200'**, **'300'**, **'400'**, **'500'**, **'600'**, **'700'**, **'800'**, **'900'**.<br>- (Optional) **font-size**: font size and row height. The unit can only be pixels.<br>- (Optional) **font-family**: font family. Available values are **'sans-serif'**, **'serif'**, and **'monospace'**.<br>Default value: **'normal normal 14px sans-serif'**<br>Since API version 9, this API is supported in ArkTS widgets. |
| [textAlign](#textalign) | CanvasTextAlign | Text alignment mode. Available values are as follows:<br>- **'left'**: The text is left-aligned.<br>- **'right'**: The text is right-aligned.<br>- **'center'**: The text is center-aligned.<br>- **'start'**: The text is aligned with the start bound.<br>- **'end'**: The text is aligned with the end bound.<br>In the **ltr** layout mode, the value **'start'** equals **'left'**. In the **rtl** layout mode, the value **'start'** equals **'right'**.<br>Default value: **'left'**<br>Since API version 9, this API is supported in ArkTS widgets. |
| [textBaseline](#textbaseline) | CanvasTextBaseline | Horizontal alignment mode of text. Available values are as follows:<br>- **'alphabetic'**: The text baseline is the normal alphabetic baseline.<br>- **'top'**: The text baseline is on the top of the text bounding box.<br>- **'hanging'**: The text baseline is a hanging baseline over the text.<br>- **'middle'**: The text baseline is in the middle of the text bounding box.<br>**'ideographic'**: The text baseline is the ideographic baseline. If a character exceeds the alphabetic baseline, the ideographic baseline is located at the bottom of the excess character.<br>- **'bottom'**: The text baseline is at the bottom of the text bounding box. Its difference from the ideographic baseline is that the ideographic baseline does not consider letters in the next line.<br>Default value: **'alphabetic'**<br>Since API version 9, this API is supported in ArkTS widgets. |
| [globalAlpha](#globalalpha) | number | Opacity.<br>**0.0**: completely transparent.<br>**1.0**: completely opaque.<br>Since API version 9, this API is supported in ArkTS widgets. |
| [lineDashOffset](#linedashoffset) | number | Offset of the dashed line. The precision is float.<br>Default value: **0.0**<br>Since API version 9, this API is supported in ArkTS widgets. |
| [globalCompositeOperation](#globalcompositeoperation) | string | Composition operation type. Available values are as follows: **'source-over'**, **'source-atop'**, **'source-in'**, **'source-out'**, **'destination-over'**, **'destination-atop'**, **'destination-in'**, **'destination-out'**, **'lighter'**, **'copy'**, and **'xor'**.<br>Default value: **'source-over'**<br>Since API version 9, this API is supported in ArkTS widgets. |
| [shadowBlur](#shadowblur) | number | Blur level during shadow drawing. A larger value indicates a more blurred effect. The precision is float.<br>Default value: **0.0**<br>Since API version 9, this API is supported in ArkTS widgets. |
| [shadowColor](#shadowcolor) | string | Shadow color.<br>Since API version 9, this API is supported in ArkTS widgets. |
| [shadowOffsetX](#shadowoffsetx) | number | X-axis shadow offset relative to the original object.<br>Since API version 9, this API is supported in ArkTS widgets. |
| [shadowOffsetY](#shadowoffsety) | number | Y-axis shadow offset relative to the original object.<br>Since API version 9, this API is supported in ArkTS widgets. |
| [imageSmoothingEnabled](#imagesmoothingenabled) | boolean | Whether to adjust the image smoothness during image drawing. The value **true** means to enable this feature, and **false** means the opposite.<br>Default value: **true**<br>Since API version 9, this API is supported in ArkTS widgets. |
> **NOTE**
>
......@@ -77,7 +81,7 @@ struct FillStyleExample {
.backgroundColor('#ffff00')
.onReady(() =>{
this.context.fillStyle = '#0000ff'
this.context.fillRect(20, 160, 150, 100)
this.context.fillRect(20, 20, 150, 100)
})
}
.width('100%')
......@@ -657,6 +661,8 @@ fillRect(x: number, y: number, w: number, h: number): void
Fills a rectangle on the canvas.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -701,6 +707,8 @@ strokeRect(x: number, y: number, w: number, h: number): void
Draws an outlined rectangle on the canvas.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -745,6 +753,8 @@ clearRect(x: number, y: number, w: number, h: number): void
Clears the content in a rectangle on the canvas.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -791,6 +801,8 @@ fillText(text: string, x: number, y: number, maxWidth?: number): void
Draws filled text on the canvas.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory| Default Value| Description |
......@@ -836,6 +848,8 @@ strokeText(text: string, x: number, y: number, maxWidth?:number): void
Draws a text stroke on the canvas.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory| Default Value| Description |
......@@ -881,6 +895,8 @@ measureText(text: string): TextMetrics
Measures the specified text to obtain its width. This API returns a **TextMetrics** object.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name| Type | Mandatory| Default Value| Description |
......@@ -890,8 +906,8 @@ Measures the specified text to obtain its width. This API returns a **TextMetric
**Return value**
| Type | Description |
| ----------- | ---------------- |
| TextMetrics | **TextMetrics** object.|
| ----------- | ------------------------------------------------------------ |
| TextMetrics | **TextMetrics** object.<br>Since API version 9, this API is supported in ArkTS widgets.|
**TextMetrics**
......@@ -951,6 +967,8 @@ stroke(path?: Path2D): void
Strokes a path.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -997,6 +1015,8 @@ beginPath(): void
Creates a drawing path.
Since API version 9, this API is supported in ArkTS widgets.
**Example**
```ts
......@@ -1037,6 +1057,8 @@ moveTo(x: number, y: number): void
Moves a drawing path to a target position on the canvas.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -1082,6 +1104,8 @@ lineTo(x: number, y: number): void
Connects the current point to a target position using a straight line.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -1127,6 +1151,8 @@ closePath(): void
Draws a closed path.
Since API version 9, this API is supported in ArkTS widgets.
**Example**
```ts
......@@ -1167,12 +1193,14 @@ createPattern(image: ImageBitmap, repetition: string | null): CanvasPattern | nu
Creates a pattern for image filling based on a specified source image and repetition mode.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory| Description |
| ---------- | -------------------------------------------------- | ---- | ------------------------------------------------------------ |
| image | [ImageBitmap](ts-components-canvas-imagebitmap.md) | Yes | Source image. For details, see **ImageBitmap**. |
| repetition | string | Yes | Repetition mode. The value can be **'repeat'**, **'repeat-x'**, **'repeat-y'**, or **'no-repeat'**.<br>Default value: **''**|
| repetition | string | Yes | Repetition mode. The value can be **'repeat'**, **'repeat-x'**, **'repeat-y'**, or **'no-repeat'**.<br>Default value: **''** |
**Return value**
......@@ -1218,6 +1246,8 @@ bezierCurveTo(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number,
Draws a cubic bezier curve on the canvas.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -1267,6 +1297,8 @@ quadraticCurveTo(cpx: number, cpy: number, x: number, y: number): void
Draws a quadratic curve on the canvas.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -1314,6 +1346,8 @@ arc(x: number, y: number, radius: number, startAngle: number, endAngle: number,
Draws an arc on the canvas.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -1362,6 +1396,8 @@ arcTo(x1: number, y1: number, x2: number, y2: number, radius: number): void
Draws an arc based on the radius and points on the arc.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -1409,6 +1445,8 @@ ellipse(x: number, y: number, radiusX: number, radiusY: number, rotation: number
Draws an ellipse in the specified rectangular region on the canvas.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -1459,6 +1497,8 @@ rect(x: number, y: number, w: number, h: number): void
Creates a rectangle on the canvas.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -1504,6 +1544,8 @@ fill(fillRule?: CanvasFillRule): void
Fills the area inside a closed path on the canvas.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -1545,6 +1587,8 @@ fill(path: Path2D, fillRule?: CanvasFillRule): void
Fills the area inside a closed path on the canvas.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -1598,6 +1642,8 @@ clip(fillRule?: CanvasFillRule): void
Sets the current path to a clipping area.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -1641,6 +1687,8 @@ clip(path: Path2D, fillRule?: CanvasFillRule): void
Sets the current path to a clipping path.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -1653,9 +1701,9 @@ Sets the current path to a clipping path.
```ts
// xxx.ets
@Entry
@Component
struct Clip {
@Entry
@Component
struct Clip {
private settings: RenderingContextSettings = new RenderingContextSettings(true)
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
build() {
......@@ -1681,7 +1729,7 @@ struct Clip {
.width('100%')
.height('100%')
}
}
}
```
![en-us_image_000000127777779](figures/en-us_image_000000127777779.png)
......@@ -1693,6 +1741,8 @@ filter(filter: string): void
Provides filter effects. This API is a void API.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -1706,6 +1756,8 @@ getTransform(): Matrix2D
Obtains the current transformation matrix being applied to the context. This API is a void API.
Since API version 9, this API is supported in ArkTS widgets.
### resetTransform
......@@ -1713,6 +1765,8 @@ resetTransform(): void
Resets the current transform to the identity matrix. This API is a void API.
Since API version 9, this API is supported in ArkTS widgets.
### direction
......@@ -1720,6 +1774,8 @@ direction(direction: CanvasDirection): void
Sets the current text direction used to draw text. This API is a void API.
Since API version 9, this API is supported in ArkTS widgets.
### rotate
......@@ -1727,6 +1783,8 @@ rotate(angle: number): void
Rotates a canvas clockwise around its coordinate axes.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -1769,6 +1827,8 @@ scale(x: number, y: number): void
Scales the canvas based on the given scale factors.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -1814,6 +1874,8 @@ transform(a: number, b: number, c: number, d: number, e: number, f: number): voi
Defines a transformation matrix. To transform a graph, you only need to set parameters of the matrix. The coordinates of the graph are multiplied by the matrix values to obtain new coordinates of the transformed graph. You can use the matrix to implement multiple transform effects.
Since API version 9, this API is supported in ArkTS widgets.
> **NOTE**
>
> The following formulas calculate coordinates of the transformed graph. **x** and **y** represent coordinates before transformation, and **x'** and **y'** represent coordinates after transformation.
......@@ -1875,6 +1937,8 @@ setTransform(a: number, b: number, c: number, d: number, e: number, f: number):
Resets the existing transformation matrix and creates a new transformation matrix by using the same parameters as the **transform()** API.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -1932,6 +1996,8 @@ translate(x: number, y: number): void
Moves the origin of the coordinate system.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -1980,7 +2046,7 @@ drawImage(image: ImageBitmap | PixelMap, sx: number, sy: number, sw: number, sh:
Draws an image on the canvas.
Since API version 9, this API is supported in ArkTS widgets.
Since API version 9, this API is supported in ArkTS widgets, except that **PixelMap** objects are not supported.
**Parameters**
......@@ -2033,6 +2099,8 @@ createImageData(sw: number, sh: number): ImageData
Creates an **[ImageData](ts-components-canvas-imagedata.md)** object with the specified dimensions.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -2045,6 +2113,8 @@ createImageData(imageData: ImageData): ImageData
Creates an **[ImageData](ts-components-canvas-imagedata.md)** object.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -2085,6 +2155,8 @@ getImageData(sx: number, sy: number, sw: number, sh: number): ImageData
Obtains the **[ImageData](ts-components-canvas-imagedata.md)** object created with the pixels within the specified area on the canvas.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -2105,9 +2177,9 @@ Obtains the **[ImageData](ts-components-canvas-imagedata.md)** object created wi
```ts
// xxx.ets
@Entry
@Component
struct GetImageData {
@Entry
@Component
struct GetImageData {
private settings: RenderingContextSettings = new RenderingContextSettings(true)
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
private img:ImageBitmap = new ImageBitmap("/common/images/1234.png")
......@@ -2127,7 +2199,7 @@ struct GetImageData {
.width('100%')
.height('100%')
}
}
}
```
![en-us_image_000000127777780](figures/en-us_image_000000127777780.png)
......@@ -2141,6 +2213,8 @@ putImageData(imageData: ImageData, dx: number, dy: number, dirtyX: number, dirty
Puts an **[ImageData](ts-components-canvas-imagedata.md)** object onto a rectangular area on the canvas.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -2195,6 +2269,8 @@ setLineDash(segments: number[]): void
Sets the dash line style.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Description |
......@@ -2238,6 +2314,8 @@ getLineDash(): number[]
Obtains the dash line style.
Since API version 9, this API is supported in ArkTS widgets.
**Return value**
| Type | Description |
......@@ -2249,9 +2327,9 @@ Obtains the dash line style.
```ts
// xxx.ets
@Entry
@Component
struct CanvasGetLineDash {
@Entry
@Component
struct CanvasGetLineDash {
@State message: string = 'Hello World'
private settings: RenderingContextSettings = new RenderingContextSettings(true)
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
......@@ -2282,7 +2360,7 @@ struct CanvasGetLineDash {
}
.height('100%')
}
}
}
```
![en-us_image_000000127777778](figures/en-us_image_000000127777778.png)
......@@ -2294,6 +2372,8 @@ imageSmoothingQuality(quality: imageSmoothingQuality)
Sets the quality of image smoothing. This API is a void API.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Description |
......@@ -2308,6 +2388,8 @@ transferFromImageBitmap(bitmap: ImageBitmap): void
Displays the specified **ImageBitmap** object.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Description |
......@@ -2377,9 +2459,9 @@ Since API version 9, this API is supported in ArkTS widgets.
```ts
// xxx.ets
@Entry
@Component
struct ToDataURL {
@Entry
@Component
struct ToDataURL {
private settings: RenderingContextSettings = new RenderingContextSettings(true)
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
......@@ -2396,7 +2478,7 @@ struct ToDataURL {
.width('100%')
.height('100%')
}
}
}
```
......@@ -2406,6 +2488,8 @@ restore(): void
Restores the saved drawing context.
Since API version 9, this API is supported in ArkTS widgets.
**Example**
```ts
......@@ -2444,6 +2528,8 @@ save(): void
Saves all states of the canvas in the stack. This API is usually called when the drawing state needs to be saved.
Since API version 9, this API is supported in ArkTS widgets.
**Example**
```ts
......@@ -2482,6 +2568,8 @@ createLinearGradient(x0: number, y0: number, x1: number, y1: number): void
Creates a linear gradient.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -2513,7 +2601,7 @@ Creates a linear gradient.
grad.addColorStop(0.5, '#ffffff')
grad.addColorStop(1.0, '#00ff00')
this.context.fillStyle = grad
this.context.fillRect(0, 0, 500, 500)
this.context.fillRect(0, 0, 400, 400)
})
}
.width('100%')
......@@ -2531,6 +2619,8 @@ createRadialGradient(x0: number, y0: number, r0: number, x1: number, y1: number,
Creates a linear gradient.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -2564,7 +2654,7 @@ Creates a linear gradient.
grad.addColorStop(0.5, '#ffffff')
grad.addColorStop(1.0, '#00ff00')
this.context.fillStyle = grad
this.context.fillRect(0, 0, 500, 500)
this.context.fillRect(0, 0, 440, 440)
})
}
.width('100%')
......@@ -2575,7 +2665,8 @@ Creates a linear gradient.
![en-us_image_0000001257058405](figures/en-us_image_0000001257058405.png)
## CanvasPattern
Defines an object created using the **[createPattern](#createpattern)** API.
Since API version 9, this API is supported in ArkTS widgets.
......@@ -47,7 +47,7 @@ struct Page45 {
grad.addColorStop(0.5, '#ffffff')
grad.addColorStop(1.0, '#00ff00')
this.context.fillStyle = grad
this.context.fillRect(0, 0, 500, 500)
this.context.fillRect(0, 0, 400, 400)
})
}
.width('100%')
......
......@@ -24,12 +24,12 @@ An **ImageData** object stores pixel data rendered on a canvas.
```ts
// xxx.ets
@Entry
@Component
struct Translate {
@Entry
@Component
struct Translate {
private settings: RenderingContextSettings = new RenderingContextSettings(true)
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
private img:ImageBitmap = new ImageBitmap("/common/images/1234.png")
private img:ImageBitmap = new ImageBitmap("common/images/1234.png")
build() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
......@@ -46,7 +46,7 @@ struct Translate {
.width('100%')
.height('100%')
}
}
}
```
![en-us_image_000000127777780](figures/en-us_image_000000127777780.png)
# Path2D
**Path2D** allows you to describe a path through an existing path. This path can be drawn through the **stroke** API of **Canvas**.
**Path2D** allows you to describe a path through an existing path. This path can be drawn through the **stroke** or **fill** API of **Canvas**.
> **NOTE**
>
......
......@@ -43,7 +43,7 @@ Invoked when a page is hidden. This callback is used in the routing process or s
onBackPress?(): void
Invoked when a user clicks the back button. It works only for the custom components decorated by **@Entry**. The value **true** is returned if the page processes the return logic instead of performing page routing. The value false is returned if the default return logic is used. If the return value is not set, the value **false** is used.
Invoked when a user clicks the back button. It works only for the custom components decorated by **@Entry**. The value **true** is returned if the page processes the return logic instead of performing page routing. The value **false** is returned if the default return logic is used. If the return value is not set, the value **false** is used.
```ts
......@@ -126,7 +126,7 @@ Since API version 9, this API is supported in ArkTS widgets.
| id | string | ID of the child component. |
| constraint | [ConstraintSizeOptions](ts-types.md#constraintsizeoptions) | Constraint size of the child component. |
| borderInfo | [LayoutBorderInfo](#layoutborderinfo9) | Provides the border information of the child component. |
| position | [Position](ts-types.md#position) | Position coordinates of the child component. |
| position | [Position](ts-types.md#position8) | Position coordinates of the child component. |
| measure | (childConstraint:) =&gt; void | Method called to apply the size constraint to the child component.|
| layout | (LayoutInfo: [LayoutInfo](#layoutinfo9)) =&gt; void| Method called to apply the layout information to the child component.|
......@@ -139,7 +139,7 @@ Since API version 9, this API is supported in ArkTS widgets.
| Parameter | Type | Description |
| ----------- | ------------------------------------ | ----------------------- |
| borderWidth | [EdgeWidths](ts-types.md#edgewidths) | Edge widths in different directions of the component.|
| borderWidth | [EdgeWidths](ts-types.md#edgewidths9) | Edge widths in different directions of the component.|
| margin | [Margin](ts-types.md#margin) | Margins in different directions of the component. |
| padding | [Padding](ts-types.md#padding) | Paddings in different directions of the component. |
......@@ -152,7 +152,7 @@ Since API version 9, this API is supported in ArkTS widgets.
| Parameter | Type | Description |
| ---------- | ---------------------------------------- | -------- |
| position | [Position](ts-types.md#position) | Position coordinates of the child component.|
| position | [Position](ts-types.md#position8) | Position coordinates of the child component.|
| constraint | [ConstraintSizeOptions](ts-types.md#constraintsizeoptions) | Constraint size of the child component.|
......
......@@ -12,6 +12,8 @@ Use **OffscreenCanvasRenderingContext2D** to draw rectangles, images, and text o
OffscreenCanvasRenderingContext2D(width: number, height: number, setting: RenderingContextSettings)
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory| Description |
......@@ -25,23 +27,23 @@ OffscreenCanvasRenderingContext2D(width: number, height: number, setting: Render
| Name | Type | Description |
| ----------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
| [fillStyle](#fillstyle) | string \| [CanvasGradient](ts-components-canvas-canvasgradient.md) \| [CanvasPattern](#canvaspattern) | Style to fill an area.<br>- When the type is **string**, this attribute indicates the color of the filling area.<br>- When the type is **CanvasGradient**, this attribute indicates a gradient object, which is created using the **[createLinearGradient](#createlineargradient)** API.<br>- When the type is **CanvasPattern**, this attribute indicates a pattern, which is created using the **[createPattern](#createpattern)** API. |
| [lineWidth](#linewidth) | number | Line width. |
| [strokeStyle](#strokestyle) | string \| [CanvasGradient](ts-components-canvas-canvasgradient.md) \| [CanvasPattern](#canvaspattern) | Stroke style.<br>- When the type is **\<color>**, this parameter indicates the stroke color.<br>- When the type is **CanvasGradient**, this attribute indicates a gradient object, which is created using the **[createLinearGradient](#createlineargradient)** API.<br>- When the type is **CanvasPattern**, this attribute indicates a pattern, which is created using the **[createPattern](#createpattern)** API. |
| [lineCap](#linecap) | CanvasLineCap | Style of the line endpoints. The options are as follows:<br>- **butt**: The endpoints of the line are squared off.<br>- **round**: The endpoints of the line are rounded.<br>- **square**: The endpoints of the line are squared off, and each endpoint has added a rectangle whose length is the same as the line thickness and whose width is half of the line thickness.<br>- Default value: **'butt'** |
| [lineJoin](#linejoin) | CanvasLineJoin | Style of the shape used to join line segments. The options are as follows:<br>- **round**: The intersection is a sector, whose radius at the rounded corner is equal to the line width.<br>- **bevel**: The intersection is a triangle. The rectangular corner of each line is independent.<br>- **miter**: The intersection has a miter corner by extending the outside edges of the lines until they meet. You can view the effect of this attribute in **miterLimit**.<br>- Default value: **'miter'** |
| [miterLimit](#miterlimit) | number | Maximum miter length. The miter length is the distance between the inner corner and the outer corner where two lines meet.<br>- Default value: **10** |
| [font](#font) | string | Font style.<br>Syntax: ctx.font='font-size font-family'<br>- (Optional) **font-size**: font size and row height. The unit can only be pixels.<br>(Optional) **font-family**: font family.<br>Syntax: ctx.font='font-style font-weight font-size font-family'<br>- (Optional) **font-style**: font style. Available values are **normal** and **italic**.<br>- (Optional) **font-weight**: font weight. Available values are as follows: **normal**, **bold**, **bolder**, **lighter**, **100**, **200**, **300**, **400**, **500**, **600**, **700**, **800**, **900**.<br>- (Optional) **font-size**: font size and row height. The unit can only be pixels.<br>- (Optional) **font-family**: font family. Available values are **sans-serif**, **serif**, and **monospace**.<br>Default value: **'normal normal 14px sans-serif'** |
| [textAlign](#textalign) | CanvasTextAlign | Text alignment mode. Available values are as follows:<br>- **left**: The text is left-aligned.<br>- **right**: The text is right-aligned.<br>- **center**: The text is center-aligned.<br>- **start**: The text is aligned with the start bound.<br>- **end**: The text is aligned with the end bound.<br>**NOTE**<br>In the **ltr** layout mode, the value **'start'** equals **'left'**. In the **rtl** layout mode, the value **'start'** equals **'right'**.<br>- Default value: **'left'** |
| [textBaseline](#textbaseline) | CanvasTextBaseline | Horizontal alignment mode of text. Available values are as follows:<br>- **alphabetic**: The text baseline is the normal alphabetic baseline.<br>- **top**: The text baseline is on the top of the text bounding box.<br>- **hanging**: The text baseline is a hanging baseline over the text.<br>- **middle**: The text baseline is in the middle of the text bounding box.<br>**'ideographic'**: The text baseline is the ideographic baseline. If a character exceeds the alphabetic baseline, the ideographic baseline is located at the bottom of the excess character.<br>- **bottom**: The text baseline is at the bottom of the text bounding box. Its difference from the ideographic baseline is that the ideographic baseline does not consider letters in the next line.<br>- Default value: **'alphabetic'** |
| [fillStyle](#fillstyle) | string \|[CanvasGradient](ts-components-canvas-canvasgradient.md) \| [CanvasPattern](#canvaspattern) | Style to fill an area.<br>- When the type is **string**, this attribute indicates the color of the filling area.<br>- When the type is **number**, this attribute indicates the color of the filling area.<br>- When the type is **CanvasGradient**, this attribute indicates a gradient object, which is created using the **[createLinearGradient](#createlineargradient)** API.<br>- When the type is **CanvasPattern**, this attribute indicates a pattern, which is created using the **[createPattern](#createpattern)** API.<br>Since API version 9, this API is supported in ArkTS widgets.|
| [lineWidth](#linewidth) | number | Line width.<br>Since API version 9, this API is supported in ArkTS widgets.|
| [strokeStyle](#strokestyle) | string \|[CanvasGradient](ts-components-canvas-canvasgradient.md) \| [CanvasPattern](#canvaspattern) | Stroke style.<br>- When the type is **string**, this attribute indicates the stroke color.<br>- When the type is **number**, this attribute indicates the stroke color.<br>- When the type is **CanvasGradient**, this attribute indicates a gradient object, which is created using the **[createLinearGradient](#createlineargradient)** API.<br>- When the type is **CanvasPattern**, this attribute indicates a pattern, which is created using the **[createPattern](#createpattern)** API.<br>Since API version 9, this API is supported in ArkTS widgets.|
| [lineCap](#linecap) | CanvasLineCap | Style of the line endpoints. The options are as follows:<br>- **butt**: The endpoints of the line are squared off.<br>- **round**: The endpoints of the line are rounded.<br>- **square**: The endpoints of the line are squared off, and each endpoint has added a rectangle whose length is the same as the line thickness and whose width is half of the line thickness.<br>- Default value: **'butt'**<br>Since API version 9, this API is supported in ArkTS widgets.|
| [lineJoin](#linejoin) | CanvasLineJoin | Style of the shape used to join line segments. The options are as follows:<br>- **round**: The intersection is a sector, whose radius at the rounded corner is equal to the line width.<br>- **bevel**: The intersection is a triangle. The rectangular corner of each line is independent.<br>- **miter**: The intersection has a miter corner by extending the outside edges of the lines until they meet. You can view the effect of this attribute in **miterLimit**.<br>- Default value: **'miter'**<br>Since API version 9, this API is supported in ArkTS widgets.|
| [miterLimit](#miterlimit) | number | Maximum miter length. The miter length is the distance between the inner corner and the outer corner where two lines meet.<br>- Default value: **10**<br>Since API version 9, this API is supported in ArkTS widgets.|
| [font](#font) | string | Font style.<br>Syntax: ctx.font='font-size font-family'<br>- (Optional) **font-size**: font size and row height. The unit can only be pixels.<br>(Optional) **font-family**: font family.<br>Syntax: ctx.font='font-style font-weight font-size font-family'<br>- (Optional) **font-style**: font style. Available values are **normal** and **italic**.<br>- (Optional) **font-weight**: font weight. Available values are as follows: **normal**, **bold**, **bolder**, **lighter**, **100**, **200**, **300**, **400**, **500**, **600**, **700**, **800**, **900**.<br>- (Optional) **font-size**: font size and row height. The unit can only be pixels.<br>- (Optional) **font-family**: font family. Available values are **sans-serif**, **serif**, and **monospace**.<br>Default value: **'normal normal 14px sans-serif'**<br>Since API version 9, this API is supported in ArkTS widgets.|
| [textAlign](#textalign) | CanvasTextAlign | Text alignment mode. Available values are as follows:<br>- **left**: The text is left-aligned.<br>- **right**: The text is right-aligned.<br>- **center**: The text is center-aligned.<br>- **start**: The text is aligned with the start bound.<br>- **end**: The text is aligned with the end bound.<br>**NOTE**<br><br>In the **ltr** layout mode, the value **'start'** equals **'left'**. In the **rtl** layout mode, the value **'start'** equals **'right'**.<br>- Default value: **'left'**<br>Since API version 9, this API is supported in ArkTS widgets.|
| [textBaseline](#textbaseline) | CanvasTextBaseline | Horizontal alignment mode of text. Available values are as follows:<br>- **alphabetic**: The text baseline is the normal alphabetic baseline.<br>- **top**: The text baseline is on the top of the text bounding box.<br>- **hanging**: The text baseline is a hanging baseline over the text.<br>- **middle**: The text baseline is in the middle of the text bounding box.<br>**'ideographic'**: The text baseline is the ideographic baseline. If a character exceeds the alphabetic baseline, the ideographic baseline is located at the bottom of the excess character.<br>- **bottom**: The text baseline is at the bottom of the text bounding box. Its difference from the ideographic baseline is that the ideographic baseline does not consider letters in the next line.<br>- Default value: **'alphabetic'**<br>Since API version 9, this API is supported in ArkTS widgets.|
| [globalAlpha](#globalalpha) | number | Opacity.<br>**0.0**: completely transparent.<br>**1.0**: completely opaque. |
| [lineDashOffset](#linedashoffset) | number | Offset of the dashed line. The precision is float.<br>- Default value: **0.0** |
| [globalCompositeOperation](#globalcompositeoperation) | string | Composition operation type. Available values are as follows: **'source-over'**, **'source-atop'**, **'source-in'**, **'source-out'**, **'destination-over'**, **'destination-atop'**, **'destination-in'**, **'destination-out'**, **'lighter'**, **'copy'**, and **'xor'**.<br>- Default value: **'source-over'** |
| [shadowBlur](#shadowblur) | number | Blur level during shadow drawing. A larger value indicates a more blurred effect. The precision is float.<br>- Default value: **0.0** |
| [shadowColor](#shadowcolor) | string | Shadow color. |
| [shadowOffsetX](#shadowoffsetx) | number | X-axis shadow offset relative to the original object. |
| [shadowOffsetY](#shadowoffsety) | number | Y-axis shadow offset relative to the original object. |
| [imageSmoothingEnabled](#imagesmoothingenabled) | boolean | Whether to adjust the image smoothness during image drawing. The value **true** means to enable this feature, and **false** means the opposite.<br>- Default value: **true** |
| [lineDashOffset](#linedashoffset) | number | Offset of the dashed line. The precision is float.<br>- Default value: **0.0**<br>Since API version 9, this API is supported in ArkTS widgets.|
| [globalCompositeOperation](#globalcompositeoperation) | string | Composition operation type. Available values are as follows: **'source-over'**, **'source-atop'**, **'source-in'**, **'source-out'**, **'destination-over'**, **'destination-atop'**, **'destination-in'**, **'destination-out'**, **'lighter'**, **'copy'**, and **'xor'**.<br>- Default value: **'source-over'**<br>Since API version 9, this API is supported in ArkTS widgets.|
| [shadowBlur](#shadowblur) | number | Blur level during shadow drawing. A larger value indicates a more blurred effect. The precision is float.<br>- Default value: **0.0**<br>Since API version 9, this API is supported in ArkTS widgets.|
| [shadowColor](#shadowcolor) | string | Shadow color.<br>Since API version 9, this API is supported in ArkTS widgets.|
| [shadowOffsetX](#shadowoffsetx) | number | X-axis shadow offset relative to the original object.<br>Since API version 9, this API is supported in ArkTS widgets.|
| [shadowOffsetY](#shadowoffsety) | number | Y-axis shadow offset relative to the original object.<br>Since API version 9, this API is supported in ArkTS widgets.|
| [imageSmoothingEnabled](#imagesmoothingenabled) | boolean | Whether to adjust the image smoothness during image drawing. The value **true** means to enable this feature, and **false** means the opposite.<br>- Default value: **true**<br>Since API version 9, this API is supported in ArkTS widgets.|
> **NOTE**
> For **fillStyle**, **shadowColor**, and **strokeStyle**, the value format of the string type is 'rgb(255, 255, 255)', 'rgba(255, 255, 255, 1.0)', '\#FFFFFF'.
......@@ -66,7 +68,7 @@ struct FillStyleExample {
.backgroundColor('#ffff00')
.onReady(() =>{
this.offContext.fillStyle = '#0000ff'
this.offContext.fillRect(20, 160, 150, 100)
this.offContext.fillRect(20, 20, 150, 100)
var image = this.offContext.transferToImageBitmap()
this.context.transferFromImageBitmap(image)
})
......@@ -700,6 +702,8 @@ fillRect(x: number, y: number, w: number, h: number): void
Fills a rectangle on the canvas.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -747,6 +751,8 @@ strokeRect(x: number, y: number, w: number, h: number): void
Draws an outlined rectangle on the canvas.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -794,6 +800,8 @@ clearRect(x: number, y: number, w: number, h: number): void
Clears the content in a rectangle on the canvas.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -843,6 +851,8 @@ fillText(text: string, x: number, y: number, maxWidth?: number): void
Draws filled text on the canvas.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -891,6 +901,8 @@ strokeText(text: string, x: number, y: number): void
Draws a text stroke on the canvas.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -939,6 +951,8 @@ measureText(text: string): TextMetrics
Returns a **TextMetrics** object used to obtain the width of specified text.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -948,8 +962,8 @@ Returns a **TextMetrics** object used to obtain the width of specified text.
**Return value**
| Type | Description |
| ----------- | ------- |
| TextMetrics | **TextMetrics** object.|
| ----------- | ------------------------------------------------------------ |
| TextMetrics | **TextMetrics** object.<br>Since API version 9, this API is supported in ArkTS widgets.|
**TextMetrics** attributes
......@@ -1009,6 +1023,8 @@ stroke(path?: Path2D): void
Strokes a path.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -1058,6 +1074,8 @@ beginPath(): void
Creates a drawing path.
Since API version 9, this API is supported in ArkTS widgets.
**Example**
```ts
......@@ -1101,6 +1119,8 @@ moveTo(x: number, y: number): void
Moves a drawing path to a target position on the canvas.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -1149,6 +1169,8 @@ lineTo(x: number, y: number): void
Connects the current point to a target position using a straight line.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -1197,6 +1219,8 @@ closePath(): void
Draws a closed path.
Since API version 9, this API is supported in ArkTS widgets.
**Example**
```ts
......@@ -1240,12 +1264,14 @@ createPattern(image: ImageBitmap, repetition: string | null): CanvasPattern | nu
Creates a pattern for image filling based on a specified source image and repetition mode.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
| ---------- | ---------------------------------------- | ---- | ---- | ---------------------------------------- |
| image | [ImageBitmap](ts-components-canvas-imagebitmap.md) | Yes | null | Source image. For details, see **ImageBitmap**. |
| repetition | string | Yes | "" | Repetition mode. The value can be **"repeat"**, **"repeat-x"**, **"repeat-y"**, or **"no-repeat"**.|
| repetition | string | Yes | "" | Repetition mode. The value can be **'repeat'**, **'repeat-x'**, **'repeat-y'**, **'no-repeat'**, **'clamp'**, or **'mirror'**.|
**Return value**
......@@ -1294,6 +1320,8 @@ bezierCurveTo(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number,
Draws a cubic bezier curve on the canvas.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -1346,6 +1374,8 @@ quadraticCurveTo(cpx: number, cpy: number, x: number, y: number): void
Draws a quadratic curve on the canvas.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -1396,6 +1426,8 @@ arc(x: number, y: number, radius: number, startAngle: number, endAngle: number,
Draws an arc on the canvas.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -1447,6 +1479,8 @@ arcTo(x1: number, y1: number, x2: number, y2: number, radius: number): void
Draws an arc based on the radius and points on the arc.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -1497,6 +1531,8 @@ ellipse(x: number, y: number, radiusX: number, radiusY: number, rotation: number
Draws an ellipse in the specified rectangular region on the canvas.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -1549,6 +1585,8 @@ rect(x: number, y: number, w: number, h: number): void
Creates a rectangle on the canvas.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -1597,6 +1635,8 @@ fill(fillRule?: CanvasFillRule): void
Fills the area inside a closed path on the canvas.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -1638,6 +1678,8 @@ fill(path: Path2D, fillRule?: CanvasFillRule): void
Fills the area inside a closed path on the canvas.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -1695,6 +1737,8 @@ clip(fillRule?: CanvasFillRule): void
Sets the current path to a clipping path.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -1741,6 +1785,8 @@ clip(path:Path2D, fillRule?: CanvasFillRule): void
Sets a closed path to a clipping path.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -1752,9 +1798,9 @@ Sets a closed path to a clipping path.
```ts
// xxx.ets
@Entry
@Component
struct Clip {
@Entry
@Component
struct Clip {
private settings: RenderingContextSettings = new RenderingContextSettings(true)
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
private offContext: OffscreenCanvasRenderingContext2D = new OffscreenCanvasRenderingContext2D(600, 600, this.settings)
......@@ -1784,7 +1830,7 @@ struct Clip {
.width('100%')
.height('100%')
}
}
}
```
![en-us_image_000000127777779](figures/en-us_image_000000127777779.png)
......@@ -1797,6 +1843,8 @@ filter(filter: string): void
Sets a filter for the image on the canvas. This API is a void API.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -1810,6 +1858,8 @@ getTransform(): Matrix2D
Obtains the current transformation matrix being applied to the context. This API is a void API.
Since API version 9, this API is supported in ArkTS widgets.
### resetTransform
......@@ -1817,6 +1867,8 @@ resetTransform(): void
Resets the current transform to the identity matrix. This API is a void API.
Since API version 9, this API is supported in ArkTS widgets.
### direction
......@@ -1824,6 +1876,8 @@ direction(direction: CanvasDirection): void
Sets the text direction for drawing text. This API is a void API.
Since API version 9, this API is supported in ArkTS widgets.
### rotate
......@@ -1831,6 +1885,8 @@ rotate(angle: number): void
Rotates a canvas clockwise around its coordinate axes.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -1876,6 +1932,8 @@ scale(x: number, y: number): void
Scales the canvas based on scale factors.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -1924,6 +1982,8 @@ transform(a: number, b: number, c: number, d: number, e: number, f: number): voi
Defines a transformation matrix. To transform a graph, you only need to set parameters of the matrix. The coordinates of the graph are multiplied by the matrix values to obtain new coordinates of the transformed graph. You can use the matrix to implement multiple transform effects.
Since API version 9, this API is supported in ArkTS widgets.
> **NOTE**
>
> The following formulas calculate coordinates of the transformed graph. **x** and **y** represent coordinates before transformation, and **x'** and **y'** represent coordinates after transformation.
......@@ -1988,6 +2048,8 @@ setTransform(a: number, b: number, c: number, d: number, e: number, f: number):
Resets the existing transformation matrix and creates a new transformation matrix by using the same parameters as the **transform()** API.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -2041,6 +2103,8 @@ translate(x: number, y: number): void
Moves the origin of the coordinate system.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -2092,6 +2156,8 @@ drawImage(image: ImageBitmap | PixelMap, sx: number, sy: number, sw: number, sh:
Draws an image on the canvas.
Since API version 9, this API is supported in ArkTS widgets, except that **PixelMap** objects are not supported.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -2145,6 +2211,8 @@ createImageData(sw: number, sh: number): ImageData
Creates an **[ImageData](ts-components-canvas-imagedata.md)** object with the specified dimensions.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -2157,6 +2225,8 @@ createImageData(imageData: ImageData): ImageData
Creates an **[ImageData](ts-components-canvas-imagedata.md)** object by copying an existing **ImageData** object.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -2190,6 +2260,27 @@ Obtains the **[PixelMap](../apis/js-apis-image.md#pixelmap7)** object created wi
| ---------------------------------------- | ------------ |
| [PixelMap](../apis/js-apis-image.md#pixelmap7) | **PixelMap** object.|
### setPixelMap
setPixelMap(value?: PixelMap): void
Draws the input [PixelMap](../apis/js-apis-image.md#pixelmap7) object on the canvas.
**Parameters**
| Parameters | Type | Mandatory | Default Value | Description |
| ---- | ------ | ---- | ---- | --------------- |
| sx | number | Yes | 0 | X-coordinate of the upper left corner of the output area.|
| sy | number | Yes | 0 | Y-coordinate of the upper left corner of the output area.|
| sw | number | Yes | 0 | Width of the output area. |
| sh | number | Yes | 0 | Height of the output area. |
**Return value**
| Type | Description |
| ---------------------------------------- | ------------ |
| [PixelMap](../apis/js-apis-image.md#pixelmap7) | **PixelMap** object.|
### getImageData
......@@ -2197,6 +2288,8 @@ getImageData(sx: number, sy: number, sw: number, sh: number): ImageData
Obtains the **[ImageData](ts-components-canvas-imagedata.md)** object created with the pixels within the specified area on the canvas.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -2217,9 +2310,9 @@ Obtains the **[ImageData](ts-components-canvas-imagedata.md)** object created wi
```ts
// xxx.ets
@Entry
@Component
struct GetImageData {
@Entry
@Component
struct GetImageData {
private settings: RenderingContextSettings = new RenderingContextSettings(true)
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
private offContext: OffscreenCanvasRenderingContext2D = new OffscreenCanvasRenderingContext2D(600, 600, this.settings)
......@@ -2242,7 +2335,7 @@ struct GetImageData {
.width('100%')
.height('100%')
}
}
}
```
![en-us_image_000000127777780](figures/en-us_image_000000127777780.png)
......@@ -2256,6 +2349,8 @@ putImageData(imageData: Object, dx: number, dy: number, dirtyX: number, dirtyY:
Puts an **[ImageData](ts-components-canvas-imagedata.md)** object onto a rectangular area on the canvas.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -2311,6 +2406,8 @@ setLineDash(segments: number[]): void
Sets the dash line style.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Description |
......@@ -2320,9 +2417,9 @@ Sets the dash line style.
**Example**
```ts
@Entry
@Component
struct SetLineDash {
@Entry
@Component
struct SetLineDash {
private settings: RenderingContextSettings = new RenderingContextSettings(true)
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
private offContext: OffscreenCanvasRenderingContext2D = new OffscreenCanvasRenderingContext2D(600, 600, this.settings)
......@@ -2344,7 +2441,7 @@ struct SetLineDash {
.width('100%')
.height('100%')
}
}
}
```
![en-us_image_000000127777772](figures/en-us_image_000000127777772.png)
......@@ -2355,6 +2452,8 @@ getLineDash(): number[]
Obtains the dash line style.
Since API version 9, this API is supported in ArkTS widgets.
**Return value**
| Type | Description |
......@@ -2365,9 +2464,9 @@ Obtains the dash line style.
```ts
// xxx.ets
@Entry
@Component
struct OffscreenCanvasGetLineDash {
@Entry
@Component
struct OffscreenCanvasGetLineDash {
@State message: string = 'Hello World'
private settings: RenderingContextSettings = new RenderingContextSettings(true)
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
......@@ -2400,7 +2499,7 @@ struct OffscreenCanvasGetLineDash {
}
.height('100%')
}
}
}
```
![en-us_image_000000127777778](figures/en-us_image_000000127777778.png)
......@@ -2430,10 +2529,10 @@ Since API version 9, this API is supported in ArkTS widgets.
**Example**
```ts
// xxx.ets
@Entry
@Component
struct ToDataURL {
// xxx.ets
@Entry
@Component
struct ToDataURL {
private settings: RenderingContextSettings = new RenderingContextSettings(true)
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
private offContext: OffscreenCanvasRenderingContext2D = new OffscreenCanvasRenderingContext2D(600, 600, this.settings)
......@@ -2451,7 +2550,7 @@ struct ToDataURL {
.width('100%')
.height('100%')
}
}
}
```
......@@ -2461,6 +2560,8 @@ imageSmoothingQuality(quality: imageSmoothingQuality)
Sets the quality of image smoothing. This API is a void API.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Description |
......@@ -2524,13 +2625,15 @@ restore(): void
Restores the saved drawing context.
Since API version 9, this API is supported in ArkTS widgets.
**Example**
```ts
// xxx.ets
@Entry
@Component
struct CanvasExample {
@Entry
@Component
struct CanvasExample {
private settings: RenderingContextSettings = new RenderingContextSettings(true)
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
private offContext: OffscreenCanvasRenderingContext2D = new OffscreenCanvasRenderingContext2D(600, 600, this.settings)
......@@ -2554,7 +2657,7 @@ struct CanvasExample {
.width('100%')
.height('100%')
}
}
}
```
![en-us_image_000000127777781](figures/en-us_image_000000127777781.png)
......@@ -2565,13 +2668,15 @@ save(): void
Saves the current drawing context.
Since API version 9, this API is supported in ArkTS widgets.
**Example**
```ts
// xxx.ets
@Entry
@Component
struct CanvasExample {
@Entry
@Component
struct CanvasExample {
private settings: RenderingContextSettings = new RenderingContextSettings(true)
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
private offContext: OffscreenCanvasRenderingContext2D = new OffscreenCanvasRenderingContext2D(600, 600, this.settings)
......@@ -2595,7 +2700,7 @@ struct CanvasExample {
.width('100%')
.height('100%')
}
}
}
```
![en-us_image_000000127777781](figures/en-us_image_000000127777781.png)
......@@ -2606,6 +2711,8 @@ createLinearGradient(x0: number, y0: number, x1: number, y1: number): void
Creates a linear gradient.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -2638,7 +2745,7 @@ Creates a linear gradient.
grad.addColorStop(0.5, '#ffffff')
grad.addColorStop(1.0, '#00ff00')
this.offContext.fillStyle = grad
this.offContext.fillRect(0, 0, 500, 500)
this.offContext.fillRect(0, 0, 400, 400)
var image = this.offContext.transferToImageBitmap()
this.context.transferFromImageBitmap(image)
})
......@@ -2658,6 +2765,8 @@ createRadialGradient(x0: number, y0: number, r0: number, x1: number, y1: number,
Creates a linear gradient.
Since API version 9, this API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Mandatory | Default Value | Description |
......@@ -2692,7 +2801,7 @@ Creates a linear gradient.
grad.addColorStop(0.5, '#ffffff')
grad.addColorStop(1.0, '#00ff00')
this.offContext.fillStyle = grad
this.offContext.fillRect(0, 0, 500, 500)
this.offContext.fillRect(0, 0, 440, 440)
var image = this.offContext.transferToImageBitmap()
this.context.transferFromImageBitmap(image)
})
......@@ -2709,3 +2818,5 @@ Creates a linear gradient.
## CanvasPattern
Defines an object created using the **[createPattern](#createpattern)** API.
Since API version 9, this API is supported in ArkTS widgets.
......@@ -207,7 +207,7 @@ XComponent({ id: 'xcomponentId1', type: 'surface', libraryname: 'nativerender' }
- **context** parameter: where the native API exposed on the module is mounted. Its usage is similar to the usage of the **context2** instance obtained after the module is directly loaded using **import context2 from "libnativerender.so"**.
- Time sequence: When the **onLoad** event is subject to the surface. The following figure shows the time sequence of the **onLoad** event and the **OnSurfaceCreated** event on the native side.
![onLoad] (figures /onLoad.png)
![onLoad](figures /onLoad.png)
- onDestroy event
Trigger time: when the **\<XComponent>** is destroyed, in the same manner as that when an ArkUI component is destroyed. The following figure shows the time sequence of the **onDestroy** event and the **OnSurfaceDestroyed** event on the native side.
......
......@@ -9,7 +9,7 @@
- Default focus
After an application opens or switches to a page, the first focusable component (if any) in the component tree of the page is the default focus. You can [customize the default focus](#customizing-default-focus) as needed.
After an application opens or switches to a page, the first focusable component (if any) in the component tree of the page is the default focus. You can [set the default focus](#setting-default-focus) as needed.
- Focused
......@@ -619,7 +619,7 @@ tabIndex(index: number)
Use **tabIndex** to set the order for sequential Tab navigation. The default value is **0**. In Tab navigation, where Tab/Shift+Tab is used (the arrow keys do not affect the navigation), the focus system automatically obtains all components whose **tabIndex** is greater than 0 and moves focus in ascending or descending order.
Take the example provided by [defaultFocus] (#setting-default-focus) as an example. The default order for sequential focus navigation is as follows:
Take the example provided by [defaultFocus](#setting-default-focus) as an example. The default order for sequential focus navigation is as follows:
![en-us_image_0000001511421364](figures/en-us_image_0000001511421364.gif)
......
# Using the Animation Feature
The animation feature provides component animation and inter-page animation, and opens the animation capability APIs for [interpolation calculation](../reference/apis/js-apis-curve.md) and [matrix transformation](../reference/apis/js-apis-matrix4.md), allowing you to design animation effects to a great extent.
This section describes the following animation effects:
1. Animation on the splash screen: fade-in and fade-out of the logo.
2. Transition animation of shared elements on the food list page and food details page.
## AnimateTo for Splash Screen Animation
Component animations include attribute animations and **animateTo** explicit animations.
1. Attribute animation: animation for the universal attribute changes of a component.
2. Explicit animation: animation for a component changing from state A to state B, including the style, location information, and node addition and deletion. You only need to specify the start and end states, without the need for paying attention to the change process. The **animateTo** class also provides the callback for the playback state change events, which is an enhancement and encapsulation of the attribute animation.
The splash screen animation refers to the fade-in and fade-out of the logo. After the animation is complete, the food list page is displayed. The following describes how to use **animateTo** to implement the splash screen animation.
1. Configure the splash screen animation to automatically play. The expected effect is as follows: **animateTo** automatically starts to play the animation once the logo page is displayed. Call the **onAppear** API of **Shape** to set the explicit animation.
```ts
Shape() {
...
}
.onAppear(() => {
animateTo()
})
```
2. Create member variables of **opacity** and **scale** values and decorate them using the **@State** decorator, which indicates that the data is stateful and its change will trigger page refresh.
```ts
@Entry
@Component
struct Logo {
@State private opacityValue: number = 0
@State private scaleValue: number = 0
build() {
Shape() {
...
}
.scale({ x: this.scaleValue, y: this.scaleValue })
.opacity(this.opacityValue)
.onAppear(() => {
animateTo()
})
}
}
```
3. Sets the **animateTo** animation curve. The acceleration curve of the logo is slow first and then fast. The Bezier curve cubicBezier, **cubicBezier(0.4, 0, 1, 1)** is used.
Before using interpolation calculation in the **animateTo**, you must import the **Curves** module.
```ts
import Curves from '@ohos.curves'
```
The **@ohos.curves** module provides the linear curve. For initialization function of the linear, step, cubicBezier, and spring interpolation curves, an interpolation curve object can be created based on the input parameters.
```ts
@Entry
@Component
struct Logo {
@State private opacityValue: number = 0
@State private scaleValue: number = 0
private curve1 = Curves.cubicBezier(0.4, 0, 1, 1)
build() {
Shape() {
...
}
.scale({ x: this.scaleValue, y: this.scaleValue })
.opacity(this.opacityValue)
.onAppear(() => {
animateTo({
curve: this.curve1
})
})
}
}
```
4. Set the animation duration to 1s, set the delay to 0.1s, and set the closure function for displaying animation events. That is, set **opacityValue** and **scaleValue** to change from 0 to 1 from the start point to the end point. In this way, the logo fades in and out.
```ts
@Entry
@Component
struct Logo {
@State private opacityValue: number = 0
@State private scaleValue: number = 0
private curve1 = Curves.cubicBezier(0.4, 0, 1, 1)
build() {
Shape() {
...
}
.scale({ x: this.scaleValue, y: this.scaleValue })
.opacity(this.opacityValue)
.onAppear(() => {
animateTo({
duration: 1000,
curve: this.curve1,
delay: 100,
}, () => {
this.opacityValue = 1
this.scaleValue = 1
})
})
}
}
```
5. After the splash screen animation plays for 1 second, the **FoodCategoryList** page is displayed. Set the **onFinish** callback of **animateTo**. Invoke the **setTimeout** API of the timer. After a delay of 1s, call **router.replaceUrl** to display the **FoodCategoryList** page.
```ts
import router from '@ohos.router'
@Entry
@Component
struct Logo {
@State private opacityValue: number = 0
@State private scaleValue: number = 0
private curve1 = Curves.cubicBezier(0.4, 0, 1, 1)
build() {
Shape() {
...
}
.scale({ x: this.scaleValue, y: this.scaleValue })
.opacity(this.opacityValue)
.onAppear(() => {
animateTo({
duration: 1000,
curve: this.curve1,
delay: 100,
onFinish: () => {
setTimeout(() => {
router.replaceUrl({ url: "pages/FoodCategoryList" })
}, 1000);
}
}, () => {
this.opacityValue = 1
this.scaleValue = 1
})
})
}
}
```
The code is as follows:
```ts
import Curves from '@ohos.curves'
import router from '@ohos.router'
@Entry
@Component
struct Logo {
@State private opacityValue: number = 0
@State private scaleValue: number = 0
private curve1 = Curves.cubicBezier(0.4, 0, 1, 1)
private pathCommands1: string = 'M319.5 128.1 c103.5 0 187.5 84 187.5 187.5 v15 a172.5 172.5 0 0 3 -172.5 172.5 H198 a36 36 0 0 3 -13.8 -1 207 207 0 0 0 87 -372 h48.3 z'
private pathCommands2: string = 'M270.6 128.1 h48.6 c51.6 0 98.4 21 132.3 54.6 a411 411 0 0 3 -45.6 123 c-25.2 45.6 -56.4 84 -87.6 110.4 a206.1 206.1 0 0 0 -47.7 -288 z'
build() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Shape() {
Path()
.commands('M162 128.7 a222 222 0 0 1 100.8 374.4 H198 a36 36 0 0 3 -36 -36')
.fill(Color.White)
.stroke(Color.Transparent)
Path()
.commands(this.pathCommands1)
.fill('none')
.stroke(Color.Transparent)
.linearGradient(
{
angle: 30,
colors: [["#C4FFA0", 0], ["#ffffff", 1]]
})
.clip(new Path().commands(this.pathCommands1))
Path()
.commands(this.pathCommands2)
.fill('none')
.stroke(Color.Transparent)
.linearGradient(
{
angle: 50,
colors: [['#8CC36A', 0.1], ["#B3EB90", 0.4], ["#ffffff", 0.7]]
})
.clip(new Path().commands(this.pathCommands2))
}
.height('630px')
.width('630px')
.scale({ x: this.scaleValue, y: this.scaleValue })
.opacity(this.opacityValue)
.onAppear(() => {
animateTo({
duration: 1000,
curve: this.curve1,
delay: 100,
onFinish: () => {
setTimeout(() => {
router.replaceUrl({ url: "pages/FoodCategoryList" })
}, 1000);
}
}, () => {
this.opacityValue = 1
this.scaleValue = 1
})
})
Text('Healthy Diet')
.fontSize(26)
.fontColor(Color.White)
.margin({ top: 300 })
Text('Healthy life comes from a balanced diet')
.fontSize(17)
.fontColor(Color.White)
.margin({ top: 4 })
}
.width('100%')
.height('100%')
.linearGradient(
{
angle: 180,
colors: [['#BDE895', 0.1], ["#95DE7F", 0.6], ["#7AB967", 1]]
})
}
}
```
![animation-feature](figures/animation-feature.gif)
## Page Transition Animation
Implement the shared element transition between the food list page and the food details page. That is, after you click **FoodListItem** or **FoodGridItem**, the food thumbnail is zoomed in, and then you are redirected to the large image on the food details page.
1. Set the **sharedTransition** method for the **<Image>** component of **FoodListItem** and **FoodGridItem**. The transition ID is **foodItem.id**, the duration of the transition animation is 1s, and the delay is 0.1s. The change curve is **Curves.cubicBezier(0.2, 0.2, 0.1, 1.0)**. You need to import the **Curves** module first.
During the shared element transition, the attributes of the current element are carried. Therefore, create a **<Row>** component as the parent component of the **<Image>** component, and set the background color on the **<Row>** component.
Set **autoResize** to **false** for the **<Image>** component of **FoodListItem**. The **<Image>** component adjusts the size of the image source based on the final display area by default to optimize the image rendering performance. In the transition animation, the image will be reloaded during the zoom-in process. Therefore, to ensure the smoothness of the transition animation, set **autoResize** to **false**.
```ts
// FoodList.ets
import Curves from '@ohos.curves'
@Component
struct FoodListItem {
private foodItem: FoodData
build() {
Navigator({ target: 'pages/FoodDetail' }) {
Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
Row() {
Image(this.foodItem.image)
.objectFit(ImageFit.Contain)
.autoResize(false)
.height(40)
.width(40)
.sharedTransition(this.foodItem.id, { duration: 1000, curve: Curves.cubicBezier(0.2, 0.2, 0.1, 1.0), delay: 100 })
}
.margin({ right: 16 })
Text(this.foodItem.name)
.fontSize(14)
.flexGrow(1)
Text(this.foodItem.calories + ' kcal')
.fontSize(14)
}
.height(64)
}
.params({ foodData: this.foodItem })
.margin({ right: 24, left:32 })
}
}
@Component
struct FoodGridItem {
private foodItem: FoodData
build() {
Column() {
Row() {
Image(this.foodItem.image)
.objectFit(ImageFit.Contain)
.autoResize(false)
.height(152)
.width('100%')
.sharedTransition(this.foodItem.id, { duration: 1000, curve: Curves.cubicBezier(0.2, 0.2, 0.1, 1.0), delay: 100 })
}
Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
Text(this.foodItem.name)
.fontSize(14)
.flexGrow(1)
.padding({ left: 8 })
Text(this.foodItem.calories + 'kcal')
.fontSize(14)
.margin({ right: 6 })
}
.height(32)
.width('100%')
.backgroundColor('#FFe5e5e5')
}
.height(184)
.width('100%')
.onClick(() => {
router.pushUrl({ url: 'pages/FoodDetail', params: { foodData: this.foodItem } })
})
}
}
```
2. Sets the **sharedTransition** method for the **<Image>** component of **FoodImageDisplay** on the **FoodDetail** page. The setting method is the same as that mentioned above.
```ts
import Curves from '@ohos.curves'
@Component
struct FoodImageDisplay {
private foodItem: FoodData
build() {
Stack({ alignContent: Alignment.BottomStart }) {
Image(this.foodItem.image)
.objectFit(ImageFit.Contain)
.sharedTransition(this.foodItem.id, { duration: 1000, curve: Curves.cubicBezier(0.2, 0.2, 0.1, 1.0), delay: 100 })
Text(this.foodItem.name)
.fontSize(26)
.fontWeight(500)
.margin({ left: 26, bottom: 17.4 })
}
.height(357)
}
}
```
![animation-feature1](figures/animation-feature1.gif)
Now we have completed the drawing of the startup logo, splash screen animation, and transition animation between pages. By applying and combining the various animation APIs in the declarative development framework, you can create a more immersive experience for your applications.
# Building a Food Category Grid Layout
The diet application allows food on the home page to display in list or grid mode. You can implement switching between food categories through tabs in grid mode.
1. Import the **Category** enumeration type to the **FoodCategoryList** page.
```ts
import { Category, FoodData } from '../model/FoodData'
```
2. Create the **FoodCategoryList** and **FoodCategory** components. The **FoodCategoryList** component is used as the entry component of the new page, and the **initializeOnStartup** method is invoked in the entry component.
```ts
@Component
struct FoodList {
private foodItems: FoodData[]
build() {
......
}
}
@Component
struct FoodCategory {
private foodItems: FoodData[]
build() {
......
}
}
@Entry
@Component
struct FoodCategoryList {
private foodItems: FoodData[] = initializeOnStartup()
build() {
......
}
}
```
3. Create the **showList** member variable in the **FoodCategoryList** component to control the rendering switchover between the list layout and grid layout. The conditional rendering statement **if...else...** is required.
```ts
@Entry
@Component
struct FoodCategoryList {
private foodItems: FoodData[] = initializeOnStartup()
private showList: boolean = false
build() {
Stack() {
if (this.showList) {
FoodList({ foodItems: this.foodItems })
} else {
FoodCategory({ foodItems: this.foodItems })
}
}
}
}
```
4. In the upper right corner of the page, create an icon for switching between the list and grid layouts. Set the stack alignment mode to **TopEnd**, top-bottom alignment. Create an image component, and set the click event, that is, negation of **showList**.
```ts
@Entry
@Component
struct FoodCategoryList {
private foodItems: FoodData[] = initializeOnStartup()
private showList: boolean = false
build() {
Stack({ alignContent: Alignment.TopEnd }) {
if (this.showList) {
FoodList({ foodItems: this.foodItems })
} else {
FoodCategory({ foodItems: this.foodItems })
}
Image($r('app.media.Switch'))
.height(24)
.width(24)
.margin({ top: 15, right: 10 })
.onClick(() => {
this.showList = !this.showList
})
}.height('100%')
}
}
```
5. Add the **@State** decorator. After you click the switch tab in the upper right corner, the page does not change. This is because the **showList** does not have state data and its change does not trigger the page refresh. You need to add the **@State** decorator to make it state data. The change of the **@State** decorator will cause re-rendering of the component where the decorator is located.
```ts
@Entry
@Component
struct FoodCategoryList {
private foodItems: FoodData[] = initializeOnStartup()
@State private showList: boolean = false
build() {
Stack({ alignContent: Alignment.TopEnd }) {
if (this.showList) {
FoodList({ foodItems: this.foodItems })
} else {
FoodCategory({ foodItems: this.foodItems })
}
Image($r('app.media.Switch'))
.height(24)
.width(24)
.margin({ top: 15, right: 10 })
.onClick(() => {
this.showList = !this.showList
})
}.height('100%')
}
}
```
When you click the switch icon, the **FoodList** component is displayed. When you click the switch icon again, the **FoodList** component is hidden.
![en-us_image_0000001170411978](figures/en-us_image_0000001170411978.gif)
6. Create a tab to display all food categories (**All**). Create the **\<Tabs>** component and its child component **TabContent** in the **FoodCategory** component, and set **tabBar** to **All**. Set the width of the **TabBars** to 280 and the layout mode to **Scrollable**. This means that the **TabBars** can be scrolled when the total length exceeds 280. The **\<Tabs>** component is a container component that allows users to switch between content views through tabs. Each tab page corresponds to a **TabContent**.
```ts
@Component
struct FoodCategory {
private foodItems: FoodData[]
build() {
Stack() {
Tabs() {
TabContent() {}.tabBar('All')
}
.barWidth(280)
.barMode(BarMode.Scrollable)
}
}
}
```
![en-us_image_0000001204538065](figures/en-us_image_0000001204538065.png)
7. Create the **FoodGrid** component to function as a child component of the **TabContent** component.
```ts
@Component
struct FoodGrid {
private foodItems: FoodData[]
build() {}
}
@Component
struct FoodCategory {
private foodItems: FoodData[]
build() {
Stack() {
Tabs() {
TabContent() {
FoodGrid({ foodItems: this.foodItems })
}.tabBar('All')
}
.barWidth(280)
.barMode(BarMode.Scrollable)
}
}
}
```
8. Implement a 2 x 6 grid layout (12 food data resources in total). Create a **Grid** component, and set **columnsTemplate** to **('1fr 1fr')**, **rowsTemplate** to **('1fr 1fr 1fr 1fr 1fr 1fr')**, and both **rowsGap** and **columnsGap** to **8**. Create a **Scroll** component so that it can be slid.
```ts
@Component
struct FoodGrid {
private foodItems: FoodData[]
build() {
Scroll() {
Grid() {
ForEach(this.foodItems, (item: FoodData) => {
GridItem() {}
}, (item: FoodData) => item.id.toString())
}
.rowsTemplate('1fr 1fr 1fr 1fr 1fr 1fr')
.columnsTemplate('1fr 1fr')
.columnsGap(8)
.rowsGap(8)
}
.scrollBar(BarState.Off)
.padding({left: 16, right: 16})
}
}
```
9. Create a **FoodGridItem** component to display the food image, name, and calories and implement the UI layout. The **FoodGridItem** component is a child component of the **GridItem** component. The height of each **FoodGridItem** is **184**, and the line spacing is **8**. The total height of the **Grid** component is calculated as follows: (184 + 8) x 6 – 8 = 1144.
```ts
@Component
struct FoodGridItem {
private foodItem: FoodData
build() {
Column() {
Row() {
Image(this.foodItem.image)
.objectFit(ImageFit.Contain)
.height(152)
.width('100%')
}
Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
Text(this.foodItem.name)
.fontSize(14)
.flexGrow(1)
.padding({ left: 8 })
Text(this.foodItem.calories + 'kcal')
.fontSize(14)
.margin({ right: 6 })
}
.height(32)
.width('100%')
.backgroundColor('#FFe5e5e5')
}
.height(184)
.width('100%')
}
}
@Component
struct FoodGrid {
private foodItems: FoodData[]
build() {
Scroll() {
Grid() {
ForEach(this.foodItems, (item: FoodData) => {
GridItem() {
FoodGridItem({foodItem: item})
}
}, (item: FoodData) => item.id.toString())
}
.rowsTemplate('1fr 1fr 1fr 1fr 1fr 1fr')
.columnsTemplate('1fr 1fr')
.columnsGap(8)
.rowsGap(8)
.height(1144)
}
.scrollBar(BarState.Off)
.padding({ left: 16, right: 16 })
}
}
```
![en-us_image_0000001170167520](figures/en-us_image_0000001170167520.png)
10. Create the **Category.Vegetable**, **Category.Fruit**, **Category.Nut**, **Category.SeaFood**, and **Category.Dessert** tabs.
```ts
@Component
struct FoodCategory {
private foodItems: FoodData[]
build() {
Stack() {
Tabs() {
TabContent() {
FoodGrid({ foodItems: this.foodItems })
}.tabBar('All')
TabContent() {
FoodGrid({ foodItems: this.foodItems.filter(item => (item.category === Category.Vegetable)) })
}.tabBar('Vegetable')
TabContent() {
FoodGrid({ foodItems: this.foodItems.filter(item => (item.category === Category.Fruit)) })
}.tabBar('Fruit')
TabContent() {
FoodGrid({ foodItems: this.foodItems.filter(item => (item.category === Category.Nut)) })
}.tabBar('Nut')
TabContent() {
FoodGrid({ foodItems: this.foodItems.filter(item => (item.category === Category.Seafood)) })
}.tabBar('Seafood')
TabContent() {
FoodGrid({ foodItems: this.foodItems.filter(item => (item.category === Category.Dessert)) })
}.tabBar('Dessert')
}
.barWidth(280)
.barMode(BarMode.Scrollable)
}
}
}
```
11. Set the number of rows and height of grids for different food categories. Because the number of foods varies according to the category, the **''1fr 1fr 1fr 1fr 1fr 1fr '** constant cannot be used to set the number of rows to 6.
Create member variables **gridRowTemplate** and **HeightValue**, and set the number of grid rows and height by using these member variables.
```ts
@Component
struct FoodGrid {
private foodItems: FoodData[]
private gridRowTemplate: string = ''
private heightValue: number
build() {
Scroll() {
Grid() {
ForEach(this.foodItems, (item: FoodData) => {
GridItem() {
FoodGridItem({ foodItem: item })
}
}, (item: FoodData) => item.id.toString())
}
.rowsTemplate(this.gridRowTemplate)
.columnsTemplate('1fr 1fr')
.columnsGap(8)
.rowsGap(8)
.height(this.heightValue)
}
.scrollBar(BarState.Off)
.padding({ left: 16, right: 16 })
}
}
```
Invoke the **aboutToAppear** API to calculate the number of rows (**gridRowTemplate**) and height (**heightValue**).
```ts
aboutToAppear() {
var rows = Math.round(this.foodItems.length / 2);
this.gridRowTemplate = '1fr '.repeat(rows);
this.heightValue = rows * 192 - 8;
}
```
The custom component provides two lifecycle callbacks: **aboutToAppear** and **aboutToDisappear**. **aboutToAppear** is executed after the custom component is created and before the **build** method of the custom component is executed. **aboutToDisappear** is executed before the custom component is destroyed.
![en-us_image_0000001215113569](figures/en-us_image_0000001215113569.png)
```ts
@Component
struct FoodGrid {
private foodItems: FoodData[]
private gridRowTemplate: string = ''
private heightValue: number
aboutToAppear() {
var rows = Math.round(this.foodItems.length / 2);
this.gridRowTemplate = '1fr '.repeat(rows);
this.heightValue = rows * 192 - 8;
}
build() {
Scroll() {
Grid() {
ForEach(this.foodItems, (item: FoodData) => {
GridItem() {
FoodGridItem({ foodItem: item })
}
}, (item: FoodData) => item.id.toString())
}
.rowsTemplate(this.gridRowTemplate)
.columnsTemplate('1fr 1fr')
.columnsGap(8)
.rowsGap(8)
.height(this.heightValue)
}
.scrollBar(BarState.Off)
.padding({ left: 16, right: 16 })
}
}
```
![en-us_image_0000001170008198](figures/en-us_image_0000001170008198.gif)
# Building a Food Category List Layout
Use the **\<List>** component and **ForEach** loop to build the food category list layout.
1. Create a page file named **FoodCategoryList.ets** in the **pages** directory and rename the **index.ets** file **FoodDetail.ets**.
2. Create a **\<List>** component named **FoodList** as the page entry point. Then, add a **\<ListItem>** component named **FoodListItem** as its child component. The **\<List>** component is used to display data of the same type. Its child component **\<ListItem>** is used to display specific items in the list.
```ts
@Component
struct FoodListItem {
build() {}
}
@Entry
@Component
struct FoodList {
build() {
List() {
ListItem() {
FoodListItem()
}
}
}
}
```
3. Import the **FoodData** class and **initializeOnStartup** method.
There are two file access methods in application code:
- Use a relative path to reference the code file: Use **"../"** for referencing the parent directory and **"./"** (which can also be omitted) for referencing the current directory.
- Use the absolute path, which is the root path of the current module, to reference the code file, for example, **common/utils/utils**.
In this example, a relative path is used for access.
```
import { FoodData } from '../model/FoodData'
import { initializeOnStartup } from '../model/FoodDataModels'
```
4. Configure the **FoodList** and **FoodListItem** components to pass values. Create a member variable named **foodItems** of the **FoodData[]** type in the **FoodList** component and invoke the **initializeOnStartup** method to assign a value to the variable. Create a member variable **foodItem** of the **FoodData** type in the **FoodListItem** component. Pass the **foodItems[0]** of the first element in the parent **foodItems** array as a parameter to **FoodListItem**.
```ts
import { FoodData } from '../model/FoodData'
import { initializeOnStartup } from '../model/FoodDataModels'
@Component
struct FoodListItem {
private foodItem: FoodData
build() {}
}
@Entry
@Component
struct FoodList {
private foodItems: FoodData[] = initializeOnStartup()
build() {
List() {
ListItem() {
FoodListItem({ foodItem: this.foodItems[0] })
}
}
}
}
```
5. Declare the UI layout of the **FoodListItem** child component. Create a **\<Flex>** component, including the food image thumbnail, food name, and calories in the food.
```ts
import { FoodData } from '../model/FoodData'
import { initializeOnStartup } from '../model/FoodDataModels'
@Component
struct FoodListItem {
private foodItem: FoodData
build() {
Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
Image(this.foodItem.image)
.objectFit(ImageFit.Contain)
.height(40)
.width(40)
.margin({ right: 16 })
Text(this.foodItem.name)
.fontSize(14)
.flexGrow(1)
Text(this.foodItem.calories + ' kcal')
.fontSize(14)
}
.height(64)
.margin({ right: 24, left:32 })
}
}
@Entry
@Component
struct FoodList {
private foodItems: FoodData[] = initializeOnStartup()
build() {
List() {
ListItem() {
FoodListItem({ foodItem: this.foodItems[0] })
}
}
}
}
```
![en-us_image_0000001204776353](figures/en-us_image_0000001204776353.png)
6. Create two **FoodListItem** objects. Create two **FoodListItem** objects in the **List** component and pass the first element **this.foodItems[0]** and the second element **foodItem: this.foodItems[1]** to the **FoodListItem**.
```ts
import { FoodData } from '../model/FoodData'
import { initializeOnStartup } from '../model/FoodDataModels'
@Component
struct FoodListItem {
private foodItem: FoodData
build() {
Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
Image(this.foodItem.image)
.objectFit(ImageFit.Contain)
.height(40)
.width(40)
.margin({ right: 16 })
Text(this.foodItem.name)
.fontSize(14)
.flexGrow(1)
Text(this.foodItem.calories + ' kcal')
.fontSize(14)
}
.height(64)
.margin({ right: 24, left: 32 })
}
}
@Entry
@Component
struct FoodList {
private foodItems: FoodData[] = initializeOnStartup()
build() {
List() {
ListItem() {
FoodListItem({ foodItem: this.foodItems[0] })
}
ListItem() {
FoodListItem({ foodItem: this.foodItems[1] })
}
}
}
}
```
![en-us_image1_0000001204776353](figures/en-us_image1_0000001204776353.png)
7. Import [ForEach](../quick-start/arkts-rendering-control.md#loop-rendering) so that you do not need to create **FoodListItem** objects one by one.
```ts
import { FoodData } from '../model/FoodData'
import { initializeOnStartup } from '../model/FoodDataModels'
@Component
struct FoodListItem {
private foodItem: FoodData
build() {
Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
Image(this.foodItem.image)
.objectFit(ImageFit.Contain)
.height(40)
.width(40)
.margin({ right: 16 })
Text(this.foodItem.name)
.fontSize(14)
.flexGrow(1)
Text(this.foodItem.calories + ' kcal')
.fontSize(14)
}
.height(64)
.margin({ right: 24, left:32 })
}
}
@Entry
@Component
struct FoodList {
private foodItems: FoodData[] = initializeOnStartup()
build() {
List() {
ForEach(this.foodItems, item => {
ListItem() {
FoodListItem({ foodItem: item })
}
}, item => item.id.toString())
}
}
}
```
8. Add a title for the **FoodList**.
```
@Entry
@Component
struct FoodList {
private foodItems: FoodData[] = initializeOnStartup()
build() {
Column() {
Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
Text('Food List')
.fontSize(20)
.margin({ left: 20 })
}
.height('7%')
.backgroundColor('#FFf1f3f5')
List() {
ForEach(this.foodItems, item => {
ListItem() {
FoodListItem({ foodItem: item })
}
}, item => item.id.toString())
}
.height('93%')
}
}
}
```
![en-us_image_0000001169678922](figures/en-us_image_0000001169678922.png)
# Building a Food Data Model
In real-world development, it is impractical to describe all aspects of food in code, including the food name and nutrition facts. This is where the food data model comes into the picture. With the food data model, you can store and manage data in a unified manner.
![en-us_image_0000001267767897](figures/en-us_image_0000001267767897.png)
1. Create a folder named **model** and create a file named **FoodData.ets** therein.
![en-us_image_0000001223127760](figures/en-us_image_0000001223127760.png)
2. Define a food data storage model, **FoodData**, and an enum variable, **Category**. The **FoodData** class contains **id**, **name**, **category**, **image**, **calories**, **protein**, **fat**, **carbohydrates**, and **vitaminC** attributes.
The ArkTS programming language is an extension of the TS language and also supports the TS syntax.
```ts
enum Category {
Fruit,
Vegetable,
Nut,
Seafood,
Dessert
}
let NextId = 0;
class FoodData {
id: string;
name: string;
image: Resource;
category: Category;
calories: number;
protein: number;
fat: number;
carbohydrates: number;
vitaminC: number;
constructor(name: string, image: Resource, category: Category, calories: number, protein: number, fat: number, carbohydrates: number, vitaminC: number) {
this.id = `${ NextId++ }`;
this.name = name;
this.image = image;
this.category = category;
this.calories = calories;
this.protein = protein;
this.fat = fat;
this.carbohydrates = carbohydrates;
this.vitaminC = vitaminC;
}
}
```
3. Store food images in the **resources** > **base** > **media** directory. Use food names as the image names.
4. Create food resource data. Create **FoodDataModels.ets** in the **model** folder and declare the food composition array **FoodComposition** on the page. The following example creates two pieces of food data.
```ts
const FoodComposition: any[] = [
{ 'name': 'Tomato', 'image': $r('app.media.Tomato'), 'category': Category.Vegetable, 'calories': 17, 'protein': 0.9, 'fat': 0.2, 'carbohydrates': 3.9, 'vitaminC': 17.8 },
{ 'name': 'Walnut', 'image': $r('app.media.Walnut'), 'category': Category.Nut, 'calories': 654 , 'protein': 15, 'fat': 65, 'carbohydrates': 14, 'vitaminC': 1.3 }
]
```
In real-world development, you can customize more data resources when needed. Use [Lazy Loading](../quick-start/arkts-rendering-control.md#lazy-loading) to load data if a large amount of food data is involved.
5. Create the **initializeOnStartUp** method to initialize the **FoodData** array. Export the **FoodData** class from **FoodData.ets**, and import **FoodData** and **Category** in **FoodDataModels.ets**.
```ts
// FoodData.ets
export enum Category {
......
}
export class FoodData {
......
}
// FoodDataModels.ets
import { Category, FoodData } from './FoodData'
export function initializeOnStartup(): Array<FoodData> {
let FoodDataArray: Array<FoodData> = []
FoodComposition.forEach(item => {
FoodDataArray.push(new FoodData(item.name, item.image, item.category, item.calories, item.protein, item.fat, item.carbohydrates, item.vitaminC ));
})
return FoodDataArray;
}
```
The data resources for the diet application are now ready. You can continue to create a food category list by loading the data.
# Common Components
Components are the core of a UI page. Each component can provide visible and interactive functional units that are independent from each other. This is achieved by data and method encapsulation. You can use and reuse any component anywhere as needed.
The table below lists the components available in the declarative development paradigm.
| Component Type | Components |
| ---------------------------- | ---------------------------------------------------------- |
| [Basic components](../reference/arkui-ts/ts-basic-components-blank.md) | Blank, Button, Checkbox, CheckboxGroup, DataPanel, DatePicker, Divider, Gauge, Image, ImageAnimator, LoadingProgress, Marquee, Navigation, PatternLock, PluginComponent, Progress, QRCode, Radio, Rating, RemoteWindow, RichText, ScrollBar, Search, Select, Slider, Span, Stepper, StepperItem, Text, TextArea, TextClock, TextInput, TextPicker, TextTimer, TimePicker, Toggle, Web, XComponent |
| [Container components](../reference/arkui-ts/ts-container-ability-component.md) | AbilityComponent, AlphabetIndexer, Badge, Column, ColumnSplit, Counter, Flex, FlowItem, GridContainer, GridCol, GridRow, Grid, GridItem, List, ListItem, ListItemGroup, Navigator, Panel, Refresh, RelativeContainer, Row, RowSplit, Scroll, SideBarContainer, Stack, Swiper, Tabs, TabContent |
| [Media components](../reference/arkui-ts/ts-media-components-video.md)| Video |
| [Drawing components](../reference/arkui-ts/ts-drawing-components-circle.md)| Circle, Ellipse, Line, Polyline, Polygon, Path, Rect, Shape |
| [Canvas components](../reference/arkui-ts/ts-components-canvas-canvas.md)| Canvas |
# Web Component Development
The **\<Web>** component can be used to display web pages. For details, see [Web](../reference/arkui-ts/ts-basic-components-web.md).
## Creating a Component
Create a **\<Web>** component in the .ets file under **main/ets/MainAbility/pages**. Then, in the created component, use **src** to specify the web page URI to be referenced, and bind a controller to the component to call the component APIs.
```ts
// xxx.ets
import web_webview from '@ohos.web.webview';
@Entry
@Component
struct WebComponent {
controller: web_webview.WebviewController = new web_webview.WebviewController();
build() {
Column() {
Web({ src: 'https://www.example.com', controller: this.controller })
}
}
}
```
## Setting Styles and Attributes
When using the **\<Web>** component, you must specify the styles and attributes. The sample code is as follows.
```ts
// xxx.ets
import web_webview from '@ohos.web.webview';
@Entry
@Component
struct WebComponent {
fileAccess: boolean = true;
controller: web_webview.WebviewController = new web_webview.WebviewController();
build() {
Column() {
Text('Hello world!')
.fontSize(20)
Web({ src: 'https://www.example.com', controller: this.controller })
// Set the height and padding.
.height(500)
.padding(20)
// Set the file access permission and script execution permission.
.fileAccess(this.fileAccess)
.javaScriptAccess(true)
Text('End')
.fontSize(20)
}
}
}
```
## Adding Events and Methods
Add the **onProgressChange** event for the **\<Web>** component, which is triggered when the loading progress changes and returns the progress value in its callback. Assign the progress value to the **\<Progress>** component to control the status of the component. When the progress reaches 100%, the **\<Progress>** component is hidden, and the web page loading is complete.
```ts
// xxx.ets
import web_webview from '@ohos.web.webview';
@Entry
@Component
struct WebComponent {
@State progress: number = 0;
@State hideProgress: boolean = true;
fileAccess: boolean = true;
controller: web_webview.WebviewController = new web_webview.WebviewController();
build() {
Column() {
Text('Hello world!')
.fontSize(20)
Progress({value: this.progress, total: 100})
.color('#0000ff')
.visibility(this.hideProgress ? Visibility.None : Visibility.Visible)
Web({ src: 'https://www.example.com', controller: this.controller })
.fileAccess(this.fileAccess)
.javaScriptAccess(true)
.height(500)
.padding(20)
// Add an onProgressChange event.
.onProgressChange(e => {
this.progress = e.newProgress;
// When the progress reaches 100%, the progress bar disappears.
if (this.progress === 100) {
this.hideProgress = true;
} else {
this.hideProgress = false;
}
})
Text('End')
.fontSize(20)
}
}
}
```
Add the **runJavaScript** method to the **onPageEnd** event. The **onPageEnd** event is triggered when the web page finishes loading. In this case, the **runJavaScript** method can be used to execute JavaScript scripts in the HTML file. In the sample code below, when the web page finishes loading, the **onPageEnd** event is triggered to call the **test** method in the HTML file and print information on the console.
```ts
// xxx.ets
import web_webview from '@ohos.web.webview';
@Entry
@Component
struct WebComponent {
@State progress: number = 0;
@State hideProgress: boolean = true;
@State webResult: string = ''
fileAccess: boolean = true;
// Define the controller for the \<Web> component.
controller: web_webview.WebviewController = new web_webview.WebviewController();
build() {
Column() {
Text('Hello world!')
.fontSize(20)
Progress({value: this.progress, total: 100})
.color('#0000ff')
.visibility(this.hideProgress ? Visibility.None : Visibility.Visible)
// Initialize the \<Web> component and bind it to the controller.
Web({ src: $rawfile('index.html'), controller: this.controller })
.fileAccess(this.fileAccess)
.javaScriptAccess(true)
.height(500)
.padding(20)
.onProgressChange(e => {
this.progress = e.newProgress;
if (this.progress === 100) {
this.hideProgress = true;
} else {
this.hideProgress = false;
}
})
.onPageEnd(e => {
// test() is defined in index.html.
try {
this.controller.runJavaScript(
'test()',
(error, result) => {
if (error) {
console.info(`run JavaScript error: ` + JSON.stringify(error))
return;
}
if (result) {
this.webResult = result
console.info(`The test() return value is: ${result}`)
}
});
console.info('url: ', e.url);
} catch (error) {
console.error(`ErrorCode: ${error.code}, Message: ${error.message}`);
}
})
Text('End')
.fontSize(20)
}
}
}
```
Create an HTML file in **main/resources/rawfile**.
```html
<!-- index.html -->
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<body>
Hello world!
</body>
<script type="text/javascript">
function test() {
console.info('Ark Web Component');
}
</script>
</html>
```
## Enabling Web Debugging
To debug web pages in the **\<Web>** component on a device, connect the device to a PC through the USB port, and then set the **webDebuggingAccess** attribute of the **\<Web>** component to **true** and enable port forwarding on the PC.
The configuration procedure is as follows:
1. Set the **webDebuggingAccess** attribute of the **\<Web>** component to **true**.
```ts
// xxx.ets
import web_webview from '@ohos.web.webview';
@Entry
@Component
struct WebComponent {
controller: web_webview.WebviewController = new web_webview.WebviewController();
build() {
Column() {
Web({ src: 'www.example.com', controller: this.controller })
.webDebuggingAccess(true) // true means to enable web debugging.
}
}
}
```
2. Enable port forwarding on the PC and add a mapping for TCP port 9222.
```ts
hdc fport tcp:9222 tcp:9222
```
You can run the following command to view the existing mapping table:
```ts
hdc fport ls
```
When you are done, you can debug a web page as follows: On the target device, open the **\<Web>** component in the application and access the web page to debug; on the PC, use the Chrome browser to access **http://localhost:9222** and start to debug the web page.
## Scenario Example
In this example, you'll implement a **\<Web>** component where videos can be played dynamically. Embed a video resource into an HTML page, and then use the **\<Web>** component controller to invoke the **onActive** and **onInactive** methods to activate and pause page rendering, respectively. Upon clicking of the **onInactive** button, the **\<Web>** component stops rendering and the video playback pauses. Upon clicking of the **onActive** button, the **\<Web>** component is activated and the video playback resumes.
```ts
// xxx.ets
import web_webview from '@ohos.web.webview';
@Entry
@Component
struct WebComponent {
controller: web_webview.WebviewController = new web_webview.WebviewController();
build() {
Column() {
Row() {
Button('onActive').onClick(() => {
console.info("Web Component onActive");
try {
this.controller.onActive();
} catch (error) {
console.error(`Errorcode: ${error.code}, Message: ${error.message}`);
}
})
Button('onInactive').onClick(() => {
console.info("Web Component onInactive");
try {
this.controller.onInactive();
} catch (error) {
console.error(`Errorcode: ${error.code}, Message: ${error.message}`);
}
})
}
Web({ src: $rawfile('index.html'), controller: this.controller })
.fileAccess(true)
}
}
}
```
```html
<!-- index.html -->
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<body>
<video width="320" height="240" controls="controls" muted="muted" autoplay="autoplay">
<!-- The test.mp4 video file is stored in main/resources/rawfile. -->
<source src="test.mp4" type="video/mp4">
</video>
</body>
</html>
```
# Creating a Simple Page
In this section, we will develop an infographic food details page, by building custom components through the container components **\<Stack>** and **\<Flex>** as well as basic components **\<Image>** and **\<Text>**.
Before creating a page, create an ArkTS project. For details, see [Creating an ArkTS Project in Stage Model](../quick-start/start-with-ets-stage.md#creating-an-arkts-project) or [Creating an ArkTS Project in FA Model](../quick-start/start-with-ets-fa.md#creating-an-arkts-project).
## Building the Stack Layout
1. Create a food name.
In the **index.ets** file, create a **\<Stack>** component, and place the **\<Text>** component in the braces of the **\<Stack>** component. When the **\<Stack>** component contains multiple child components, the latter child component overwrites the former one.
```ts
@Entry
@Component
struct MyComponent {
build() {
Stack() {
Text('Tomato')
.fontSize(26)
.fontWeight(500)
}
}
}
```
![en-us_image_0000001214128687](figures/en-us_image_0000001214128687.png)
2. Display food pictures.
Create an **\<Image>** component and specify a URL for it. To display the **\<Text>** component above the **\<Image>** component, declare the **\<Image>** component first. Image resources are stored in the **rawfile** folder in **resources**. When referencing the resources in the **rawfile** folder, use the `$rawfile('filename')` format, where **filename** indicates the relative path of the file in the **rawfile** folder. `$rawfile` only allows the **\<Image>** component to reference image resources.
```ts
@Entry
@Component
struct MyComponent {
build() {
Stack() {
Image($rawfile('Tomato.png'))
Text('Tomato')
.fontSize(26)
.fontWeight(500)
}
}
}
```
![en-us_image_0000001168410342](figures/en-us_image_0000001168410342.png)
3. Access images through resources.
In addition to specifying the image path, you can also use the media resource symbol **$r** to reference resources based on the resource qualifier rules in the **resources** folder. Right-click the **resources** folder, choose **New** > **Resource Directory** from the shortcut menu, and set **Resource Type** to **Media** (image resource).
Place **Tomato.png** in the **media** folder. You can then reference the application resources in the `$r('app.type.name')` format, which is `$r('app.media.Tomato')` in this example.
```ts
@Entry
@Component
struct MyComponent {
build() {
Stack() {
Image($r('app.media.Tomato'))
.objectFit(ImageFit.Contain)
.height(357)
Text('Tomato')
.fontSize(26)
.fontWeight(500)
}
}
}
```
4. Set the width and height of the image, and set the **objectFit** attribute of the image to **ImageFit.Contain**, which means to keep the aspect ratio of the image to ensure that the image is completely displayed within the boundary.
If the image fills the entire screen, the possible causes are as follows:
1. The width and height of the image are not set.
2. The default attribute of **objectFit** of the image is **ImageFit.Cover**, that is, the image is zoomed in or zoomed out to fill the entire display boundary with the aspect ratio locked.
```ts
@Entry
@Component
struct MyComponent {
build() {
Stack() {
Image($r('app.media.Tomato'))
.objectFit(ImageFit.Contain)
.height(357)
Text('Tomato')
.fontSize(26)
.fontWeight(500)
}
}
}
```
![en-us_image_0000001214210217](figures/en-us_image_0000001214210217.png)
5. Set the food image and name layout. Set the alignment mode of the stack to bottom alignment. By default, the stack is center aligned. Set **alignContent** to **Alignment.BottomStart**. Similar to **FontWeight**, **Alignment** is a built-in enumeration type provided by the framework.
```ts
@Entry
@Component
struct MyComponent {
build() {
Stack({ alignContent: Alignment.BottomStart }) {
Image($r('app.media.Tomato'))
.objectFit(ImageFit.Contain)
.height(357)
Text('Tomato')
.fontSize(26)
.fontWeight(500)
}
}
}
```
![en-us_image_0000001168728872](figures/en-us_image_0000001168728872.png)
6. Adjust the left and bottom margin of the **\<Text>** component. **margin** is a shorthand attribute. You can specify the margins of the four edges in a unified manner or separately.
1. To set the margins of the four edges in a unified manner, use the **Margin(Length)** format. For example, **margin(20)** indicates that the margins of the top, right, bottom, and left edges are all 20.
2. To set the margins of the four edges separately, use the **{top?: Length, right?: Length, bottom?: Length, left?:Length}** format. For example, **margin({ left: 26, bottom: 17.4 })** indicates that the left margin is 26 and the bottom margin is 17.4.
```ts
@Entry
@Component
struct MyComponent {
build() {
Stack({ alignContent: Alignment.BottomStart }) {
Image($r('app.media.Tomato'))
.objectFit(ImageFit.Contain)
.height(357)
Text('Tomato')
.fontSize(26)
.fontWeight(500)
.margin({left: 26, bottom: 17.4})
}
}
}
```
![en-us_image_0000001213968747](figures/en-us_image_0000001213968747.png)
7. Adjust the structure between components and semanticize component names. Create the **FoodDetail** page entry component, create a **\<Column>** component in **FoodDetail**, and set the alignment to **alignItems(HorizontalAlign.Center)**. Change the name of the **MyComponent** component to **FoodImageDisplay**, which is a child component of the **FoodDetail** component.
The **\<Column>** component is a container whose child components are vertically arranged. It is in linear layout in essence. Therefore, only the alignment in the cross axis direction can be set.
```ts
@Component
struct FoodImageDisplay {
build() {
Stack({ alignContent: Alignment.BottomStart }) {
Image($r('app.media.Tomato'))
.objectFit(ImageFit.Contain)
Text('Tomato')
.fontSize(26)
.fontWeight(500)
.margin({ left: 26, bottom: 17.4 })
}
.height(357)
}
}
@Entry
@Component
struct FoodDetail {
build() {
Column() {
FoodImageDisplay()
}
.alignItems(HorizontalAlign.Center)
}
}
```
## Building the Flex Layout
Use the **Flex** layout to build a food composition table. In this way, cell sizes are flexibly set based on the proportion, eliminating the need for width and height calculation.
1. Create a **ContentTable** component as a child component of the **FoodDetail** component.
```ts
@Component
struct FoodImageDisplay {
build() {
Stack({ alignContent: Alignment.BottomStart }) {
Image($r('app.media.Tomato'))
.objectFit(ImageFit.Contain)
.height(357)
Text('Tomato')
.fontSize(26)
.fontWeight(500)
.margin({ left: 26, bottom: 17.4 })
}
}
}
@Component
struct ContentTable {
build() {
}
}
@Entry
@Component
struct FoodDetail {
build() {
Column() {
FoodImageDisplay()
ContentTable()
}
.alignItems(HorizontalAlign.Center)
}
}
```
2. Create a **\<Flex>** component to display two food composition categories in the tomato: **Calories** and **Nutrition**.
**Calories** contains information about calories. **Nutrition** contains information about protein, fat, carbohydrates, and vitamin C.
Create the **Calories** class. Create a **\<Flex>** component and set its height to **280**, and the top, right, and left margins to **30**. The **Flex** component contains three **\<Text>** child components, which represent the category name (**Calories**), content name (**Calories**), and contain value (**17 kcal**), respectively. By default, child components in the **Flex** component are arranged horizontally.
In the following example, code of **FoodImageDisplay** is omitted, and only code of **ContentTable** is provided.
```ts
@Component
struct ContentTable {
build() {
Flex() {
Text('Calories')
.fontSize(17.4)
.fontWeight(FontWeight.Bold)
Text('Calories')
.fontSize(17.4)
Text('17kcal')
.fontSize(17.4)
}
.height(280)
.padding({ top: 30, right: 30, left: 30 })
}
}
@Entry
@Component
struct FoodDetail {
build() {
Column() {
FoodImageDisplay()
ContentTable()
}
.alignItems(HorizontalAlign.Center)
}
}
```
![en-us_image_0000001169759552](figures/en-us_image_0000001169759552.png)
3. Adjust the layout and set the proportion (**layoutWeight**) of each part. Set **layoutWeight** of the category name to **1**, and **layoutWeight** of the content name and content value to **2**. The content name and content value are in a same **Flex**, and the content name occupies all remaining space **flexGrow(1)**.
```ts
@Component
struct FoodImageDisplay {
build() {
Stack({ alignContent: Alignment.BottomStart }) {
Image($r('app.media.Tomato'))
.objectFit(ImageFit.Contain)
.height(357)
Text('Tomato')
.fontSize(26)
.fontWeight(500)
.margin({ left: 26, bottom: 17.4 })
}
}
}
@Component
struct ContentTable {
build() {
Flex() {
Text('Calories')
.fontSize(17.4)
.fontWeight(FontWeight.Bold)
.layoutWeight(1)
Flex() {
Text('Calories')
.fontSize(17.4)
.flexGrow(1)
Text('17kcal')
.fontSize(17.4)
}
.layoutWeight(2)
}
.height(280)
.padding({ top: 30, right: 30, left: 30 })
}
}
@Entry
@Component
struct FoodDetail {
build() {
Column() {
FoodImageDisplay()
ContentTable()
}
.alignItems(HorizontalAlign.Center)
}
}
```
![en-us_image_0000001215079443](figures/en-us_image_0000001215079443.png)
4. Create the **Nutrient** class in a similar process. **Nutrition** consists of four parts: **Protein**, **Fat**, **Carbohydrates**, and **VitaminC**. The names of the last three parts are omitted in the table and represented by spaces.
Set **FlexDirection.Column**, **FlexAlign.SpaceBetween**, and **ItemAlign.Start**.
```ts
@Component
struct ContentTable {
build() {
Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Start }) {
Flex() {
Text('Calories')
.fontSize(17.4)
.fontWeight(FontWeight.Bold)
.layoutWeight(1)
Flex() {
Text('Calories')
.fontSize(17.4)
.flexGrow(1)
Text('17kcal')
.fontSize(17.4)
}
.layoutWeight(2)
}
Flex() {
Text('Nutrition')
.fontSize(17.4)
.fontWeight(FontWeight.Bold)
.layoutWeight(1)
Flex() {
Text('Protein')
.fontSize(17.4)
.flexGrow(1)
Text('0.9g')
.fontSize(17.4)
}
.layoutWeight(2)
}
Flex() {
Text(' ')
.fontSize(17.4)
.fontWeight(FontWeight.Bold)
.layoutWeight(1)
Flex() {
Text('Fat')
.fontSize(17.4)
.flexGrow(1)
Text('0.2g')
.fontSize(17.4)
}
.layoutWeight(2)
}
Flex() {
Text(' ')
.fontSize(17.4)
.fontWeight(FontWeight.Bold)
.layoutWeight(1)
Flex() {
Text('Carbohydrates')
.fontSize(17.4)
.flexGrow(1)
Text('3.9g')
.fontSize(17.4)
}
.layoutWeight(2)
}
Flex() {
Text(' ')
.fontSize(17.4)
.fontWeight(FontWeight.Bold)
.layoutWeight(1)
Flex() {
Text('vitaminC')
.fontSize(17.4)
.flexGrow(1)
Text('17.8mg')
.fontSize(17.4)
}
.layoutWeight(2)
}
}
.height(280)
.padding({ top: 30, right: 30, left: 30 })
}
}
@Entry
@Component
struct FoodDetail {
build() {
Column() {
FoodImageDisplay()
ContentTable()
}
.alignItems(HorizontalAlign.Center)
}
}
```
![en-us_image_0000001215199399](figures/en-us_image_0000001215199399.png)
5. Use the custom constructor **\@Builder** to simplify the code. It can be found that the food groups in each food composition table are actually of the same UI structure.
![en-us_image_0000001169599582](figures/en-us_image_0000001169599582.png)
Currently, all food groups are declared, resulting in code duplication and redundancy. You can use **\@Builder** to build a custom method and abstract the same UI structure declaration. The **\@Builder** decorated method and the **build** method for the **@Component** decorated component are used to declare some UI rendering structures and comply with the same ArkTS syntax. You can define one or more methods decorated by **\@Builder**, but a component decorated by **@Component** can have only one **build** method.
Declare the **IngredientItem** method decorated by **\@Builder** in **ContentTable** to declare the UI descriptions for the category name, content name, and content value.
```ts
@Component
struct ContentTable {
@Builder IngredientItem(title:string, name: string, value: string) {
Flex() {
Text(title)
.fontSize(17.4)
.fontWeight(FontWeight.Bold)
.layoutWeight(1)
Flex({ alignItems: ItemAlign.Center }) {
Text(name)
.fontSize(17.4)
.flexGrow(1)
Text(value)
.fontSize(17.4)
}
.layoutWeight(2)
}
}
}
```
When the **IngredientItem** API is called in the **build** method of **ContentTable**, **this** needs to be used to invoke the method in the scope of the component to distinguish the global method call.
```ts
@Component
struct ContentTable {
......
build() {
Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Start }) {
this.IngredientItem('Calories', 'Calories', '17kcal')
this.IngredientItem('Nutrition', 'Protein', '0.9g')
this.IngredientItem('', 'Fat', '0.2g')
this.IngredientItem('', 'Carbohydrates', '3.9g')
this.IngredientItem('', 'VitaminC', '17.8mg')
}
.height(280)
.padding({ top: 30, right: 30, left: 30 })
}
}
```
The overall code of the **ContentTable** component is as follows:
```ts
@Component
struct ContentTable {
@Builder IngredientItem(title:string, name: string, value: string) {
Flex() {
Text(title)
.fontSize(17.4)
.fontWeight(FontWeight.Bold)
.layoutWeight(1)
Flex() {
Text(name)
.fontSize(17.4)
.flexGrow(1)
Text(value)
.fontSize(17.4)
}
.layoutWeight(2)
}
}
build() {
Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Start }) {
this.IngredientItem('Calories', 'Calories', '17kcal')
this.IngredientItem('Nutrition', 'Protein', '0.9g')
this.IngredientItem('', 'Fat', '0.2g')
this.IngredientItem('', 'Carbohydrates', '3.9g')
this.IngredientItem('', 'VitaminC', '17.8mg')
}
.height(280)
.padding({ top: 30, right: 30, left: 30 })
}
}
@Entry
@Component
struct FoodDetail {
build() {
Column() {
FoodImageDisplay()
ContentTable()
}
.alignItems(HorizontalAlign.Center)
}
}
```
![en-us_image_0000001215199399](figures/en-us_image_0000001215199399.png)
You've learned how to build a simple food details page. Read on to learn how to define the page layout and connection.
# Custom Component Lifecycle Callbacks
The lifecycle callbacks of a custom component are used to notify users of the lifecycle of the component. These callbacks are private and are invoked by the development framework at a specified time at runtime. They cannot be manually invoked from applications.
> **NOTE**
>
> - Promise and asynchronous callback functions can be used in lifecycle functions, for example, network resource getters and timer setters.
>
> - Do not use **async await** in lifecycle functions.
## aboutToAppear
aboutToAppear?(): void
Invoked after a new instance of the custom component is created and before its **build** function is executed. You can change state variables in the **aboutToAppear** function. The change will take effect when you execute the **build** function next time.
Since API version 9, this API is supported in ArkTS widgets.
## aboutToDisappear
aboutToDisappear?(): void
Invoked before the destructor of the custom component is consumed. Do not change state variables in the **aboutToDisappear** function as doing this can cause unexpected errors. For example, the modification of the **@Link** decorated variable may cause unstable application running.
Since API version 9, this API is supported in ArkTS widgets.
**Example 1:**
```ts
// xxx.ets
@Entry
@Component
struct CountDownTimerComponent {
@State countDownFrom: number = 10
private timerId: number = -1
aboutToAppear(): void {
this.timerId = setInterval(() => {
if (this.countDownFrom <= 1) {
clearTimeout(this.timerId) // Clear the timer when the value of countDownFrom is less than or equal to 1.
}
this.countDownFrom -= 1
}, 1000) // The value of countDownFrom decreases by 1 every second.
}
aboutToDisappear(): void {
if (this.timerId > 0) {
clearTimeout(this.timerId)
this.timerId = -1
}
}
build() {
Column() {
Text(`${this.countDownFrom} sec left`)
.fontSize(30)
.margin(30)
}.width('100%')
}
}
```
![aboutToAppear](figures/aboutToAppear.gif)
The example above shows that lifecycle functions are critical for **CountDownTimerComponent** to manage its timer resources. Similar functions include loading resources asynchronously from the network.
## onPageShow
onPageShow?(): void
Invoked each time when a page is displayed. This callback is used in the routing process or scenarios where the application is switched to the foreground or background. Only the custom components decorated by **@Entry** take effect.
## onPageHide
onPageHide?(): void
Invoked each time when a page is hidden. This callback is used in the routing process or scenarios where the application is switched to the foreground or background. Only the custom components decorated by **@Entry** take effect.
## onBackPress
onBackPress?(): void
Invoked when a user clicks the back button. This callback takes effect only for the custom components decorated by **@Entry**. If **true** is returned, the page processes the return logic and no page routing is performed. If **false** is returned, the default route return logic is used. If the return value is not set, the value **false** is used.
**Example 2:**
```ts
// xxx.ets
@Entry
@Component
struct IndexComponent {
@State textColor: Color = Color.Black
onPageShow() {
this.textColor = Color.Blue
console.info('IndexComponent onPageShow')
}
onPageHide() {
this.textColor = Color.Transparent
console.info('IndexComponent onPageHide')
}
onBackPress() {
this.textColor = Color.Red
console.info('IndexComponent onBackPress')
}
build() {
Column() {
Text('Hello World')
.fontColor(this.textColor)
.fontSize(30)
.margin(30)
}.width('100%')
}
}
```
![lifecycle](figures/lifecycle.PNG)
## onLayout<sup>9+</sup>
onLayout?(children: Array\<LayoutChild\>, constraint: ConstraintSizeOptions): void
Invoked when the custom component lays out its child components. Through this callback the component receives its child component layout information and size constraint from the framework. The state variable cannot be changed in the **onLayout** function.
This API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Description |
| ---------- | -------------------------------------------------------------------------------- | ---------------------- |
| children | Array\<[LayoutChild](#layoutchild9)\> | Child component layout information. |
| constraint | [ConstraintSizeOptions](../reference/arkui-ts/ts-types.md#constraintsizeoptions) | Size constraint information of the parent component.|
## onMeasure<sup>9+</sup>
onMeasure?(children: Array\<LayoutChild\>, constraint: ConstraintSizeOptions): void
Invoked when the custom component needs to determine its size. Through this callback the component receives its child component layout information and size constraint from the framework. The state variable cannot be changed in the onMeasure function.
This API is supported in ArkTS widgets.
**Parameters**
| Name | Type | Description |
| ---------- | -------------------------------------------------------------------------------- | ---------------------- |
| children | Array\<[LayoutChild](#layoutchild9)\> | Child component layout information. |
| constraint | [ConstraintSizeOptions](../reference/arkui-ts/ts-types.md#constraintsizeoptions) | Size constraint information of the parent component.|
## LayoutChild<sup>9+</sup>
Provides the child component layout information.
This API is supported in ArkTS widgets.
| Name | Type | Description |
| ---------- | ----------------------------------------------------------------------------------------------------------- | -------------------------------------- |
| name | string | Name of the child component. |
| id | string | ID of the child component. |
| constraint | [ConstraintSizeOptions](../reference/arkui-ts/ts-types.md#constraintsizeoptions) | Size constraint of the child component. |
| borderInfo | [LayoutBorderInfo](#layoutborderinfo9) | Border information of the child component. |
| position | [Position](../reference/arkui-ts/ts-types.md#position) | Position coordinates of the child component. |
| measure | (childConstraint: [ConstraintSizeOptions](../reference/arkui-ts/ts-types.md#constraintsizeoptions)) => void | Method called to apply the size constraint to the child component.|
| layout | (LayoutInfo: [LayoutInfo](#layoutinfo9)) => void | Method called to apply the layout information to the child component.|
## LayoutBorderInfo<sup>9+</sup>
Provides the border information of the child component.
This API is supported in ArkTS widgets.
| Name | Type | Description |
| ----------- | ---------------------------------------------------------- | ---------------------------------------------- |
| borderWidth | [EdgeWidths](../reference/arkui-ts/ts-types.md#edgewidths) | Edge widths in different directions of the child component.|
| margin | [Margin](../reference/arkui-ts/ts-types.md#margin) | Margin areas in different directions of the child component. |
| padding | [Padding](../reference/arkui-ts/ts-types.md#padding) | Padding areas in different directions of the child component. |
## LayoutInfo<sup>9+</sup>
Provides the layout information of the child component.
This API is supported in ArkTS widgets.
| Name | Type | Description |
| ---------- | -------------------------------------------------------------------------------- | ---------------- |
| position | [Position](../reference/arkui-ts/ts-types.md#position) | Position coordinates of the child component.|
| constraint | [ConstraintSizeOptions](../reference/arkui-ts/ts-types.md#constraintsizeoptions) | Size constraint of the child component.|
Example 3:
```ts
// xxx.ets
@Entry
@Component
struct Index {
build() {
Column() {
CustomLayout() {
ForEach([1, 2, 3], (index) => {
Text('Sub' + index)
.fontSize(30)
.borderWidth(2)
})
}
}
}
}
@Component
struct CustomLayout {
@BuilderParam builder: () => {}
onLayout(children: Array<LayoutChild>, constraint: ConstraintSizeOptions) {
let pos = 0
children.forEach((child) => {
child.layout({ position: { x: pos, y: pos }, constraint: constraint })
pos += 100
})
}
onMeasure(children: Array<LayoutChild>, constraint: ConstraintSizeOptions) {
let size = 100
children.forEach((child) => {
child.measure({ minHeight: size, minWidth: size, maxWidth: size, maxHeight: size })
size += 50
})
}
build() {
this.builder()
}
}
```
![customlayout](figures/customLayout.png)
# Declarative UI Development Guidelines
## Overview
| Task | Description | Related Resources |
| ----------- | ---------------------------------------- | ---------------------------------------- |
| Set up the development environment. | Understand the project structure of the declarative UI.<br>Learn the resource categories and how to access resources. | [OpenHarmony Project Overview](https://developer.harmonyos.com/en/docs/documentation/doc-guides/ohos-project-overview-0000001218440650)<br>[Resource Categories and Access](../quick-start/resource-categories-and-access.md)|
| Learn ArkTS. | As its name implies, ArkTS is a superset of TypeScript. It is the preferred, primary programming language for application development in OpenHarmony.| [Learning ArkTS](../quick-start/arkts-get-started.md)|
| Develop a page. | Select an appropriate layout based on the use case.<br>Add built-in components and set the component style based on the page content to present.<br>Update and diversify the page content.| [Creating a Page](#creating-a-page)<br> [Common Layout Development](ui-ts-layout-linear.md)<br> [Common Components](ui-ts-components-intro.md)<br>[Setting the Component Style](#setting-the-component-styles)<br>[Updating Page Content](#updating-page-content)|
| (Optional) Diversify the page content. | Leverage the drawing and animation features to effectively increase user engagement. | [Drawing Components](../reference/arkui-ts/ts-drawing-components-circle.md)<br>[Canvas Components](../reference/arkui-ts/ts-components-canvas-canvas.md)<br>[Animation](../reference/arkui-ts/ts-animatorproperty.md)|
| (Optional) Implement page redirection.| Use page routing to implement redirection between pages. | [Page Routing](../reference/apis/js-apis-router.md)|
| (Optional) Improve performance. | Learn how you can improve your implementation, thereby avoiding possible performance drop. | [Recommendations for Improving Performance](ui-ts-performance-improvement-recommendation.md)|
## Creating a Page
Select a layout to create a page and add basic built-in components to the page. In this example, the [flex layout](ui-ts-layout-flex.md) is used, and the **\<Text>** component is centered horizontally and vertically on the page.
```ts
// xxx.ets
@Entry
@Component
struct MyComponent {
build() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Text('Hello')
}
.width('100%')
.height('100%')
}
}
```
## Setting the Component Style
If a built-in component does not have attribute methods set, it takes the default style. To change the style of a component and thereby how it looks on the UI, modify the settings of styles, including component-specific styles and [universal styles](../reference/arkui-ts/ts-universal-attributes-size.md).
1. Change the display content of the **\<Text>** component to **Tomato** by modifying its constructor parameters.
2. Set the **fontSize** attribute to **26** and the **fontWeight** attribute to **500**.
```ts
// xxx.ets
@Entry
@Component
struct MyComponent {
build() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Text('Tomato')
.fontSize(26)
.fontWeight(500)
}
.width('100%')
.height('100%')
}
}
```
![en-us_image_0000001168888224](figures/en-us_image_0000001168888224.png)
## Updating Page Content
You can update page content by updating component status. Below is a simple example.
> **NOTE**
>
> Before updating the status of a component, initialize its member variables. The member variables of a custom component can be initialized in either of the following methods: [local initialization](../quick-start/arkts-restrictions-and-extensions.md#initialization-rules-and-restrictions-of-custom-components-member-variables) or [initialization using constructor parameters](../quick-start/arkts-restrictions-and-extensions.md#initialization-rules-and-restrictions-of-custom-components-member-variables). Select the appropriate method based on the decorator used by the variable.
**Example**
```ts
// xxx.ets
@Entry
@Component
struct ParentComp {
@State isCountDown: boolean = true
build() {
Column() {
Text(this.isCountDown ? 'Count Down' : 'Stopwatch').fontSize(20).margin(20)
if (this.isCountDown) {
// The image resources are stored in the media directory.
Image($r("app.media.countdown")).width(120).height(120)
TimerComponent({ counter: 10, changePerSec: -1, showInColor: Color.Red })
} else {
// The image resources are stored in the media directory.
Image($r("app.media.stopwatch")).width(120).height(120)
TimerComponent({ counter: 0, changePerSec: +1, showInColor: Color.Black })
}
Button(this.isCountDown ? 'Switch to Stopwatch' : 'Switch to Count Down')
.onClick(() => {
this.isCountDown = !this.isCountDown
})
}.width('100%')
}
}
// Custom timer/countdown component.
@Component
struct TimerComponent {
@State counter: number = 0
private changePerSec: number = -1
private showInColor: Color = Color.Black
private timerId: number = -1
build() {
Text(`${this.counter}sec`)
.fontColor(this.showInColor)
.fontSize(20)
.margin(20)
}
aboutToAppear() {
this.timerId = setInterval(() => {
this.counter += this.changePerSec
}, 1000)
}
aboutToDisappear() {
if (this.timerId > 0) {
clearTimeout(this.timerId)
this.timerId = -1
}
}
}
```
![component](figures/component.gif)
**Initial creation and rendering:**
1. Create the parent component **ParentComp**.
2. Locally initialize the state variable **isCountDown** of **ParentComp**.
3. Execute the **build** function of **ParentComp**.
4. Create a **\<Column>** component.
1. Create a **\<Text>** component, set the text content, and add the **\<Text>** component instance to the **\<Column>** component.
2. With the **if** statement, create a component under the **true** condition.
1. Create an **\<Image>** component and set the image source address.
2. Create a **TimerComponent** using the given constructor.
3. Create a **\<Button>** component and set the corresponding content.
**Status update:**
When the user clicks a button:
1. The value of the **isCountDown** state variable of **ParentComp** is changed to **false**.
2. The **build** function of **ParentComp** is executed.
3. The **\<Column>** component is reused and reinitialized.
4. The child components of **\<Column>** reuse and reinitialize the objects in the memory.
1. The **\<Text>** component is reused after being re-initialized using the new text content.
2. With the **if** statement, the components under the **false** condition are used.
1. The component instances created under the **true** condition are destroyed.
2. Components under the **false** condition are created.
3. The **\<Button>** component is reused with the new image source.
# Using the Drawing Feature
The drawing feature is provided by the drawing component in the framework, with support for the SVG standard drawing commands.
This section describes how to use the drawing component to draw food composition labels (basic geometric shapes) and apply logos (custom graphics) on the details page.
## Drawing Basic Geometric Shapes
The drawing component encapsulates some common basic geometric shapes, such as rectangles, circles, and ellipses, eliminating the need to calculate routes.
In the food component table on the **FoodDetail** page, each food group is labeled with a round icon.
1. Create a **Circle** component and add a round icon before each food group as a label. Set the circle diameter to 6vp. Modify the **IngredientItem** method in the **ContentTable** component on the **FoodDetail** page by adding **Circle** before the food group name.
```ts
// FoodDetail.ets
@Component
struct ContentTable {
private foodItem: FoodData
@Builder IngredientItem(title:string, colorValue: string, name: string, value: string) {
Flex() {
Text(title)
.fontSize(17.4)
.fontWeight(FontWeight.Bold)
.layoutWeight(1)
Flex({ alignItems: ItemAlign.Center }) {
Circle({width: 6, height: 6})
.margin({right: 12})
.fill(colorValue)
Text(name)
.fontSize(17.4)
.flexGrow(1)
Text(value)
.fontSize(17.4)
}
.layoutWeight(2)
}
}
build() {
......
}
}
```
2. To use different label colors for different food groups, call **IngredientItem** in the **build** method to fill the circles with different colors.
```ts
// FoodDetail.ets
@Component
struct ContentTable {
private foodItem: FoodData
@Builder IngredientItem(title:string, colorValue: string, name: string, value: string) {
Flex() {
Text(title)
.fontSize(17.4)
.fontWeight(FontWeight.Bold)
.layoutWeight(1)
Flex({ alignItems: ItemAlign.Center }) {
Circle({width: 6, height: 6})
.margin({right: 12})
.fill(colorValue)
Text(name)
.fontSize(17.4)
.flexGrow(1)
Text(value)
.fontSize(17.4)
}
.layoutWeight(2)
}
}
build() {
Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Start }) {
this.IngredientItem('Calories', '#FFf54040', 'Calories', this.foodItem.calories + 'kcal')
this.IngredientItem('Nutrition', '#FFcccccc', 'Protein', this.foodItem.protein + 'g')
this.IngredientItem(' ', '#FFf5d640', 'Fat', this.foodItem.fat + 'g')
this.IngredientItem(' ', '#FF9e9eff', 'Carbohydrates', this.foodItem.carbohydrates + 'g')
this.IngredientItem(' ', '#FF53f540', 'VitaminC', this.foodItem.vitaminC + 'mg')
}
.height(280)
.padding({ top: 30, right: 30, left: 30 })
}
}
```
![drawing-feature](figures/drawing-feature.png)
## Drawing Custom Graphs
In addition to drawing basic geometric shapes, you can also use the **\<Path>** component to draw a custom path. The following describes how to draw an application logo.
1. Create the page **Logo.ets** in the **pages** folder.
![drawing-feature1](figures/drawing-feature1.png)
2. Delete the template code from **Logo.ets** and create a logo component.
```ts
@Entry
@Component
struct Logo {
build() {
}
}
```
3. Create a **\<Flex>** component as the root node, set the width and height to 100%, set the alignment mode on the main axis and cross axis to **Center**, and create a **\<Shape>** component as a child component of **\<Flex>**.
The **\<Shape>** component is the parent component of all drawing components. To combine multiple drawing components into a whole, create a **\<Shape>** as their parent component.
Draw a logo with the size of 630 px x 630 px. The declarative development paradigm supports multiple length units. In the preceding sections, **number** is used as a parameter, which means that the default length unit – virtual pixel (vp) is used. The virtual pixel count is related to the device resolution and screen density. For example, if the device resolution is 1176 x 2400 and the reference screen resolution is 3, with the formula of vp = px/resolution, the width of the device screen is 392 vp.
The drawing components follow the SVG standard and use px as the unit by default. Therefore, to facilitate consistency, px is also used as the unit of the logo. The declarative development framework also supports the px unit, with the input parameter of the string type. For example, to set the width to 630px (210vp), use **width('*630px*')** or **width(*210*)**.
```ts
@Entry
@Component
struct Logo {
build() {
Flex({ alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Shape() {
}
.height('630px')
.width('630px')
}
.width('100%')
.height('100%')
}
}
```
4. Fill the page with gradient colors. Set the gradient mode to linear gradient, the offset angle to 180 degrees, and the color stops to #BDE895 -> 95DE7F -> #7AB967 in the ranges of [0, 0.1], (0.1, 0.6], and (0.6, 1], respectively.
```ts
.linearGradient(
{
angle: 180,
colors: [['#BDE895', 0.1], ["#95DE7F", 0.6], ["#7AB967", 1]]
})
```
![drawing-feature2](figures/drawing-feature2.png)
```ts
@Entry
@Component
struct Logo {
build() {
Flex({ alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Shape() {
}
.height('630px')
.width('630px')
}
.width('100%')
.height('100%')
.linearGradient(
{
angle: 180,
colors: [['#BDE895', 0.1], ["#95DE7F", 0.6], ["#7AB967", 1]]
})
}
}
```
![drawing-feature3](figures/drawing-feature3.png)
5. Set the drawing command to draw the first path.
```ts
Path()
.commands('M162 128.7 a222 222 0 0 1 100.8 374.4 H198 a36 36 0 0 3 -36 -36')
```
The **Path** drawing commands comply with the SVG standard. The preceding command can be divided into the following:
```ts
M162 128.7
```
Start a new subpath at point (162, 128.7).
```ts
a222 222 0 0 1 100.8 374.4
```
Draw an elliptical arc, setting **rx** and **ry** to **222**, **x-axis-rotation** to **0**, **large-arc-flag** to **0**, and **sweep-flag** to **1** (which means to draw the arc counterclockwise). The lowercase **a** indicates a relative position. In this example, the end point coordinate is (162 + 100.8 = 262.8, 128.7 + 374.4 = 503.1).
```ts
H198
```
Draw a horizontal line from point (262.8, 503.1) to point (198, 503.1).
```ts
a36 36 0 0 3 -36 -36
```
Draw a circular arc. The input parameters are the same as those in drawing an elliptical arc. The end point is (198 - 36 = 162, 503.1 - 36 = 467.1).
```ts
V128.7
```
Draw a vertical line from point (162, 467.1) to point (162, 128.7).
```ts
z
```
Close the current subpath.
![drawing-feature4](figures/drawing-feature4.png)
In the sample code, the fill color is white and the stroke is transparent.
```ts
.fill(Color.White)
.stroke(Color.Transparent)
```
```ts
@Entry
@Component
struct Logo {
build() {
Flex({ alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Shape() {
Path()
.commands('M162 128.7 a222 222 0 0 1 100.8 374.4 H198 a36 36 0 0 3 -36 -36')
.fill(Color.White)
.stroke(Color.Transparent)
}
.height('630px')
.width('630px')
}
.width('100%')
.height('100%')
.linearGradient(
{
angle: 180,
colors: [['#BDE895', 0.1], ["#95DE7F", 0.6], ["#7AB967", 1]]
})
}
}
```
![drawing-feature5](figures/drawing-feature5.png)
6. Draw the second path. Fill the shape background with gradient colors. Because the gradient colors fill the entire box, you must use the **clip** method with the **Shape** as the input parameter so that the path is clipped.
```ts
Path()
.commands('M319.5 128.1 c103.5 0 187.5 84 187.5 187.5 v15 a172.5 172.5 0 0 3 -172.5 172.5 H198 a36 36 0 0 3 -13.8 -1 207 207 0 0 0 87 -372 h48.3 z')
.fill('none')
.stroke(Corlor.Transparent)
.linearGradient(
{
angle: 30,
colors: [["#C4FFA0", 0], ["#ffffff", 1]]
})
.clip(new Path().commands('M319.5 128.1 c103.5 0 187.5 84 187.5 187.5 v15 a172.5 172.5 0 0 3 -172.5 172.5 H198 a36 36 0 0 3 -13.8 -1 207 207 0 0 0 87 -372 h48.3 z'))
```
The path drawing command is long. You can call the command by using it as a member variable of the component through **this**.
```ts
@Entry
@Component
struct Logo {
private pathCommands1:string = 'M319.5 128.1 c103.5 0 187.5 84 187.5 187.5 v15 a172.5 172.5 0 0 3 -172.5 172.5 H198 a36 36 0 0 3 -13.8 -1 207 207 0 0 0 87 -372 h48.3 z'
build() {
......
Path()
.commands(this.pathCommands1)
.fill('none')
.stroke(Color.Transparent)
.linearGradient(
{
angle: 30,
colors: [["#C4FFA0", 0], ["#ffffff", 1]]
})
.clip(new Path().commands(this.pathCommands1))
......
}
}
```
![drawing-feature6](figures/drawing-feature6.png)
7. Draw the third path.
```ts
@Entry
@Component
struct Logo {
private pathCommands1:string = 'M319.5 128.1 c103.5 0 187.5 84 187.5 187.5 v15 a172.5 172.5 0 0 3 -172.5 172.5 H198 a36 36 0 0 3 -13.8 -1 207 207 0 0 0 87 -372 h48.3 z'
private pathCommands2:string = 'M270.6 128.1 h48.6 c51.6 0 98.4 21 132.3 54.6 a411 411 0 0 3 -45.6 123 c-25.2 45.6 -56.4 84 -87.6 110.4 a206.1 206.1 0 0 0 -47.7 -288 z'
build() {
Flex({ alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Shape() {
Path()
.commands('M162 128.7 a222 222 0 0 1 100.8 374.4 H198 a36 36 0 0 3 -36 -36')
.fill(Color.White)
.stroke(Color.Transparent)
Path()
.commands(this.pathCommands1)
.fill('none')
.stroke(Color.Transparent)
.linearGradient(
{
angle: 30,
colors: [["#C4FFA0", 0], ["#ffffff", 1]]
})
.clip(new Path().commands(this.pathCommands1))
Path()
.commands(this.pathCommands2)
.fill('none')
.stroke(Color.Transparent)
.linearGradient(
{
angle: 50,
colors: [['#8CC36A', 0.1], ["#B3EB90", 0.4], ["#ffffff", 0.7]]
})
.clip(new Path().commands(this.pathCommands2))
}
.height('630px')
.width('630px')
}
.width('100%')
.height('100%')
.linearGradient(
{
angle: 180,
colors: [['#BDE895', 0.1], ["#95DE7F", 0.6], ["#7AB967", 1]]
})
}
}
```
![drawing-feature7](figures/drawing-feature7.png)
The drawing of the application logo is now completed. This logo combines three **\<Path>** components through SVG commands and takes the shape of an artistic leaf.
8. Add the application title and slogan.
```ts
@Entry
@Component
struct Logo {
private pathCommands1: string = 'M319.5 128.1 c103.5 0 187.5 84 187.5 187.5 v15 a172.5 172.5 0 0 3 -172.5 172.5 H198 a36 36 0 0 3 -13.8 -1 207 207 0 0 0 87 -372 h48.3 z'
private pathCommands2: string = 'M270.6 128.1 h48.6 c51.6 0 98.4 21 132.3 54.6 a411 411 0 0 3 -45.6 123 c-25.2 45.6 -56.4 84 -87.6 110.4 a206.1 206.1 0 0 0 -47.7 -288 z'
build() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Shape() {
Path()
.commands('M162 128.7 a222 222 0 0 1 100.8 374.4 H198 a36 36 0 0 3 -36 -36')
.fill(Color.White)
.stroke(Color.Transparent)
Path()
.commands(this.pathCommands1)
.fill('none')
.stroke(Color.Transparent)
.linearGradient(
{
angle: 30,
colors: [["#C4FFA0", 0], ["#ffffff", 1]]
})
.clip(new Path().commands(this.pathCommands1))
Path()
.commands(this.pathCommands2)
.fill('none')
.stroke(Color.Transparent)
.linearGradient(
{
angle: 50,
colors: [['#8CC36A', 0.1], ["#B3EB90", 0.4], ["#ffffff", 0.7]]
})
.clip(new Path().commands(this.pathCommands2))
}
.height('630px')
.width('630px')
Text('Healthy Diet')
.fontSize(26)
.fontColor(Color.White)
.margin({ top: 300 })
Text('Healthy life comes from a balanced diet')
.fontSize(17)
.fontColor(Color.White)
.margin({ top: 4 })
}
.width('100%')
.height('100%')
.linearGradient(
{
angle: 180,
colors: [['#BDE895', 0.1], ["#95DE7F", 0.6], ["#7AB967", 1]]
})
}
}
```
![drawing-feature8](figures/drawing-feature8.png)
# Flex Layout
The flex layout is the most flexible layout used in adaptive layout. It provides simple and powerful tools for distributing space and aligning items.
- Container: [\<Flex>](../reference/arkui-ts/ts-container-flex.md) component, used to set layout attributes.
- Child component: any of the child items in the **\<Flex>** component.
- Main axis: axis along which items are placed in the **\<Flex>** component. Child components are laid out along the main axis by default. The start point of the main axis is referred to as main-start, and the end point is referred to as main-end.
- Cross axis: axis that runs perpendicular to the main axis. The start point of the cross axis is referred to as cross-start, and the end point is referred to as cross-end.
The following uses **FlexDirection.Row** as an example.
![](figures/flex.png)
## Container Attributes
Create a flex layout through the **Flex** API provided by the **\<Flex>** component. The sample code is as follows:
`Flex(options?: { direction?: FlexDirection, wrap?: FlexWrap, justifyContent?: FlexAlign, alignItems?: ItemAlign, alignContent?: FlexAlign })`
### Flex Layout Direction
The **direction** parameter sets the direction in which items are laid out in the **\<Flex>** component and thereby defines the main axis. Available values are as follows:
![](figures/direction.png)
- **FlexDirection.Row** (default value): The main axis runs along the row horizontally, and the child components are laid out from the start edge of the main axis.
```ts
Flex({ direction: FlexDirection.Row }) {
Text('1').width('33%').height(50).backgroundColor(0xF5DEB3)
Text('2').width('33%').height(50).backgroundColor(0xD2B48C)
Text('3').width('33%').height(50).backgroundColor(0xF5DEB3)
}
.height(70)
.width('90%')
.padding(10)
.backgroundColor(0xAFEEEE)
```
![en-us_image_0000001218579606](figures/en-us_image_0000001218579606.png)
- **FlexDirection.RowReverse**: The main axis runs along the row horizontally, and the child components are laid out from the end edge of the main axis, in a direction opposite to **FlexDirection.Row**.
```ts
Flex({ direction: FlexDirection.RowReverse }) {
Text('1').width('33%').height(50).backgroundColor(0xF5DEB3)
Text('2').width('33%').height(50).backgroundColor(0xD2B48C)
Text('3').width('33%').height(50).backgroundColor(0xF5DEB3)
}
.height(70)
.width('90%')
.padding(10)
.backgroundColor(0xAFEEEE)
```
![en-us_image_0000001218739566](figures/en-us_image_0000001218739566.png)
- **FlexDirection.Column**: The main axis runs along the column vertically, and the child components are laid out from the start edge of the main axis.
```ts
Flex({ direction: FlexDirection.Column }) {
Text('1').width('100%').height(50).backgroundColor(0xF5DEB3)
Text('2').width('100%').height(50).backgroundColor(0xD2B48C)
Text('3').width('100%').height(50).backgroundColor(0xF5DEB3)
}
.height(70)
.width('90%')
.padding(10)
.backgroundColor(0xAFEEEE)
```
![en-us_image_0000001263019457](figures/en-us_image_0000001263019457.png)
- **FlexDirection.ColumnReverse**: The main axis runs along the column vertically, and the child components are laid out from the end edge of the main axis, in a direction opposite to **FlexDirection.Column**.
```ts
Flex({ direction: FlexDirection.ColumnReverse }) {
Text('1').width('100%').height(50).backgroundColor(0xF5DEB3)
Text('2').width('100%').height(50).backgroundColor(0xD2B48C)
Text('3').width('100%').height(50).backgroundColor(0xF5DEB3)
}
.height(70)
.width('90%')
.padding(10)
.backgroundColor(0xAFEEEE)
```
![en-us_image_0000001263339459](figures/en-us_image_0000001263339459.png)
### Wrapping in the Flex Layout
By default, child components are laid out on a single line (also called an axis) in the flex container. You can use the **wrap** parameter to set whether child components can wrap onto multiple lines. Available values are as follows:
- **FlexWrap.NoWrap** (default value): Child components are laid out on a single line. This may cause the child components to shrink to fit the container when the total width of the child components is greater than the width of the container.
```ts
Flex({ wrap: FlexWrap.NoWrap }) {
Text('1').width('50%').height(50).backgroundColor(0xF5DEB3)
Text('2').width('50%').height(50).backgroundColor(0xD2B48C)
Text('3').width('50%').height(50).backgroundColor(0xF5DEB3)
}
.width('90%')
.padding(10)
.backgroundColor(0xAFEEEE)
```
![en-us_image_0000001263139409](figures/en-us_image_0000001263139409.png)
- **FlexWrap.Wrap**: Child components can break into multiple lines along the main axis.
```ts
Flex({ wrap: FlexWrap.Wrap }) {
Text('1').width('50%').height(50).backgroundColor(0xF5DEB3)
Text('2').width('50%').height(50).backgroundColor(0xD2B48C)
Text('3').width('50%').height(50).backgroundColor(0xD2B48C)
}
.width('90%')
.padding(10)
.backgroundColor(0xAFEEEE)
```
![en-us_image_0000001218419614](figures/en-us_image_0000001218419614.png)
- **FlexWrap.WrapReverse**: Child components can break into multiple lines in the reverse direction to the main axis.
```ts
Flex({ wrap: FlexWrap.WrapReverse}) {
Text('1').width('50%').height(50).backgroundColor(0xF5DEB3)
Text('2').width('50%').height(50).backgroundColor(0xD2B48C)
Text('3').width('50%').height(50).backgroundColor(0xF5DEB3)
}
.width('90%')
.padding(10)
.backgroundColor(0xAFEEEE)
```
![en-us_image_0000001263259399](figures/en-us_image_0000001263259399.png)
### Alignment in the Flex Layout
#### Alignment on the Main Axis
Use the **justifyContent** parameter to set alignment of items on the main axis. The available options are as follows:
![](figures/justifyContent.png)
- **FlexAlign.Start** (default value): The items are aligned with each other toward the start edge of the container along the main axis.
```ts
Flex({ justifyContent: FlexAlign.Start }) {
Text('1').width('20%').height(50).backgroundColor(0xF5DEB3)
Text('2').width('20%').height(50).backgroundColor(0xD2B48C)
Text('3').width('20%').height(50).backgroundColor(0xF5DEB3)
}
.width('90%')
.padding({ top: 10, bottom: 10 })
.backgroundColor(0xAFEEEE)
```
![en-us_image_0000001218259634](figures/mainStart.png)
- **FlexAlign.Center**: The items are aligned with each other toward the center of the container along the main axis.
```ts
Flex({ justifyContent: FlexAlign.Center }) {
Text('1').width('20%').height(50).backgroundColor(0xF5DEB3)
Text('2').width('20%').height(50).backgroundColor(0xD2B48C)
Text('3').width('20%').height(50).backgroundColor(0xF5DEB3)
}
.width('90%')
.padding({ top: 10, bottom: 10 })
.backgroundColor(0xAFEEEE)
```
![en-us_image_0000001218579608](figures/mainCenter.png)
- **FlexAlign.End**: The items are aligned with each other toward the end edge of the container along the main axis.
```ts
Flex({ justifyContent: FlexAlign.End }) {
Text('1').width('20%').height(50).backgroundColor(0xF5DEB3)
Text('2').width('20%').height(50).backgroundColor(0xD2B48C)
Text('3').width('20%').height(50).backgroundColor(0xF5DEB3)
}
.width('90%')
.padding({ top: 10, bottom: 10 })
.backgroundColor(0xAFEEEE)
```
![en-us_image_0000001218739568](figures/mainEnd.png)
- **FlexAlign.SpaceBetween**: The items are evenly distributed within the container along the main axis. The first and last items are aligned with the edges of the container.
```ts
Flex({ justifyContent: FlexAlign.SpaceBetween }) {
Text('1').width('20%').height(50).backgroundColor(0xF5DEB3)
Text('2').width('20%').height(50).backgroundColor(0xD2B48C)
Text('3').width('20%').height(50).backgroundColor(0xF5DEB3)
}
.width('90%')
.padding({ top: 10, bottom: 10 })
.backgroundColor(0xAFEEEE)
```
![en-us_image_0000001263019461](figures/mainSpacebetween.png)
- **FlexAlign.SpaceAround**: The items are evenly distributed in the container along the main axis. The space between the first item and main-start, and that between the last item and main-end are both half of the space between two adjacent items.
```ts
Flex({ justifyContent: FlexAlign.SpaceAround }) {
Text('1').width('20%').height(50).backgroundColor(0xF5DEB3)
Text('2').width('20%').height(50).backgroundColor(0xD2B48C)
Text('3').width('20%').height(50).backgroundColor(0xF5DEB3)
}
.width('90%')
.padding({ top: 10, bottom: 10 })
.backgroundColor(0xAFEEEE)
```
![en-us_image_0000001263339461](figures/mainSpacearound.png)
- **FlexAlign.SpaceEvenly**: The items are equally distributed along the main axis. The space between the first item and main-start, the space between the last item and main-end, and the space between two adjacent items are the same.
```ts
Flex({ justifyContent: FlexAlign.SpaceEvenly }) {
Text('1').width('20%').height(50).backgroundColor(0xF5DEB3)
Text('2').width('20%').height(50).backgroundColor(0xD2B48C)
Text('3').width('20%').height(50).backgroundColor(0xF5DEB3)
}
.width('90%')
.padding({ top: 10, bottom: 10 })
.backgroundColor(0xAFEEEE)
```
![en-us_image_0000001263139411](figures/mainSpaceevenly.png)
#### Alignment on the Cross Axis
Alignment on the cross axis can be set for both the container and child components, with that set for child components having a higher priority.
##### Setting Alignment on the Cross Axis for the Container
Use the **alignItems** parameter of the **\<Flex>** component to set alignment of items on the cross axis. The available options are as follows:
- **ItemAlign.Auto**: The items are automatically aligned in the flex container.
```ts
Flex({ alignItems: ItemAlign.Auto }) {
Text('1').width('33%').height(30).backgroundColor(0xF5DEB3)
Text('2').width('33%').height(40).backgroundColor(0xD2B48C)
Text('3').width('33%').height(50).backgroundColor(0xF5DEB3)
}
.size({width: '90%', height: 80})
.padding(10)
.backgroundColor(0xAFEEEE)
```
![en-us_image_0000001218419616](figures/en-us_image_0000001218419616.png)
- **ItemAlign.Start**: The items are aligned with the start edge of the container along the cross axis.
```ts
Flex({ alignItems: ItemAlign.Start }) {
Text('1').width('33%').height(30).backgroundColor(0xF5DEB3)
Text('2').width('33%').height(40).backgroundColor(0xD2B48C)
Text('3').width('33%').height(50).backgroundColor(0xF5DEB3)
}
.size({width: '90%', height: 80})
.padding(10)
.backgroundColor(0xAFEEEE)
```
![en-us_image_0000001263259401](figures/en-us_image_0000001263259401.png)
- **ItemAlign.Start**: The items are aligned with the center of the container along the cross axis.
```ts
Flex({ alignItems: ItemAlign.Center }) {
Text('1').width('33%').height(30).backgroundColor(0xF5DEB3)
Text('2').width('33%').height(40).backgroundColor(0xD2B48C)
Text('3').width('33%').height(50).backgroundColor(0xF5DEB3)
}
.size({width: '90%', height: 80})
.padding(10)
.backgroundColor(0xAFEEEE)
```
![en-us_image_0000001218259636](figures/en-us_image_0000001218259636.png)
- **ItemAlign.End**: The items are aligned with the end edge of the container along the cross axis.
```ts
Flex({ alignItems: ItemAlign.End }) {
Text('1').width('33%').height(30).backgroundColor(0xF5DEB3)
Text('2').width('33%').height(40).backgroundColor(0xD2B48C)
Text('3').width('33%').height(50).backgroundColor(0xF5DEB3)
}
.size({width: '90%', height: 80})
.padding(10)
.backgroundColor(0xAFEEEE)
```
![en-us_image_0000001218579610](figures/en-us_image_0000001218579610.png)
- **ItemAlign.Stretch**: The items are stretched along the cross axis. If no constraints are set, the items are stretched to fill the size of the container on the cross axis.
```ts
Flex({ alignItems: ItemAlign.Stretch }) {
Text('1').width('33%').height(30).backgroundColor(0xF5DEB3)
Text('2').width('33%').height(40).backgroundColor(0xD2B48C)
Text('3').width('33%').height(50).backgroundColor(0xF5DEB3)
}
.size({width: '90%', height: 80})
.padding(10)
.backgroundColor(0xAFEEEE)
```
![en-us_image_0000001218739570](figures/itemalignstretch.png)
- **ItemAlign.Baseline**: The items are aligned at the baseline of the cross axis.
```ts
Flex({ alignItems: ItemAlign.Baseline }) {
Text('1').width('33%').height(30).backgroundColor(0xF5DEB3)
Text('2').width('33%').height(40).backgroundColor(0xD2B48C)
Text('3').width('33%').height(50).backgroundColor(0xF5DEB3)
}
.size({width: '90%', height: 80})
.padding(10)
.backgroundColor(0xAFEEEE)
```
![en-us_image_0000001263019463](figures/en-us_image_0000001263019463.png)
##### Setting Alignment on the Cross Axis for Child Components
Use the **alignSelf** attribute of child components to set their alignment in the container on the cross axis. The settings overwrite the default **alignItems** settings in the flex layout container. The sample code is as follows:
```ts
Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center }) { // The items are aligned with the center of the container.
Text('alignSelf Start').width('25%').height(80)
.alignSelf(ItemAlign.Start)
.backgroundColor(0xF5DEB3)
Text('alignSelf Baseline')
.alignSelf(ItemAlign.Baseline)
.width('25%')
.height(80)
.backgroundColor(0xD2B48C)
Text('alignSelf Baseline').width('25%').height(100)
.backgroundColor(0xF5DEB3)
.alignSelf(ItemAlign.Baseline)
Text('no alignSelf').width('25%').height(100)
.backgroundColor(0xD2B48C)
Text('no alignSelf').width('25%').height(100)
.backgroundColor(0xF5DEB3)
}.width('90%').height(220).backgroundColor(0xAFEEEE)
```
![alignself](figures/alignself.png)
In the preceding example, both **alignItems** of the **\<Flex>** component and the **alignSelf** attribute of the child component are set. In this case, the **alignSelf** settings take effect.
#### Content Alignment
Use the **alignContent** parameter to set how space is distributed between and around content items along the cross axis. This parameter is valid only for a flex layout that contains multiple lines. The available options are as follows:
- **FlexAlign.Start**: The items are aligned toward the start edge of the cross axis in the container.
```ts
Flex({ justifyContent: FlexAlign.SpaceBetween, wrap: FlexWrap.Wrap, alignContent: FlexAlign.Start }) {
Text('1').width('30%').height(20).backgroundColor(0xF5DEB3)
Text('2').width('60%').height(20).backgroundColor(0xD2B48C)
Text('3').width('40%').height(20).backgroundColor(0xD2B48C)
Text('4').width('30%').height(20).backgroundColor(0xF5DEB3)
Text('5').width('20%').height(20).backgroundColor(0xD2B48C)
}
.width('90%')
.height(100)
.backgroundColor(0xAFEEEE)
```
![crossStart.png](figures/crossStart.png)
- **FlexAlign.Center**: The items are aligned toward the center of the cross axis in the container.
```ts
Flex({ justifyContent: FlexAlign.SpaceBetween, wrap: FlexWrap.Wrap, alignContent: FlexAlign.Center }) {
Text('1').width('30%').height(20).backgroundColor(0xF5DEB3)
Text('2').width('60%').height(20).backgroundColor(0xD2B48C)
Text('3').width('40%').height(20).backgroundColor(0xD2B48C)
Text('4').width('30%').height(20).backgroundColor(0xF5DEB3)
Text('5').width('20%').height(20).backgroundColor(0xD2B48C)
}
.width('90%')
.height(100)
.backgroundColor(0xAFEEEE)
```
![crossCenter.png](figures/crossCenter.png)
- **FlexAlign.End**: The items are aligned toward the end edge of the cross axis in the container.
```ts
Flex({ justifyContent: FlexAlign.SpaceBetween, wrap: FlexWrap.Wrap, alignContent: FlexAlign.End }) {
Text('1').width('30%').height(20).backgroundColor(0xF5DEB3)
Text('2').width('60%').height(20).backgroundColor(0xD2B48C)
Text('3').width('40%').height(20).backgroundColor(0xD2B48C)
Text('4').width('30%').height(20).backgroundColor(0xF5DEB3)
Text('5').width('20%').height(20).backgroundColor(0xD2B48C)
}
.width('90%')
.height(100)
.backgroundColor(0xAFEEEE)
```
![crossEnd.png](figures/crossEnd.png)
- **FlexAlign.SpaceBetween**: The items are evenly distributed in the container along the cross axis, with the first and last items aligned with the edges of the cross axis.
```ts
Flex({ justifyContent: FlexAlign.SpaceBetween, wrap: FlexWrap.Wrap, alignContent: FlexAlign.SpaceBetween }) {
Text('1').width('30%').height(20).backgroundColor(0xF5DEB3)
Text('2').width('60%').height(20).backgroundColor(0xD2B48C)
Text('3').width('40%').height(20).backgroundColor(0xD2B48C)
Text('4').width('30%').height(20).backgroundColor(0xF5DEB3)
Text('5').width('20%').height(20).backgroundColor(0xD2B48C)
}
.width('90%')
.height(100)
.backgroundColor(0xAFEEEE)
```
![crossSpacebetween.png](figures/crossSpacebetween.png)
- **FlexAlign.SpaceAround**: The items are evenly distributed in the container along the cross axis. The spacing before the first item and after the last item is half of the spacing between two adjacent items.
```ts
Flex({ justifyContent: FlexAlign.SpaceBetween, wrap: FlexWrap.Wrap, alignContent: FlexAlign.SpaceAround }) {
Text('1').width('30%').height(20).backgroundColor(0xF5DEB3)
Text('2').width('60%').height(20).backgroundColor(0xD2B48C)
Text('3').width('40%').height(20).backgroundColor(0xD2B48C)
Text('4').width('30%').height(20).backgroundColor(0xF5DEB3)
Text('5').width('20%').height(20).backgroundColor(0xD2B48C)
}
.width('90%')
.height(100)
.backgroundColor(0xAFEEEE)
```
![crossSpacearound.png](figures/crossSpacearound.png)
- **FlexAlign.SpaceEvenly**: The items are evenly distributed in the container along the cross axis. The spacing between each two adjacent items, the spacing between the start edge and the first item, and the spacing between the end edge and the last item, are the same.
```ts
Flex({ justifyContent: FlexAlign.SpaceBetween, wrap: FlexWrap.Wrap, alignContent: FlexAlign.SpaceEvenly }) {
Text('1').width('30%').height(20).backgroundColor(0xF5DEB3)
Text('2').width('60%').height(20).backgroundColor(0xD2B48C)
Text('3').width('40%').height(20).backgroundColor(0xD2B48C)
Text('4').width('30%').height(20).backgroundColor(0xF5DEB3)
Text('5').width('20%').height(20).backgroundColor(0xD2B48C)
}
.width('90%')
.height(100)
.backgroundColor(0xAFEEEE)
```
![crossSpaceevenly.png](figures/crossSpaceevenly.png)
### Adaptive Stretching of Flex Layout
When the size of the container in the flex layout is not large enough, the following attributes of the child component can be used to achieve adaptive layout.
- **flexBasis**: base size of the child component in the container along the main axis. It sets the space occupied by the child component. If this attribute is not set or is set to **auto**, the space occupied by the child component is the value of width/height.
```ts
Flex() {
Text('flexBasis("auto")')
.flexBasis('auto') // When width is not set and flexBasis is set to auto, the content is loose.
.height(100)
.backgroundColor(0xF5DEB3)
Text('flexBasis("auto")'+' width("40%")')
.width('40%')
.flexBasis('auto') // When width is set and flexBasis is set to auto, the value of width is used.
.height(100)
.backgroundColor(0xD2B48C)
Text('flexBasis(100)') // When width is not set and flexBasis is set to 100, the width is 100 vp.
.flexBasis(100)
.height(100)
.backgroundColor(0xF5DEB3)
Text('flexBasis(100)')
.flexBasis(100)
.width(200) // When width is set to 200 and flexBasis 100, the width is 100 vp, which means that the settings of flexBasis take precedence.
.height(100)
.backgroundColor(0xD2B48C)
}.width('90%').height(120).padding(10).backgroundColor(0xAFEEEE)
```
![flexbasis](figures/flexbasis.png)
- **flexGrow**: percentage of the flex layout's remaining space that is allocated to the child component. In other words, it is the grow factor of the child component.
```ts
Flex() {
Text('flexGrow(1)')
.flexGrow(2)
.width(100)
.height(100)
.backgroundColor(0xF5DEB3)
Text('flexGrow(2)')
.flexGrow(2)
.width(100)
.height(100)
.backgroundColor(0xD2B48C)
Text('no flexGrow')
.width(100)
.height(100)
.backgroundColor(0xF5DEB3)
}.width(400).height(120).padding(10).backgroundColor(0xAFEEEE)
```
![flexgrow](figures/flexgrow.png)
In the preceding figure, the width of the parent container is 400 vp, the original width of the three child components is 100 vp, which adds up to the total width of 300 vp. The remaining space 100 vp is allocated to the child components based on their **flexGrow** settings. Child components that do not have **flexGrow** set are not involved in the allocation of remaining space.
The first child component and the second child component receive their share of remaining space at the 2:3 ratio. The width of the first child component is 100 vp + 100 vp x 2/5 = 140 vp, and the width of the second child component is 100 vp + 100 vp x 3/5 = 160 vp.
- **flexShrink**: shrink factor of the child component when the size of all child components is larger than the flex container.
```ts
Flex({ direction: FlexDirection.Row }) {
Text('flexShrink(3)')
.flexShrink(3)
.width(200)
.height(100)
.backgroundColor(0xF5DEB3)
Text('no flexShrink')
.width(200)
.height(100)
.backgroundColor(0xD2B48C)
Text('flexShrink(2)')
.flexShrink(2)
.width(200)
.height(100)
.backgroundColor(0xF5DEB3)
}.width(400).height(120).padding(10).backgroundColor(0xAFEEEE)
```
![flexshrink](figures/flexshrink.png)
## Example Scenario
With the flex layout, child components can be arranged horizontally, aligned at both edges, evenly spaced, and centered in the vertical direction. The sample code is as follows:
```ts
@Entry
@Component
struct FlexExample {
build() {
Column() {
Column({ space: 5 }) {
Flex({ direction: FlexDirection.Row, wrap: FlexWrap.NoWrap, justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) {
Text('1').width('30%').height(50).backgroundColor(0xF5DEB3)
Text('2').width('30%').height(50).backgroundColor(0xD2B48C)
Text('3').width('30%').height(50).backgroundColor(0xF5DEB3)
}
.height(70)
.width('90%')
.backgroundColor(0xAFEEEE)
}.width('100%').margin({ top: 5 })
}.width('100%')
}
}
```
![en-us_image_0000001261605867](figures/flexExample.png)
# Grid Layout
As a tool to provide layout auxiliary lines, the grid system is handy in graphic and website design. When employed in the UI design of mobile devices, the grid system exhibits the following advantages:
1. Provides rules for layout design and resolves issues of dynamic layout across devices with different sizes.
2. Provides a unified positioning method for the system to ensure layout consistency across layouts on different devices.
3. Provides a flexible spacing adjustment method for applications to keep up with layout in special scenarios.
To implement a grid layout, **[GridRow](../reference/arkui-ts/ts-container-gridrow.md)** and **[GridCol](../reference/arkui-ts/ts-container-gridcol.md)** are prioritized
over the obsolete **[GridContainer](../reference/arkui-ts/ts-container-gridcontainer.md)**, because the former provides more flexibility and functionality. **GridRow** is a grid container item and must be used only with **GridCol** in the grid container.
## GridRow
The grid container works in terms of **columns**, **gutter**, **direction**, and **breakpoints**.
- **columns**: total number of columns in the grid container. This attribute is the main tool for placing items in the grid layout.
- **gutter**: spacing between elements. This attribute determines how close the content tracks are with each other.
- **direction**: alignment of child components in the grid container.
- **breakpoints**: ranges of application window widths, which are set based on the device screen widths. You can use the breakpoints to meet specific layout requirements.
After you set the breakpoints, the layout listens for changes in the application window size, determines which breakpoint range the application window is located, and adjusts to match the window size accordingly.
### Grid Breakpoints
The grid system defines breakpoints, which are screen width types in effect, based on the horizontal width (screen density pixels, in vp) of the screens. You can use the breakpoints to meet specific layout requirements.
By default, the grid system provides four breakpoints: xs, sm, md, and lg.
| Breakpoint | Value Range (vp)|
| --------| ------ |
| xs | [0, 320) |
| sm | [320, 520) |
| md | [520, 840) |
| lg | [840, +∞) |
In the **\<GridRow>** component, you can use **breakpoints** to customize the value range of breakpoints. A maximum of six breakpoints are supported.
In addition to the four default breakpoints, you can also enable the xl and xxl breakpoints for your application window layout.
| Breakpoint| Device Description |
| ----- | ---------------------------------------- |
| xs | Device of the minimum size. |
| sm | Small-sized device. |
| md | Medium-sized device. |
| lg | Large-sized device. |
| xl | Extra-large-sized device. |
| xxl | Ultra-large-sized device. |
- Set **breakpoints** with a monotonically increasing array based on the use case. Because **breakpoints** supports a maximum of six breakpoints, the maximum length of the monotonically increasing array is 5.
```ts
breakpoints: {value: ["100vp", "200vp"]}
```
Enables three breakpoints: xs, sm, and md. If the value is less than 100 vp, the breakpoint is xs. If the value is 100–200 vp, the breakpoint is sm. If the value is greater than 200 vp, the breakpoint is md.
```ts
breakpoints: {value: ["320vp", "520vp", "840vp", "1080vp"]}
```
Enables five breakpoints: xs, sm, md, lg, and xl. If the value is less than 320 vp, the breakpoint is xs. If the value is 320–520 vp, the breakpoint is sm. If the value is 520–840 vp, the breakpoint is md. If the value is 840–1080vp, the breakpoint is lg. If the value is greater than 1080 vp, the breakpoint is xl.
- The grid system implements breakpoints by listening for the changes in the window or container size, and sets the breakpoint references through **reference**. Considering that the application may be displayed in non-full-screen mode, design the breakpoints with the application window width as the reference.
In the following example, the default number of columns of a grid is 12. Breakpoints are used to divide the application window width into six ranges, where different grid items occupy a different number of columns. The following figure shows the effect.
```ts
GridRow({
breakpoints: {
value: ['200vp', '300vp', '400vp', '500vp', '600vp'],
reference: BreakpointsReference.WindowSize
}
}) {
ForEach(this.bgColors, (color, index) => {
GridCol({
span: {
xs: 2,
sm: 3,
md: 4,
lg: 6,
xl: 8,
xxl: 12
}
}) {
Row() {
Text(`${index}`)
}.width("100%").height("50vp")
}.backgroundColor(color)
})
}
```
![](figures/breakpoints.gif)
### Columns
In the **\<GridRow>**, **columns** is used to set the total number of columns in the grid layout.
- The default value of **columns** is 12. If **columns** is not set, the grid layout is divided into 12 columns at any breakpoint.
```ts
GridRow() {
ForEach(this.bgColors, (item, index) => {
GridCol() {
Row() {
Text(`${index + 1}`)
}.width("100%").height("50")
}.backgroundColor(item)
})
}
```
![](figures/columns1.png)
- When **columns** is set to a number, the grid layout is divided into the specified number of columns regardless of the screen size. The following example sets the number of grid layout columns to 4 and 8 in sequence, where a child component occupies one column by default.
```ts
Row() {
GridRow({ columns: 4 }) {
ForEach(this.bgColors, (item, index) => {
GridCol() {
Row() {
Text(`${index + 1}`)
}.width("100%").height("50")
}.backgroundColor(item)
})
}
.width("100%").height("100%")
.onBreakpointChange((breakpoint) => {
this.currentBp = breakpoint
})
}
.height(160)
.border({ color: Color.Blue, width: 2 })
.width('90%')
Row() {
GridRow({ columns: 8 }) {
ForEach(this.bgColors, (item, index) => {
GridCol() {
Row() {
Text(`${index + 1}`)
}.width("100%").height("50")
}.backgroundColor(item)
})
}
.width("100%").height("100%")
.onBreakpointChange((breakpoint) => {
this.currentBp = breakpoint
})
}
.height(160)
.border({ color: Color.Blue, width: 2 })
.width('90%')
```
![](figures/columns2.png)
- When **columns** is set to a value of the **GridRowColumnOption** type, you can assign values specific to the screen size (xs, sm, md, lg, xl, xxl).
```ts
GridRow({ columns: { sm: 4, md: 8 }, breakpoints: { value: ['200vp', '300vp', '400vp', '500vp', '600vp'] } }) {
ForEach(this.bgColors, (item, index) => {
GridCol() {
Row() {
Text(`${index + 1}`)
}.width("100%").height("50")
}.backgroundColor(item)
})
}
```
![](figures/columns3.gif)
As shown above, if **columns** is only set for the sm and md screen size types, screen sizes smaller than sm use the default value **12**, and screen sizes larger than md (lg, xl, and xxl) use the value of **columns** of the md type.
### Gutters
In the **\<GridRow>**, **gutter** is used to set the spacing between adjacent child components in the horizontal and vertical directions.
- When **gutter** is set to a number, the number applies to both the horizontal and vertical directions. In the following example, the horizontal and vertical spacing between adjacent child components are set to **10**.
```ts
GridRow({ gutter: 10 }){}
```
![](figures/gutter1.png)
- When **gutter** is set to a value of the **GutterOption** type, the **x** attribute of the value indicates the horizontal gutter, and the **y** attribute indicates the vertical gutter.
```ts
GridRow({ gutter: { x: 20, y: 50 } }){}
```
![](figures/gutter2.png)
### Alignment
In the **\<GridRow>**, **direction** is used to set the alignment of child components in the grid container.
- Child components are arranged from left to right by default.
```ts
GridRow({ direction: GridRowDirection.Row }){}
```
![](figures/direction1.png)
- Child components are arranged from right to left.
```ts
GridRow({ direction: GridRowDirection.RowReverse }){}
```
![](figures/direction2.png)
## GridCol
The **\<GridCol>** component is a child component of the **\<GridRow>** component. You can set the **span**, **offset**, and **order** attributes of this component by passing parameters or using setters.
- Setting **span**
```ts
GridCol({ span: 2 }){}
GridCol({ span: { xs: 1, sm: 2, md: 3, lg: 4 } }){}
GridCol(){}.span(2)
GridCol(){}.span({ xs: 1, sm: 2, md: 3, lg: 4 })
```
- Setting **offset**
```ts
GridCol({ offset: 2 }){}
GridCol({ offset: { xs: 2, sm: 2, md: 2, lg: 2 } }){}
GridCol(){}.offset(2)
GridCol(){}.offset({ xs: 1, sm: 2, md: 3, lg: 4 })
```
- Setting **order**
```ts
GridCol({ order: 2 }){}
GridCol({ order: { xs: 1, sm: 2, md: 3, lg: 4 } }){}
GridCol(){}.order(2)
GridCol(){}.order({ xs: 1, sm: 2, md: 3, lg: 4 })
```
The following describes how to set the attributes by passing parameters.
### span
Sets the number of columns occupied by a child component in the grid layout, which determines the child component width. The default value is **1**.
- When the value type is number, the number of columns occupied by the child component is the same across screen sizes.
```ts
GridRow({ columns: 8 }) {
ForEach(this.bgColors, (color, index) => {
GridCol({ span: 2 }) {
Row() {
Text(`${index}`)
}.width("100%").height("50vp")
}
.backgroundColor(color)
})
}
```
![](figures/span1.png)
- When the value type is **GridColColumnOption**, you can assign values specific to the screen size (xs, sm, md, lg, xl, xxl).
```ts
GridRow({ columns: 8 }) {
ForEach(this.bgColors, (color, index) => {
GridCol({ span: { xs: 1, sm: 2, md: 3, lg: 4 } }) {
Row() {
Text(`${index}`)
}.width("100%").height("50vp")
}
.backgroundColor(color)
})
}
```
![](figures/span2.gif)
### offset
Sets the column offset of a child component relative to the previous child component. The default value is **0**.
- When the value type is number, the column offset of the child component is the same across screen sizes.
```ts
GridRow() {
ForEach(this.bgColors, (color, index) => {
GridCol({ offset: 2 }) {
Row() {
Text("" + index)
}.width("100%").height("50vp")
}
.backgroundColor(color)
})
}
```
![](figures/offset1.png)
By default, a grid is divided into 12 columns and each child component occupies one column with an offset of two columns. Each row holds four child components, with three columns per child component plus the gutter.
- When the value type is **GridColColumnOption**, you can assign values specific to the screen size (xs, sm, md, lg, xl, xxl).
```ts
GridRow() {
ForEach(this.bgColors, (color, index) => {
GridCol({ offset: { xs: 1, sm: 2, md: 3, lg: 4 } }) {
Row() {
Text("" + index)
}.width("100%").height("50vp")
}
.backgroundColor(color)
})
}
```
![](figures/offset2.gif)
### order
Sets the sequence number of a child component in the grid layout. If a child component shares an **order** value with another child component or does not have **order** set, it is displayed based on its code sequence number. A child components with a smaller **order** value is placed before the one with a larger **order** value.
If **order** is not set for all child components, those that have **order** set are displayed after those that do not have **order** set and are sorted in ascending order based on the value.
- When the value type is number, child components are sorted in the same order across screen sizes.
```ts
GridRow() {
GridCol({ order: 5 }) {
Row() {
Text("1")
}.width("100%").height("50vp")
}.backgroundColor(Color.Red)
GridCol({ order: 4 }) {
Row() {
Text("2")
}.width("100%").height("50vp")
}.backgroundColor(Color.Orange)
GridCol({ order: 3 }) {
Row() {
Text("3")
}.width("100%").height("50vp")
}.backgroundColor(Color.Yellow)
GridCol({ order: 2 }) {
Row() {
Text("4")
}.width("100%").height("50vp")
}.backgroundColor(Color.Green)
}
```
![](figures/order1.png)
- When the value type is **GridColColumnOption**, you can assign values specific to the screen size (xs, sm, md, lg, xl, xxl).
```ts
GridRow() {
GridCol({ order: { xs:1, sm:5, md:3, lg:7}}) {
Row() {
Text("1")
}.width("100%").height("50vp")
}.backgroundColor(Color.Red)
GridCol({ order: { xs:2, sm:2, md:6, lg:1} }) {
Row() {
Text("2")
}.width("100%").height("50vp")
}.backgroundColor(Color.Orange)
GridCol({ order: { xs:3, sm:3, md:1, lg:6} }) {
Row() {
Text("3")
}.width("100%").height("50vp")
}.backgroundColor(Color.Yellow)
GridCol({ order: { xs:4, sm:4, md:2, lg:5} }) {
Row() {
Text("4")
}.width("100%").height("50vp")
}.backgroundColor(Color.Green)
}
```
![](figures/order2.gif)
# Grid Layout
The grid layout is an important layout in adaptive layout. It excels at dividing a page and defining proportion.
You can implement a grid layout through the **[\<Grid>](../reference/arkui-ts/ts-container-grid.md)** container and the **[\<GridItem>](../reference/arkui-ts/ts-container-griditem.md)** child component.
**\<Grid>** sets grid-wide parameters, and **\<GridItem>** defines parameters specific to grid items. The grid layout has the following features:
1. When the size of the container changes, all child components and their spacings are adjusted proportionally.
2. The numbers of rows and columns in the grid layout as well as the proportion of each row and column are customizable.
3. The row and column spacing of child components in the grid layout can be set.
4. A child component can span the specified number of rows or columns.
## Grid Settings
### Number and Proportion of Rows and Columns
The number and proportion of rows and columns are set through the **columnsTemplate** and **rowTemplate** attributes.
The following describes how to set **columnsTemplate**. The value of this attribute is a string consisting of multiple 'number+fr' entries separated by spaces. The number of entries is the number of columns in the grid layout. The number before **fr** is used to calculate the proportion of the column to the grid layout width, thereby determining the column width.
```ts
struct GridExample {
@State Number: Array<string> = ['1', '2', '3', '4']
build() {
Column({ space: 5 }) {
Grid() {
ForEach(this.Number, (num: string) => {
GridItem() {
Text(`Column ${num}`)
.fontSize(16)
.textAlign(TextAlign.Center)
.backgroundColor(0xd0d0d0)
.width('100%')
.height('100%')
.borderRadius(5)
}
})
}
.columnsTemplate('1fr 1fr 1fr 1fr')
.rowsTemplate('1fr')
.columnsGap(10)
.rowsGap(20)
.width('90%')
.backgroundColor(0xF0F0F0)
.height(100)
}.width('100%')
}
}
```
Create a four-column grid, where each column has an equal width.
```ts
struct GridExample {
@State Number: Array<string> = ['1', '2', '3', '4']
build() {
Column({ space: 5 }) {
Grid() {
ForEach(this.Number, (num: string) => {
GridItem() {
Text(`Column ${num}`)
.fontSize(16)
.textAlign(TextAlign.Center)
.backgroundColor(0xd0d0d0)
.width('100%')
.height('100%')
.borderRadius(5)
}
})
}
.columnsTemplate('1fr 2fr 3fr 4fr')
.rowsTemplate('1fr')
.columnsGap(10)
.rowsGap(20)
.width('90%')
.backgroundColor(0xF0F0F0)
.height(100)
}.width('100%')
}
}
```
Create a four-column grid, where the proportions of the columns are 1:2:3:4.
```ts
struct GridExample {
@State Number: Array<string> = ['1', '2', '3']
build() {
Column({ space: 5 }) {
Grid() {
ForEach(this.Number, (num: string) => {
GridItem() {
Text(`Column ${num}`)
.fontSize(16)
.textAlign(TextAlign.Center)
.backgroundColor(0xd0d0d0)
.width('100%')
.height('100%')
.borderRadius(5)
}
})
}
.columnsTemplate('4fr 2fr 3fr')
.rowsTemplate('1fr')
.columnsGap(10)
.rowsGap(20)
.width('90%')
.backgroundColor(0xF0F0F0)
.height(100)
}.width('100%')
}
}
```
Create a three-column grid, where the proportions of the columns are 4:2:3.
The effect is as follows:
![](figures/columnTemplate.png)
### Alignment
Set **layoutDirection** to define how grid items are laid out along the main axis.
The layout options are **Row**, **RowReverse**, **Column**, and **ColumnReverse**.
The effect is as follows:
![](figures/gridlayout.png)
### Spacing Between Rows and Columns
Set **columnsGap** to define the vertical spacing of the grid items and **rowsGap** to define the horizontal spacing.
```ts
Grid()
.columnsTemplate('1fr 1fr 1fr 1fr')
.columnsGap(10)
.rowsGap(20)
```
![](figures/columnGap.png)
In the preceding example, the horizontal and vertical spacings between grid items are 10 and 20, respectively.
## GridItem Settings
### Setting the Number of Rows and Columns per Grid Item
The rows and columns of a grid layout are numbered in sequence, starting from 1.
If a grid item spans multiple rows, use **rowStart** to set the start row number and **rowEnd** to set the end row number. If the value of **rowStart** is the same as that of **rowEnd**, the grid item occupies only one grid. The sample code is as follows:
```ts
Grid() {
GridItem() {
Text('5')
.fontSize(16)
.textAlign(TextAlign.Center)
.textStyle()
}.rowStart(2).rowEnd(3) // The grid item spans the second and third rows.
GridItem() {
Text('4')
.fontSize(16)
.textAlign(TextAlign.Center)
.textStyle()
}.columnStart(4).columnEnd(5) // The grid item spans the fourth and fifth columns.
GridItem() {
Text('6')
.fontSize(16)
.textAlign(TextAlign.Center)
.textStyle()
}.columnStart(2).columnEnd(4) // The grid item spans the second to fourth columns.
GridItem() {
Text('9')
.fontSize(16)
.textAlign(TextAlign.Center)
.textStyle()
}.columnStart(3).columnEnd(4) // The grid item spans the third and fourth columns.
}
.columnsTemplate('1fr 1fr 1fr 1fr 1fr')
.rowsTemplate('1fr 1fr 1fr')
.columnsGap(10)
.rowsGap(20)
.width('90%')
.backgroundColor(0xF0F0F0)
.height('200vp')
.layoutDirection(GridDirection.Column)
```
![](figures/griditem.png)
## Example Scenario
Use the grid layout to implement a calculator. The sample code is as follows:
```ts
@Entry
@Component
struct GridExample {
@State Number: Array<string> = ['1', '2', '3', '+', '4', '5', '6', '-', '7', '8', '9', '*', '0', '.', '/']
@Styles textStyle(){
.backgroundColor(0xd0d0d0)
.width('100%')
.height('100%')
.borderRadius(5)
}
build() {
Column({ space: 5 }) {
Grid() {
GridItem() {
Text('0')
.fontSize(30)
.textStyle()
}.columnStart(1).columnEnd(4)
GridItem() {
Text ('Clear')
.fontSize(16)
.textAlign(TextAlign.Center)
.textStyle()
}.columnStart(1).columnEnd(2)
GridItem() {
Text ('Back')
.fontSize(16)
.textAlign(TextAlign.Center)
.textStyle()
}.columnStart(3).columnEnd(4)
ForEach(this.Number, (day: string) => {
if (day === '0') {
GridItem() {
Text(day)
.fontSize(16)
.textAlign(TextAlign.Center)
.textStyle()
}.columnStart(1).columnEnd(2)
} else {
GridItem() {
Text(day)
.fontSize(16)
.textAlign(TextAlign.Center)
.textStyle()
}
}
})
}
.columnsTemplate('1fr 1fr 1fr 1fr')
.rowsTemplate('2fr 1fr 1fr 1fr 1fr 1fr')
.columnsGap(10)
.rowsGap(15)
.width('90%')
.backgroundColor(0xF0F0F0)
.height('70%')
}.width('100%').margin({ top: 5 })
}
}
```
Below you can see how the calculator displays on a large-screen device.
![](figures/gridExp1.png)
Below you can see how the calculator displays on a small-screen device.
![](figures/gridExp2.png)
# Linear Layout
The linear layout is the most commonly used layout in development. The child components in a linear layout are arranged one by one in a linear direction, either vertically or horizontally.
You can implement a linear layout through the **[\<Row>](../reference/arkui-ts/ts-container-row.md)** and **[\<Column>](../reference/arkui-ts/ts-container-column.md)** containers. In the **\<Column>** container, child components are arranged vertically. In the **\<Row>** container, child components are arranged horizontally.
## Linear Layout Orientation
The orientation of a linear layout is subject to the container: **\<Row>** or **\<Column>**. You can use the container attributes to adjust the spacing between child components and the horizontal and vertical alignment modes.
1. The **space** attribute sets the spacing between child components so that they are evenly spaced along the main axis.
2. The **alignItems** attribute sets the alignment mode of the child components along the cross axis, which is consistent across screens of various sizes. When the cross axis is vertical, the value type is [VerticalAlign](../reference/arkui-ts/ts-appendix-enums.md#verticalalign). When the cross axis is horizontal, the value type is [HorizontalAlign](../reference/arkui-ts/ts-appendix-enums.md#horizontalalign).
3. The **justifyContent** attribute sets the alignment mode of the child components along the main axis, implementing adaptive layout. The value type is [FlexAlign](../reference/arkui-ts/ts-appendix-enums.md#flexalign).
The table below provides the usage and demo effects.
|Attribute|Description|Row Effect|Column Effect|
|------|---------------------------|----------------------------|---------------------------|
|space |- Horizontal spacing between child components in the horizontal layout<br> - Vertical spacing between child components in the vertical layout| ![](figures/rowspace.png) | ![](figures/columnspace.png) |
|alignItems |Alignment mode of child components along the cross axis of the container.|![](figures/rowalign.png) |![](figures/columnalign.png)|
|justifyContent |Alignment mode of child components along the main axis of the container.|![](figures/rowjustify.png) |![](figures/columnjustify.png)|
## Adaptive Stretching
In linear layout, the **[\<Blank>](../reference/arkui-ts/ts-basic-components-blank.md)** component is commonly used to automatically fill blank space along the main axis of the container, so as to achieve adaptive stretching.
```ts
@Entry
@Component
struct BlankExample {
build() {
Column() {
Row() {
Text('Bluetooth').fontSize(18)
Blank()
Toggle({ type: ToggleType.Switch, isOn: true })
}.backgroundColor(0xFFFFFF).borderRadius(15).padding({ left: 12 }).width('100%')
}.backgroundColor(0xEFEFEF).padding(20).width('100%')
}
}
```
![](figures/blank.gif)
## Adaptive Scaling
Adaptive scaling means that child components scale according to the preset ratio to adapt to the container size on devices of various sizes. Below are the methods to implement adaptive scaling in a linear layout:
1. When the size of the parent container is determined, child components are laid out along the main axis based on the **layoutWeight** attribute settings, with the size settings of the child components ignored. In this way, the child components are adaptively scaled to fill up remaining space, regardless of the screen size.
```ts
@Entry
@Component
struct layoutWeightExample {
build() {
Column() {
Text('1:2:3').width('100%')
Row() {
Column() {
Text('layoutWeight(1)')
.textAlign(TextAlign.Center)
}.layoutWeight(2).backgroundColor(0xffd306).height('100%')
Column() {
Text('layoutWeight(2)')
.textAlign(TextAlign.Center)
}.layoutWeight(4).backgroundColor(0xffed97).height('100%')
Column() {
Text('layoutWeight(6)')
.textAlign(TextAlign.Center)
}.layoutWeight(6).backgroundColor(0xffd306).height('100%')
}.backgroundColor(0xffd306).height('30%')
Text('2:5:3').width('100%')
Row() {
Column() {
Text('layoutWeight(2)')
.textAlign(TextAlign.Center)
}.layoutWeight(2).backgroundColor(0xffd306).height('100%')
Column() {
Text('layoutWeight(5)')
.textAlign(TextAlign.Center)
}.layoutWeight(5).backgroundColor(0xffed97).height('100%')
Column() {
Text('layoutWeight(3)')
.textAlign(TextAlign.Center)
}.layoutWeight(3).backgroundColor(0xffd306).height('100%')
}.backgroundColor(0xffd306).height('30%')
}
}
}
```
![](figures/layoutWeight.gif)
3. When the size of the parent container is determined, set the widths of the child component and sibling components in percentage.
```ts
@Entry
@Component
struct WidthExample {
build() {
Column() {
Row() {
Column() {
Text('left width 20%')
.textAlign(TextAlign.Center)
}.width('20%').backgroundColor(0xffd306).height('100%')
Column() {
Text('center width 50%')
.textAlign(TextAlign.Center)
}.width('50%').backgroundColor(0xffed97).height('100%')
Column() {
Text('right width 30%')
.textAlign(TextAlign.Center)
}.width('30%').backgroundColor(0xffd306).height('100%')
}.backgroundColor(0xffd306).height('30%')
}
}
}
```
![](figures/width.gif)
In the preceding example, the proportion of child components remains unchanged across devices of different sizes.
## Positioning
- Relative layout
You can use the **[offset](../reference/arkui-ts/ts-universal-attributes-location.md)** attribute to set the offset of a component relative to itself, thereby implmenting relative layout. Setting this attribute does not affect the layout of the parent container. It only adjusts the component position during drawing. The linear layout and offset can work together to meet most layout development requirements.
```ts
@Entry
@Component
struct OffsetExample {
@Styles eleStyle() {
.size({ width: 120, height: '50' })
.backgroundColor(0xbbb2cb)
.border({ width: 1 })
}
build() {
Column({ space: 20 }) {
Row() {
Text('1').size({ width: '15%', height: '50' }).backgroundColor(0xdeb887).border({ width: 1 }).fontSize(16)
Text('2 offset(15, 30)')
.eleStyle()
.fontSize(16)
.align(Alignment.Start)
.offset({ x: 15, y: 30 })
Text('3').size({ width: '15%', height: '50' }).backgroundColor(0xdeb887).border({ width: 1 }).fontSize(16)
Text('4 offset(-10%, 20%)')
.eleStyle()
.fontSize(16)
.offset({ x: '-5%', y: '20%' })
}.width('90%').height(150).border({ width: 1, style: BorderStyle.Dashed })
}
.width('100%')
.margin({ top: 25 })
}
}
```
![](figures/offset.gif)
- Absolute layout
In linear layout, you can use the **[position](../reference/arkui-ts/ts-universal-attributes-location.md)** attribute to set the offset of the upper left corner of a component relative to the upper left corner of the parent container, thereby implementing absolute layout. Absolute layout offers poorer adaptability to screen sizes than relative layout.
```ts
@Entry
@Component
struct PositionExample {
@Styles eleStyle(){
.backgroundColor(0xbbb2cb)
.border({ width: 1 })
.size({ width: 120, height: 50 })
}
build() {
Column({ space: 20 }) {
// Set the offset of the upper left corner of the child component relative to the upper left corner of the parent container.
Row() {
Text('position(30, 10)')
.eleStyle()
.fontSize(16)
.position({ x: 10, y: 10 })
Text('position(50%, 70%)')
.eleStyle()
.fontSize(16)
.position({ x: '50%', y: '70%' })
Text('position(10%, 90%)')
.eleStyle()
.fontSize(16)
.position({ x: '10%', y: '80%' })
}.width('90%').height('100%').border({ width: 1, style: BorderStyle.Dashed })
}
.width('90%').margin(25)
}
}
```
![](figures/position.gif)
## Adaptive Extension
Adaptive extension allows users to drag the scrollbar to display content when the content amount displayed on a page is subject to the screen size and the content extends beyond the viewport. Below are the methods to implement adaptive extension in a linear layout:
- **\<List>** component
If the list items cannot be fully displayed on one screen, you can use the scrollbar to let users view the list items in full. Use the **scrollBar** attribute to set the scrollbar status, and the **edgeEffect** to set the rebound effect when the scrollbar has reached the edge.
Vertical list:
```ts
@Entry
@Component
struct ListExample1 {
@State arr: string[] = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]
@State alignListItem: ListItemAlign = ListItemAlign.Start
build() {
Column() {
List({ space: 20, initialIndex: 0 }) {
ForEach(this.arr, (item) => {
ListItem() {
Text('' + item)
.width('100%')
.height(100)
.fontSize(16)
.textAlign(TextAlign.Center)
.borderRadius(10)
.backgroundColor(0xFFFFFF)
}
.border({ width: 2, color: Color.Green })
}, item => item)
}
.border({ width: 2, color: Color.Red, style: BorderStyle.Dashed })
.scrollBar (BarState.On) // ScrollBar status
.edgeEffect(EdgeEffect.Spring) // Rebound effect when the scrollbar has reached the edge
}.width('100%').height('100%').backgroundColor(0xDCDCDC).padding(20)
}
}
```
![](figures/listcolumn.gif)
Horizontal List:
```ts
@Entry
@Component
struct ListExample2 {
@State arr: string[] = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]
@State alignListItem: ListItemAlign = ListItemAlign.Start
build() {
Column() {
List({ space: 20, initialIndex: 0 }) {
ForEach(this.arr, (item) => {
ListItem() {
Text('' + item)
.height('100%')
.width(100)
.fontSize(16)
.textAlign(TextAlign.Center)
.borderRadius(10)
.backgroundColor(0xFFFFFF)
}
.border({ width: 2, color: Color.Green })
}, item => item)
}
.border({ width: 2, color: Color.Red, style: BorderStyle.Dashed })
.scrollBar (BarState.On) // Scrollbar status.
.edgeEffect(EdgeEffect.Spring) // Rebound effect when the scrollbar has reached the edge.
.listDirection(Axis.Horizontal) // Horizontal layout.
}.width('100%').height('100%').backgroundColor(0xDCDCDC).padding(20)
}
}
```
![](figures/listrow.gif)
- **\<Scroll>** component
In linear layout, the **\<Scroll>** component scrolls the content when the layout size of a component exceeds the size of its parent component. You can wrap a **\<Scroll>** component at the outer layer of the **\<Column>** or **\<Row>** component.
Vertical Scroll:
```ts
@Entry
@Component
struct ScrollExample {
scroller: Scroller = new Scroller();
private arr: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
build() {
Scroll(this.scroller) {
Column() {
ForEach(this.arr, (item) => {
Text(item.toString())
.width('90%')
.height(150)
.backgroundColor(0xFFFFFF)
.borderRadius(15)
.fontSize(16)
.textAlign(TextAlign.Center)
.margin({ top: 10 })
}, item => item)
}.width('100%')
}
.backgroundColor(0xDCDCDC)
.scrollable(ScrollDirection.Vertical) // Vertical scrolling.
.scrollBar(BarState.On) // Scrollbar status.
.scrollBarColor(Color.Gray) // Scrollbar color.
.scrollBarWidth(30) // Scrollbar width.
.edgeEffect(EdgeEffect.Spring) // Rebound effect when the scrollbar has reached the edge.
}
}
```
![](figures/scrollcolumn.gif)
Horizontal scrolling:
```ts
@Entry
@Component
struct ScrollExample {
scroller: Scroller = new Scroller();
private arr: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
build() {
Scroll(this.scroller) {
Row() {
ForEach(this.arr, (item) => {
Text(item.toString())
.height('90%')
.width(150)
.backgroundColor(0xFFFFFF)
.borderRadius(15)
.fontSize(16)
.textAlign(TextAlign.Center)
.margin({ left: 10 })
}, item => item)
}.height('100%')
}
.backgroundColor(0xDCDCDC)
.scrollable(ScrollDirection.Horizontal) // Horizontal scrolling.
.scrollBar(BarState.On) // Scrollbar status.
.scrollBarColor(Color.Gray) // Scrollbar color
.scrollBarWidth(30) // Scrollbar width.
.edgeEffect(EdgeEffect.Spring) // Rebound effect when the scrollbar has reached the edge.
}
}
```
![](figures/scrollrow.gif)
# Media Query
[Media queries](../reference/apis/js-apis-mediaquery.md) are at the core of responsive design and widely used on mobile devices. You can use them to create different application styles depending on the device type or device state. Specifically, media queries allow you to:
1. Design a layout style based on the device and application attributes (such as display area, dark light color, and resolution).
2. Update the page layout to adapt to dynamic screen changes (for example, screen splitting or switching between landscape and portrait modes).
## Usage
Invoke the **mediaquery** API to set the media query condition and the callback, and change the page layout or implement service logic in the callback corresponding to the condition. The procedure is as follows:
1. Import the **mediaquery** module, as shown below:
```ts
import mediaquery from '@ohos.mediaquery'
```
2. Use the **matchMediaSync** API to set the media query condition and save the returned listener, as shown below:
```ts
listener = mediaquery.matchMediaSync('(orientation: landscape)')
```
3. Register the **onPortrait** callback using the saved listener, and change the page layout or implement service logic in the callback. When the media query condition is matched, the callback is triggered. The sample code is as follows:
```ts
onPortrait(mediaQueryResult) {
if (mediaQueryResult.matches) {
// do something here
} else {
// do something here
}
}
listener.on('change', onPortrait)
```
## Media Query Conditions
The media query condition consists of the media type (optional), logical operator, and media feature. The logical operator is used to connect different media types and media features. A media feature must be enclosed in parentheses (). There may be multiple media features. The specific rules are as follows:
### Syntax
```
[media-type] [and|not|only] [(media-feature)]
```
Examples are as follows:
**screen and (round-screen: true)**: The query is valid when the device screen is round.
**(max-height: 800)**: The query is valid when the height is less than or equal to 800.
**(height <= 800)**: The query is valid when the height is less than or equal to 800.
**screen and (device-type: tv) or (resolution < 2)**: The query is valid when the device type is TV or the device resolution is less than 2. This is a multi-condition query that contains multiple media features.
### media-type
| Type | Description |
| ------ | -------------- |
| screen | Media query based on screen-related parameters.|
### Media Logic Operation (and|or|not|only)
You can use logical operators (**and**, **or**, **not**, and **only**) to compose complex media queries. You can also combine them using comma (,). The following table describes the operators.
**Table 1** Media logical operators
| Type | Description |
| -------- | ---------------------------------------- |
| and | The **and** operator is used to combine multiple media features into one media query, in a logical AND operation. The query is valid only when all media features are true. It can also combine media types and media functions.<br>For example, **screen and (device-type: wearable) and (max-height: 600)** indicates that the query is valid when the device type is wearable and the maximum height of the application is 600 pixel units.|
| or | The **or** operator is used to combine multiple media features into one media query, in a logical OR operation. The query is valid if a media feature is true.<br>For example, **screen and (max-height: 1000) or (round-screen: true)** indicates that the query is valid when the maximum height of the application is 1000 pixel units or the device screen is round.|
| not | The **not** operator is used to perform a logical negation for a media query. **true** is returned if the query condition is not met. Otherwise, **false** is returned.<br>For example, **not screen and (min-height: 50) and (max-height: 600)** indicates that the query is valid when the height of the application is less than 50 pixel units or greater than 600 pixel units.<br>You must specify the media type when using the **not** operator.|
| only | The **only** operator applies the selected style only when the entire expression is matched. It can be used to prevent ambiguity on browsers of earlier versions. The statements that contain both media types and media features produce ambiguity when they are received by some browsers of earlier versions. For example:<br>screen and (min-height: 50)<br>The browsers of earlier versions would mislead this sentence into screen, causing the fact that the specified style is applied when only the media type is matched. In this case, the **only** operator can be used to avoid this problem.<br>You must specify the media type when using the **only** operator.|
| , (comma) | The **or** operator is used to combine multiple media features into one media query, in a logical OR operation. The query is valid if a media feature is true. The effect of a comma operator is equivalent to that of the **or** operator.<br>For example, **screen and (min-height: 1000), (round-screen: true)** indicates that the query is valid when the minimum height of the application is 1000 pixel units or the device screen is round.|
At MediaQuery Level 4, range query is imported so that you can use the operators including &lt;=, &gt;=, &lt;, and &gt; besides the max- and min-operators.
**Table 2** Logical operators for range query
| Type | Description |
| ----- | ---------------------------------------- |
| &lt; = | Less than or equal to, for example, **screen and (50 &lt;= height)**.|
| &gt; = | Greater than or equal to, for example, **screen and (600 &gt;= height)**.|
| &lt; | Less than, for example, **screen and (50 &lt; height)**.|
| &gt; | Greater than, for example, **screen and (600 &gt; height)**.|
### media-feature
| Type | Description |
| ----------------- | ------------------------------------------------------------ |
| height | Height of the display area on the application page. |
| min-height | Minimum height of the display area on the application page. |
| max-height | Maximum height of the display area on the application page. |
| width | Width of the display area on the application page. |
| min-width | Minimum width of the display area on the application page. |
| max-width | Maximum width of the display area on the application page. |
| resolution | Resolution of the device. The unit can be dpi, dppx, or dpcm. <br>- **dpi** indicates the number of physical pixels per inch. 1 dpi ≈ 0.39 dpcm.<br>- **dpcm** indicates the number of physical pixels per centimeter. 1 dpcm ≈ 2.54 dpi.<br>- **dppx** indicates the number of physical pixels in each pixel. (This unit is calculated based on this formula: 96 px = 1 inch, which is different from the calculation method of the px unit on the page.) 1 dppx = 96 dpi.|
| min-resolution | Minimum device resolution. |
| max-resolution | Maximum device resolution. |
| orientation | Screen orientation.<br>Options are as follows:<br>- orientation: portrait<br>- orientation: landscape|
| device-height | Height of the device. |
| min-device-height | Minimum height of the device. |
| max-device-height | Maximum height of the device. |
| device-width | Width of the device. |
| device-type | Type of the device.<br>Options: **default** and tablet |
| min-device-width | Minimum width of the device. |
| max-device-width | Maximum width of the device. |
| round-screen | Screen type. The value **true** means that the screen is round, and **false** means the opposite. |
| dark-mode | Whether the device is in dark mode. The value **true** means that the device is in dark mode, and **false** means the opposite. |
## Example Scenario
In the following example, media queries are used to apply different content and styles to the page text when the screen is switched between landscape and portrait modes.
```ts
import mediaquery from '@ohos.mediaquery'
let portraitFunc = null
@Entry
@Component
struct MediaQueryExample {
@State color: string = '#DB7093'
@State text: string = 'Portrait'
listener = mediaquery.matchMediaSync('(orientation: landscape)') // The query is valid when the device is in landscape mode.
onPortrait(mediaQueryResult) {
if (mediaQueryResult.matches) {
this.color = '#FFD700'
this.text = 'Landscape'
} else {
this.color = '#DB7093'
this.text = 'Portrait'
}
}
aboutToAppear() {
portraitFunc = this.onPortrait.bind(this) // Bind to the current application instance.
this.listener.on('change', portraitFunc)
}
build() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Text(this.text).fontSize(50).fontColor(this.color)
}
.width('100%').height('100%')
}
}
```
When the device is in landscape orientation, the text content is displayed in landscape mode in the color of #FFD700.
![en-us_image_0000001262954829](figures/en-us_image_0000001262954829.png)
When the device is not in landscape orientation, the text content is displayed in portrait mode in the color of #DB7093.
![en-us_image_0000001263074739](figures/en-us_image_0000001263074739.png)
# Statck Layout
The stack layout reserves an area on the screen to display elements in a component and allows the elements to be stacked.
You can implement a stack layout through the **[\<Stack>](../reference/arkui-ts/ts-container-stack.md)** component, which provides a stack container where child components are successively stacked and the latter one overwrites the previous one.
## Alignment
Child components in the container can be aligned in any of the alignment modes described in the table below.
|Name| Description| Image|
|---|---|---|
|TopStart| Top start.|![](figures/stacktopstart.png)|
|Top |Horizontally centered on the top.|![](figures/stacktop.png)|
|TopEnd| Top end.|![](figures/stacktopend.png)|
|Start| Vertically centered start.|![](figures/stackstart.png)|
|Center| Horizontally and vertically centered.|![](figures/stackcenter.png)|
|End| Vertically centered end.|![](figures/stackend.png)|
|BottomStart |Bottom start.|![](figures/stackbottomstart.png)|
|Bottom| Horizontally centered on the bottom.|![](figures/stackbottom.png)|
|BottomEnd| Bottom end.|![](figures/stackbottomend.png)|
## Z-order Control
You can use the **[zIndex](../reference/arkui-ts/ts-universal-attributes-z-order.md)** attribute to set the z-order of a component in the stacking context,
so as to create a custom stacking order of the child components. A larger **zIndex** value indicates a higher display level.
- In the statck layout, if the size of a component is greater than that of the one before it, the one before it is hidden.
```ts
Stack({ alignContent: Alignment.BottomStart }) {
Column() {
Text ('Stacked component 1').textAlign (TextAlign.End).fontSize (20)
}.width(100).height(100).backgroundColor(0xffd306)
Column() {
Text ('Stacked component 2').fontSize (20)
}.width(150).height(150).backgroundColor(Color.Pink)
Column() {
Text ('Stacked component 3').fontSize (20)
}.width(200).height(200).backgroundColor(Color.Grey)
}.margin({ top: 100 }).width(350).height(350).backgroundColor(0xe0e0e0)
```
![](figures/stack2.png)
In the preceding figure, the size of the stacked component 3 is greater than that of all the components before it. Therefore, the first two components are completely hidden. To show these components, modify their **zIndex** attribute settings.
```ts
Stack({ alignContent: Alignment.BottomStart }) {
Column() {
Text ('Stacked component 1').textAlign (TextAlign.End).fontSize (20)
}.width(100).height(100).backgroundColor(0xffd306).zIndex(2)
Column() {
Text ('Stacked component 2').fontSize (20)
}.width(150).height(150).backgroundColor(Color.Pink).zIndex(1)
Column() {
Text ('Stacked component 3').fontSize (20)
}.width(200).height(200).backgroundColor(Color.Grey)
}.margin({ top: 100 }).width(350).height(350).backgroundColor(0xe0e0e0)
```
![](figures/stack1.png)
# Overview
The ArkTS-based declarative development paradigm of ArkUI is a simplified, high-performance UI development framework for cross-device applications.
## Basic Capabilities
In ArkUI that uses the ArkTS-based declarative development paradigm, the programming mode is closer to natural semantics. You can intuitively describe the UI without caring about how the framework implements UI drawing and rendering, leading to simplified and efficient development. The UI capabilities are provided from three dimensions: component, animation, and state management. System capability APIs are also provided to allow for effortless invocation of system capabilities.
To accelerate your journey with ArkUI development, take time to learn the [ArkTS programming language](../quick-start/arkts-get-started.md) and the [built-in components](../reference/arkui-ts/ts-universal-events-click.md) provided in ArkUI.
- **Out-of-the-box components**
A wide range of preset system components are provided. You can set the rendering effect of these components in method chaining mode. You can combine system components to form custom components. In this way, page components are divided into independent UI units to implement independent creation, development, and reuse of different units on pages, making pages more engineering-oriented.
- **A diverse array of animation APIs**
By drawing from the standard SVG drawing capability and various open animation APIs, you can customize animation tracks by encapsulating physical models or calling the provided APIs.
- **State and data management**
State data management provides clear page update and rendering processes and pipes through decorators with different functions. State management covers UI component states and application states. With these features, you are able to build an application-wide data update and UI rendering process.
- **System capability APIs**
Development has never been so easy, with a diverse array of encapsulated system capability APIs, from UI design to system capability invoking.
## Overall Architecture
![en-us_image_0000001223287712](figures/en-us_image_0000001223287712.png)
- **Declarative UI frontend**
Provides basic language specifications of the UI development paradigm, built-in UI components, layouts, and animations, and multiple state management mechanisms, with a wide array of APIs for you to call as required.
- **Language runtime**
Provides parsing for the UI paradigm syntax, cross-language invoking, and a TS-based high-performance running environment.
- **Declarative UI backend engine**
Provides UI rendering pipelines that are compatible with different development paradigms, multiple basic components, layout calculation, dynamic effects, and interaction events, with state management and drawing capabilities.
- **Render engine**
Provides efficient drawing capabilities, which enable rendering instructions collected by the rendering pipeline to be drawn to the screen.
- **Porting layer**
Provides abstract APIs to connect to different systems, such as system rendering pipelines and lifecycle scheduling.
# Implementing Page Redirection and Data Transmission
This section describes how to implement page redirection and data transmission between pages:
1. Page redirection: Click a food item on the food list page to go to the food details page. Click the back button on the food details page to go back to the food list page.
2. Data transmission between pages: After you click a food item, **FoodDetail** receives data from the previous page and renders the corresponding food details page.
## Page Redirection
The declarative UI paradigm provides two mechanisms for page redirection:
1. **Navigator**: encapsulates the page routing capability. After the page target is specified, all child components in the page target have the routing capability.
2. Router APIs: called to implement various operations of page routing. You'll need to import **router** before calling the router APIs.
The procedure below uses these two mechanisms for redirection between the page list page and food details page.
1. Click **FoodListItem**. The **FoodDetail** page is displayed. Create a **Navigator** component in **FoodListItem** to enable its child components to have the routing function. The target page is **'pages/FoodDetail'**.
```ts
@Component
struct FoodListItem {
private foodItem: FoodData
build() {
Navigator({ target: 'pages/FoodDetail' }) {
Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
Image(this.foodItem.image)
.objectFit(ImageFit.Contain)
.height(40)
.width(40)
.backgroundColor('#FFf1f3f5')
.margin({ right: 16 })
Text(this.foodItem.name)
.fontSize(14)
.flexGrow(1)
Text(this.foodItem.calories + ' kcal')
.fontSize(14)
}
.height(64)
}
.margin({ right: 24, left:32 })
}
}
```
![en-us_image_0000001223127744](figures/en-us_image_0000001223127744.gif)
2. Click **FoodGridItem**. The **FoodDetail** page is displayed. Import the **router** module, and then call the **push** API of this module to push the **FoodDetail** page to the route stack to implement page redirection.
```ts
import router from '@ohos.router'
@Component
struct FoodGridItem {
private foodItem: FoodData
build() {
Column() {
......
}
.height(184)
.width('100%')
.onClick(() => {
router.pushUrl({ url: 'pages/FoodDetail' })
})
}
}
```
![en-us_image_0000001267607909](figures/en-us_image_0000001267607909.gif)
3. Add the icon for returning from the **FoodDetail** page to the food list page. Save the **Back.png** file to the **resources** > **base** > **media** directory. Create a custom component **PageTitle**, which contains the back icon and Food Detail text. Call the **router.back()** API of the router to display the top page of the route stack, that is, the upper-level page.
```ts
// FoodDetail.ets
import router from '@ohos.router'
@Component
struct PageTitle {
build() {
Flex({ alignItems: ItemAlign.Start }) {
Image($r('app.media.Back'))
.width(21.8)
.height(19.6)
Text('Food Detail')
.fontSize(21.8)
.margin({left: 17.4})
}
.height(61)
.backgroundColor('#FFedf2f5')
.padding({ top: 13, bottom: 15, left: 28.3 })
.onClick(() => {
router.back()
})
}
}
```
4. Create the Stack component in the **FoodDetail** component, including the **FoodImageDisplay** and **PageTitle** child components. Set the alignment mode to **TopStart**.
```ts
@Entry
@Component
struct FoodDetail {
build() {
Column() {
Stack( { alignContent: Alignment.TopStart }) {
FoodImageDisplay()
PageTitle()
}
ContentTable()
}
.alignItems(HorizontalAlign.Center)
}
}
```
![en-us_image_0000001267767881](figures/en-us_image_0000001267767881.png)
## Data Transmission Between Pages
We have implemented the redirection and going back of the **FoodCategoryList** and **FoodDetail** pages. At this point, the tomato details page is displayed no matter which **FoodListItem**/**FoodGridItem** is clicked. This is because the data transmission between pages is not yet configured. To configure data transmission between pages, set the routing with parameters as follows:
1. Set the **params** attribute, which accepts the key-value object, in the **Navigator** of the **FoodListItem** component.
```ts
// FoodList.ets
@Component
struct FoodListItem {
private foodItem: FoodData
build() {
Navigator({ target: 'pages/FoodDetail' }) {
......
}
.params({ foodData: this.foodItem })
}
}
```
The router API called by **FoodGridItem** also has supports redirection with parameters. The method of using the router API is similar to that of using the **Navigator**.
```ts
router.pushUrl({
url: 'pages/FoodDetail',
params: { foodData: this.foodItem }
})
```
2. Import the **FoodData** class to the **FoodDetail** page and add the **foodItem** member variable to the **FoodDetail** component.
```ts
// FoodDetail.ets
import { FoodData } from '../model/FoodData'
@Entry
@Component
struct FoodDetail {
private foodItem: FoodData
build() {
......
}
}
```
3. Obtain the value of **foodData**. Call **router.getParams()['foodData']** to obtain the data corresponding to **foodData** carried when the **FoodCategoryList** page is displayed.
```ts
@Entry
@Component
struct FoodDetail {
private foodItem: FoodData = router.getParams()['foodData']
build() {
......
}
}
```
4. Re-build the components on the **FoodDetail** page. During building, the food information on the **FoodDetail** page is all directly declared constants. You need to use the passed **FoodData** data to assign a new value to the constants. The sample code is as follows:
```ts
@Component
struct PageTitle {
build() {
Flex({ alignItems: ItemAlign.Start }) {
Image($r('app.media.Back'))
.width(21.8)
.height(19.6)
Text('Food Detail')
.fontSize(21.8)
.margin({left: 17.4})
}
.height(61)
.backgroundColor('#FFedf2f5')
.padding({ top: 13, bottom: 15, left: 28.3 })
.onClick(() => {
router.back()
})
}
}
@Component
struct FoodImageDisplay {
private foodItem: FoodData
build() {
Stack({ alignContent: Alignment.BottomStart }) {
Image(this.foodItem.image)
.objectFit(ImageFit.Contain)
Text(this.foodItem.name)
.fontSize(26)
.fontWeight(500)
.margin({ left: 26, bottom: 17.4 })
}
.height(357)
.backgroundColor('#FFedf2f5')
}
}
@Component
struct ContentTable {
private foodItem: FoodData
@Builder IngredientItem(title:string, name: string, value: string) {
Flex() {
Text(title)
.fontSize(17.4)
.fontWeight(FontWeight.Bold)
.layoutWeight(1)
Flex() {
Text(name)
.fontSize(17.4)
.flexGrow(1)
Text(value)
.fontSize(17.4)
}
.layoutWeight(2)
}
}
build() {
Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Start }) {
this.IngredientItem('Calories', 'Calories', this.foodItem.calories + 'kcal')
this.IngredientItem('Nutrition', 'Protein', this.foodItem.protein + 'g')
this.IngredientItem('', 'Fat', this.foodItem.fat + 'g')
this.IngredientItem('', 'Carbohydrates', this.foodItem.carbohydrates + 'g')
this.IngredientItem('', 'VitaminC', this.foodItem.vitaminC + 'mg')
}
.height(280)
.padding({ top: 30, right: 30, left: 30 })
}
}
@Entry
@Component
struct FoodDetail {
private foodItem: FoodData = router.getParams()['foodData']
build() {
Column() {
Stack( { alignContent: Alignment.TopStart }) {
FoodImageDisplay({ foodItem: this.foodItem })
PageTitle()
}
ContentTable({ foodItem: this.foodItem })
}
.alignItems(HorizontalAlign.Center)
}
}
```
# Recommendations for Improving Performance
Poor-performing code may work, but will take away from your application performance. This topic presents a line-up of recommendations that you can take to improve your implementation, thereby avoiding possible performance drop.
## Lazy Loading
When developing a long list, use of loop rendering, as in the code snippet below, can greatly slow down page loading and increase server load.
```ts
@Entry
@Component
struct MyComponent {
@State arr: number[] = Array.from(Array(100), (v,k) =>k); // Construct an array of 0 to 99.
build() {
List() {
ForEach(this.arr, (item: number) => {
ListItem() {
Text(`item value: ${item}`)
}
}, (item: number) => item.toString())
}
}
}
```
The preceding code snippet loads all of the 100 list elements at a time during page loading. This is generally not desirable. Instead, what we need is to load data from the data source and create corresponding components on demand. This can be achieved through lazy loading. The sample code is as follows:
```ts
class BasicDataSource implements IDataSource {
private listeners: DataChangeListener[] = []
public totalCount(): number {
return 0
}
public getData(index: number): any {
return undefined
}
registerDataChangeListener(listener: DataChangeListener): void {
if (this.listeners.indexOf(listener) < 0) {
console.info('add listener')
this.listeners.push(listener)
}
}
unregisterDataChangeListener(listener: DataChangeListener): void {
const pos = this.listeners.indexOf(listener);
if (pos >= 0) {
console.info('remove listener')
this.listeners.splice(pos, 1)
}
}
notifyDataReload(): void {
this.listeners.forEach(listener => {
listener.onDataReloaded()
})
}
notifyDataAdd(index: number): void {
this.listeners.forEach(listener => {
listener.onDataAdd(index)
})
}
notifyDataChange(index: number): void {
this.listeners.forEach(listener => {
listener.onDataChange(index)
})
}
notifyDataDelete(index: number): void {
this.listeners.forEach(listener => {
listener.onDataDelete(index)
})
}
notifyDataMove(from: number, to: number): void {
this.listeners.forEach(listener => {
listener.onDataMove(from, to)
})
}
}
class MyDataSource extends BasicDataSource {
private dataArray: string[] = ['item value: 0', 'item value: 1', 'item value: 2']
public totalCount(): number {
return this.dataArray.length
}
public getData(index: number): any {
return this.dataArray[index]
}
public addData(index: number, data: string): void {
this.dataArray.splice(index, 0, data)
this.notifyDataAdd(index)
}
public pushData(data: string): void {
this.dataArray.push(data)
this.notifyDataAdd(this.dataArray.length - 1)
}
}
@Entry
@Component
struct MyComponent {
private data: MyDataSource = new MyDataSource()
build() {
List() {
LazyForEach(this.data, (item: string) => {
ListItem() {
Row() {
Text(item).fontSize(20).margin({ left: 10 })
}
}
.onClick(() => {
this.data.pushData('item value: ' + this.data.totalCount())
})
}, item => item)
}
}
}
```
![LazyForEach1](figures/LazyForEach1.gif)
The preceding code initializes only three list elements during page loading and loads a new list item each time a list element is clicked.
## Prioritizing Conditional Rendering over Visibility Control
Use of the visibility attribute to hide or show a component, as in the code snippet below, results in re-creation of the component, leading to performance drop.
```ts
@Entry
@Component
struct MyComponent {
@State isVisible: Visibility = Visibility.Visible;
build() {
Column() {
Button ("Show/Hide")
.onClick(() => {
if (this.isVisible == Visibility.Visible) {
this.isVisible = Visibility.None
} else {
this.isVisible = Visibility.Visible
}
})
Row().visibility(this.isVisible)
.width(300).height(300).backgroundColor(Color.Pink)
}.width('100%')
}
}
```
To avoid the preceding issue, use the **if** statement instead. The sample code is as follows:
```ts
@Entry
@Component
struct MyComponent {
@State isVisible: boolean = true;
build() {
Column() {
Button ("Show/Hide")
.onClick(() => {
this.isVisible = !this.isVisible
})
if (this.isVisible) {
Row()
.width(300).height(300).backgroundColor(Color.Pink)
}
}.width('100%')
}
}
```
![isVisible](figures/isVisible.gif)
## Prioritizing Flex over Column/Row
By default, the flex container needs to re-lay out flex items to comply with the **flexShrink** and **flexGrow** settings. This may result in drop in rendering performance.
```ts
@Entry
@Component
struct MyComponent {
build() {
Flex({ direction: FlexDirection.Column }) {
Flex().width(300).height(200).backgroundColor(Color.Pink)
Flex().width(300).height(200).backgroundColor(Color.Yellow)
Flex().width(300).height(200).backgroundColor(Color.Grey)
}
}
}
```
To avoid the preceding issue, replace **Flex** with **Column** and **Row**, which can create the same page layout as **Flex** does.
```ts
@Entry
@Component
struct MyComponent {
build() {
Column() {
Row().width(300).height(200).backgroundColor(Color.Pink)
Row().width(300).height(200).backgroundColor(Color.Yellow)
Row().width(300).height(200).backgroundColor(Color.Grey)
}
}
}
```
![flex1](figures/flex1.PNG)
## Setting Width and Height for \<List> Components
When a **\<List>** component is nested within a **\<Scroll>** component, all of its content will be loaded if its width and height is not specified, which may result in performance drop.
```ts
@Entry
@Component
struct MyComponent {
private arr: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
build() {
Scroll() {
List() {
ForEach(this.arr, (item) => {
ListItem() {
Text(`item value: ${item}`).fontSize(30).margin({ left: 10 })
}.height(100)
}, (item) => item.toString())
}
}.backgroundColor(Color.Pink)
}
}
```
Therefore, in the above scenario, you are advised to set the width and height for the **\<List>** component as follows:
```ts
@Entry
@Component
struct MyComponent {
private arr: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
build() {
Scroll() {
List() {
ForEach(this.arr, (item) => {
ListItem() {
Text(`item value: ${item}`).fontSize(30).margin({ left: 10 })
}.height(100)
}, (item) => item.toString())
}.width('100%').height(500)
}.backgroundColor(Color.Pink)
}
}
```
![list1](figures/list1.gif)
## Minimizing White Blocks During Swiping
To minimize white blocks during swiping, expand the UI loading range by increasing the value of **cachedCount** for the **\<List>** and **\<Grid>** components. **cachedCount** indicates the number of list or grid items preloaded outside of the screen.
If an item needs to request an online image, set **cachedCount** as appropriate so that the image is downloaded in advance before the item comes into view on the screen, thereby reducing the number of white blocks.
The following is an example of using **cachedCount**:
```ts
@Entry
@Component
struct MyComponent {
private source: MyDataSource = new MyDataSource();
build() {
List() {
LazyForEach(this.source, item => {
ListItem() {
Text("Hello" + item)
.fontSize(50)
.onAppear(() => {
console.log("appear:" + item)
})
}
})
}.cachedCount(3) // Increase the number of list or grid items preloaded outside of the screen.
}
}
class MyDataSource implements IDataSource {
data: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
public totalCount(): number {
return this.data.length
}
public getData(index: number): any {
return this.data[index]
}
registerDataChangeListener(listener: DataChangeListener): void {
}
unregisterDataChangeListener(listener: DataChangeListener): void {
}
}
```
![list2](figures/list2.gif)
**Instructions**
A greater **cachedCount** value may result in higher CPU and memory overhead of the UI. Adjust the value by taking into account both the comprehensive performance and user experience.
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册