未验证 提交 0a074838 编写于 作者: O openharmony_ci 提交者: Gitee

!12028 删除冗余文件和图片

Merge pull request !12028 from ester.zhou/C-1201
# @Builder
The **@Builder** decorated method is used to define the declarative UI description of a component and quickly generate multiple layouts in a custom component. If a custom component is used in the **@Builder** decorated method, this component will be re-created each time the method is invoked. The functionality and syntax of the **@Builder** decorator are the same as those of the **build** function.
```ts
// xxx.ets
@Component
struct CompB {
@State CompValue: string = '';
aboutToAppear() {
console.info('CompB aboutToAppear.');
}
aboutToDisappear() {
console.info('CompB aboutToDisappear.');
}
build() {
Column() {
Button(this.CompValue);
}
}
}
@Entry
@Component
struct CompA {
size1: number = 100;
@State CompValue1: string = "Hello,CompValue1";
@State CompValue2: string = "Hello,CompValue2";
@State CompValue3: string = "Hello,CompValue3";
// Use a custom component in the @Builder decorated method.
@Builder CompC(value: string) {
CompB({ CompValue: value });
}
@Builder SquareText(label: string) {
Text(label)
.width(1 * this.size1)
.height(1 * this.size1)
}
@Builder RowOfSquareTexts(label1: string, label2: string) {
Row() {
this.SquareText(label1)
this.SquareText(label2)
}
.width(2 * this.size1)
.height(1 * this.size1)
}
build() {
Column() {
Row() {
this.SquareText("A")
this.SquareText("B")
// or as long as tsc is used
}
.width(2 * this.size1)
.height(1 * this.size1)
this.RowOfSquareTexts("C", "D")
Column() {
// Use the custom component in the @Builder decorated method three times.
this.CompC(this.CompValue1);
this.CompC(this.CompValue2);
this.CompC(this.CompValue3);
}
.width(2 * this.size1)
.height(2 * this.size1)
}
.width(2 * this.size1)
.height(2 * this.size1)
}
}
```
## @BuilderParam<sup>8+<sup>
The **@BuilderParam** decorator is used to modify the function type attributes (for example, `@BuilderParam content: () => any;`) in a custom component. When the custom component is initialized, the attributes modified by the **@BuilderParam** decorator must be assigned values.
### Background
In certain circumstances, you may need to add a specific function, such as a click-to-jump action, to a custom component. However, embedding an event method directly inside of the component will add the function to all places where the component is initialized. This is where the **@BuilderParam** decorator comes into the picture. When initializing a custom component, you can assign a **@Builder** decorated method to the **@BuilderParam** decorated attribute, thereby adding the specific function to the custom component.
### Component Initialization Through Parameters
When initializing a custom component through parameters, assign a **@Builder** decorated method to the **@BuilderParam** decorated attribute — **content**, and call the value of **content** in the custom component. If no parameter is passed when assigning a value to the **@BuilderParam** decorated attribute (for example, `content: this.specificParam`), define the type of the attribute as a function without a return value (for example, `@BuilderParam content: () => void`). If any parameter is passed when assigning a value to the **@BuilderParam** decorated attribute (for example, `callContent: this.specificParam1("111")`), define the type of the attribute as `any` (for example,`@BuilderParam callContent: any;`).
```ts
// xxx.ets
@Component
struct CustomContainer {
header: string = "";
@BuilderParam noParam: () => void;
@BuilderParam withParam: any;
footer: string = "";
build() {
Column() {
Text(this.header)
.fontSize(50)
this.noParam()
this.withParam()
Text(this.footer)
.fontSize(50)
}
}
}
@Entry
@Component
struct CustomContainerUser {
@Builder specificNoParam() {
Column() {
Text("noParam").fontSize(50)
}
}
@Builder SpecificWithParam(label: string) {
Column() {
Text(label).fontSize(50)
}
}
build() {
Column() {
CustomContainer({
header: "Header",
noParam: this.specificNoParam,
withParam: this.SpecificWithParam("WithParam"),
footer: "Footer",
})
}
}
}
```
### Component Initialization Through Trailing Closure
In a custom component, use the **@BuilderParam** decorated attribute to receive a trailing closure. When the custom component is initialized, the component name is followed by a pair of braces ({}) to form a trailing closure (`CustomComponent(){}`). You can consider a trailing closure as a container and add content to it. For example, you can add a component (`{Column(){Text("content")}`) to a trailing closure. The syntax of the closure is the same as that of the **build** function. In this scenario, the custom component has one and only one **@BuilderParam** decorated attribute.
Example: Add a **\<Column>** component and a click event to the closure, and call the **specificParam** method decorated by **@Builder** in the new **\<Column>** component. After the **\<Column>** component is clicked, the value of the component's `header` attribute will change to `changeHeader`. In addition, when the component is initialized, the content of the trailing closure will be assigned to the `closer` attribute decorated by **@BuilderParam**.
```ts
// xxx.ets
@Component
struct CustomContainer {
header: string = "";
@BuilderParam closer: () => void;
build() {
Column() {
Text(this.header)
.fontSize(50)
this.closer()
}
}
}
@Builder function specificParam(label1: string, label2: string) {
Column() {
Text(label1)
.fontSize(50)
Text(label2)
.fontSize(50)
}
}
@Entry
@Component
struct CustomContainerUser {
@State text: string = "header"
build() {
Column() {
CustomContainer({
header: this.text,
}){
Column(){
specificParam("111", "22")
}.onClick(()=>{
this.text = "changeHeader"
})
}
}
}
}
```
# Component Creation and Re-initialization
## 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 preset &lt;Column&gt; component.
1. Create a preset &lt;Text&gt; component, set the text content to be displayed, and add the &lt;Text&gt; component instance to the &lt;Column&gt; component.
2. Create the component on the true branch based on the if condition.
1. Create a preset &lt;Image&gt; component and set the image source address.
2. Create a TimerComponent using the given constructor.
1. Create a TimerComponent object.
2. Initialize the values of member variables locally.
3. Use the parameters provided by the TimerComponent constructor to update the values of member variables.
4. Execute the aboutToAppear function of TimerComponent.
5. Execute the build function of TimerComponent to create the corresponding UI description structure.
3. Create a preset &lt;Button&gt; component and set the corresponding content.
## Status Update
When a 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 preset &lt;Column&gt; component is reused by the framework and reinitialized.
4. The child components of &lt;Column&gt; reuse and reinitialize the objects in the memory.
1. Reuse the preset &lt;Text&gt; component after re-initializing the component using new text content.
2. Reuse the component on the false branch based on the if condition.
1. Destroy the components on the original true branch as these components are no longer used.
1. Destroy the created preset &lt;image&gt; component instance.
2. Destroy the TimerComponent component instance, and call the aboutToDisappear function.
2. Create components on the false branch.
1. Create a preset &lt;Image&gt; component and set the image source address.
2. Create a TimerComponent again using the given constructor.
3. Initialize the newly created TimerComponent and call the aboutToAppear and build functions.
3. Reuse the preset &lt;Button&gt; component, with the new image source address.
## Example
```ts
// xxx.ets
@Entry
@Component
struct ParentComp {
@State isCountDown: boolean = true
build() {
Column() {
Text(this.isCountDown ? 'Count Down' : 'Stopwatch')
if (this.isCountDown) {
Image('countdown.png')
TimerComponent({counter: 10, changePerSec: -1, showInColor: Color.Red})
} else {
Image('stopwatch.png')
TimerComponent({counter: 0, changePerSec: +1, showInColor: Color.Black })
}
Button(this.isCountDown ? 'Switch to Stopwatch' : 'Switch to Count Down')
.onClick(() => {this.isCountDown = !this.isCountDown})
}
}
}
// Manage and display a count down / stop watch timer
@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)
}
aboutToAppear() {
this.timerId = setInterval(() => {this.counter += this.changePerSec}, 1000)
}
aboutToDisappear() {
if (this.timerId > 0) {
clearTimeout(this.timerId)
this.timerId = -1
}
}
}
```
# Initialization of Custom Components' Member Variables
The member variables of a component can be initialized in either of the following ways:
- Local initialization. For example:
```ts
@State counter: Counter = new Counter()
```
- Initialization using constructor parameters. For example:
```ts
MyComponent({counter: $myCounter})
```
The allowed method depends on the decorator of the state variable, as shown in the following table.
| Decorator Type | Local Initialization | Initialization Using Constructor Parameters |
| -------- | -------- | -------- |
| @State | Mandatory | Optional |
| @Prop | Forbidden | Mandatory |
| @Link | Forbidden | Mandatory |
| @StorageLink | Mandatory | Forbidden |
| @StorageProp | Mandatory | Forbidden |
| @Provide | Mandatory | Optional |
| @Consume | Forbidden | Forbidden |
| @ObjectLink | Forbidden | Mandatory |
| Normal member variable | Recommended | Optional |
As indicated by the preceding table:
- The @State decorated variables need to be initialized locally. The initial value can be overwritten by the constructor parameter.
- The @Prop and @Link decorated variables must be initialized only by constructor parameters.
Comply with the following rules when using constructors to initialize member variables:
| From the Variable in the Parent Component (Below) to the Variable in the Child Component (Right) | @State | @Link | @Prop | Normal Variable |
| -------- | -------- | -------- | -------- | -------- |
| @State | Not allowed | Allowed | Allowed | Allowed |
| @Link | Not allowed | Allowed | Not recommended | Allowed |
| @Prop | Not allowed | Not allowed | Allowed | Allowed |
| @StorageLink | Not allowed | Allowed | Not allowed | Allowed |
| @StorageProp | Not allowed | Not allowed | Not allowed | Allowed |
| Normal variable | Allowed | Not allowed | Not allowed | Allowed |
As indicated by the preceding table:
- The normal variables of the parent component can be used to initialize the @State decorated variables of the child component, but not the @Link or @Prop decorated variables.
- The @State decorated variable of the parent component can be used to initialize the @Prop, @Link (using $), or normal variables of the child component, but not the @State decorated variables of the child component.
- The @Link decorated variables of the parent component can be used to initialize the @Link decorated or normal variables of the child component. However, initializing the @State decorated members of the child component can result in a syntax error. In addition, initializing the @Prop decorated variables is not recommended.
- The @Prop decorated variables of the parent component can be used to initialize the normal variables or @Prop decorated variables of the child component, but not the @State or @Link decorated variables.
- Passing @StorageLink and @StorageProp from the parent component to the child component is prohibited.
- In addition to the preceding rules, the TypeScript strong type rules need to be followed.
## Example
```ts
// xxx.ets
class ClassA {
public a:number
constructor(a: number) {
this.a = a
}
}
@Entry
@Component
struct Parent {
@State parentState: ClassA = new ClassA(1)
build() {
Column() {
Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
CompA({ aState: new ClassA(2), aLink: $parentState })
}
Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
CompA({ aLink: $parentState })
}
Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
CompA({ aState: new ClassA(3), aLink: $parentState })
}
}
}
}
@Component
struct CompA {
@State aState: any = false
@Link aLink: ClassA
build() {
Column() {
CompB({ bLink: $aLink, bProp: this.aState })
CompB({ bLink: $aState, bProp: false })
}
}
}
@Component
struct CompB {
@Link bLink: ClassA
@Prop bProp: boolean
build() {
Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
Text(JSON.stringify(this.bLink.a)).fontSize(30)
Text(JSON.stringify(this.bProp)).fontSize(30).fontColor(Color.Red)
}.margin(10)
}
}
```
# 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.
## Lifecycle Callback Definitions
| Name | Description |
| ---------------- | ------------------------------------------------------------ |
| aboutToAppear | 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.|
| aboutToDisappear | 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.|
| onPageShow | Invoked 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 | Invoked 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 | Invoked when a user clicks the back button. Only the custom components decorated by **@Entry** take effect.<br>The value **true** is returned if the page processes the return logic instead of performing page routing.<br>The value **false** is returned if the default return logic is used.<br>If no value is returned, the default return logic is used.|
## Example
```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)
}
this.countDownFrom -= 1
}, 1000) // decr counter by 1 every second
}
aboutToDisappear(): void {
if (this.timerId > 0) {
clearTimeout(this.timerId)
this.timerId = -1
}
}
build() {
Text(`${this.countDownFrom} sec left`)
}
}
```
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.
> **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.
# Directory Structure
The following figure shows the typical directory structure of the **ets** module (in **entry/src/main**) for an application with feature abilities (FAs).
![en-us_image_0000001222967752](figures/en-us_image_0000001222967752.png)
The **ets** directory contains the following files:
**.ets** files: Extended TypeScript (eTS) files that describe the UI layouts, styles, event interactions, and page logics.
Functions of the folders and files are as follows:
- The **app.ets** file manages global application logics and lifecycles.
- The **pages** directory stores all pages.
- The **common** directory stores common code files, such as files of custom components and public methods.
> **NOTE**
>
> - For details about the **resources** directory in **src/main**, see [Resource File Categories](ui-ts-basic-resource-file-categories.md).
>- TypeScript and JavaScript files can be imported as page files.
"js" tag configuration:
Configure the **"js"** tag in the configuration file of your application. The **"js"** tag contains the instance name, page route, and window configuration information.
> **NOTE**
>
> For details about the **"js"** tag in the FA model, see [Table 22 Internal structure of the js attribute](../quick-start/package-structure.md#internal-structure-of-the-js-attribute).
>
> For details about the **"js"** tag in the stage model, see [Table 3 Internal structure of the module tag](../quick-start/stage-structure.md#internal-structure-of-the-module-tag).
# Rules for Accessing Application Code Files
The application code files can be accessed in the following ways:
- Use a relative path to reference the code file. For example, if the upper-level directory is **../common/utils/utils**, use **./common/utils/utils** for the current directory.
- Use the root path of the current module to reference the code file, for example, **common/utils/utils**.
- Store common code files in the **common** directory.
## Example
```ts
// xxx.ets
import { FoodData, FoodList } from "../common/utils/utils";
@Entry
@Component
struct FoodCategoryList {
private foodItems: FoodData[] = [
new FoodData("Tomato"),
new FoodData("Strawberry"),
new FoodData("Cucumber")
]
build() {
Column() {
FoodList({ foodItems: this.foodItems })
}
}
}
```
Example for importing a code file:
```ts
//common/utils/utils.ets
export class FoodData {
name: string;
constructor(name: string) {
this.name = name;
}
}
@Component
export struct FoodList {
private foodItems: FoodData[]
build() {
Column() {
Flex({justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center}) {
Text('Food List')
.fontSize(20)
}
.width(200)
.height(56)
.backgroundColor('#FFf1f3f5')
List() {
ForEach(this.foodItems, item => {
ListItem() {
Text(item.name)
.fontSize(14)
}
}, item => item.toString())
}
}
}
}
```
# 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)
}
}
}
```
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** conditional 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%')
}
}
```
## 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)
}
}
}
```
## 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)
}
}
```
## Minimizing White Blocks During Swiping
To minimize white blocks durign 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 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(100)
.onAppear(()=>{
console.log("appear:" + item)
})
}
})
}.cachedCount(3) // Increase the value to enlarge the range of logs that appear.
}
}
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 {
}
}
```
**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.
\ No newline at end of file
# Pixel Units
The framework provides four pixel units, with vp as the reference data unit.
| Name | Description |
| ---- | ---------------------------------------- |
| px | Physical pixel unit of the screen. |
| vp | Pixel unit specific to the screen density. Pixels in this unit are converted into physical pixels of the screen based on the screen pixel density. This unit is used for values whose unit is not specified. |
| fp | Font pixel, which is similar to vp and varies according to the system font size. |
| lpx | Logical pixel unit of the window. It is the ratio of the actual screen width to the logical width (configured by **[designWidth](../quick-start/package-structure.md)**). For example, if **designWidth** is set to **720** (default value), then 1lpx is equal to 2px for a screen with an actual width of 1440 physical pixels.|
## Pixel Unit Conversion
Conversion between px and other pixel units is supported.
| API | Description |
| ---------------------------------------- | ---------------------- |
| vp2px(value : number) : number | Converts a value in units of vp to a value in units of px. |
| px2vp(value : number) : number | Converts a value in units of px to a value in units of vp. |
| fp2px(value : number) : number | Converts a value in units of fp to a value in units of px. |
| px2fp(value : number) : number | Converts a value in units of px to a value in units of fp. |
| lpx2px(value : number) : number | Converts a value in units of lpx to a value in units of px.|
| px2lpx(value : number) : number | Converts a value in units of px to a value in units of lpx.|
## Example
```ts
// xxx.ets
@Entry
@Component
struct Example {
build() {
Column() {
Flex({ wrap: FlexWrap.Wrap }) {
Column() {
Text("width(220)")
.width(220).height(40).backgroundColor(0xF9CF93)
.textAlign(TextAlign.Center).fontColor(Color.White).fontSize('12vp')
}.margin(5)
Column() {
Text("width('220px')")
.width('220px').height(40).backgroundColor(0xF9CF93)
.textAlign(TextAlign.Center).fontColor(Color.White)
}.margin(5)
Column() {
Text("width('220vp')")
.width('220vp').height(40).backgroundColor(0xF9CF93)
.textAlign(TextAlign.Center).fontColor(Color.White).fontSize('12vp')
}.margin(5)
Column() {
Text("width('220lpx') designWidth:720")
.width('220lpx').height(40).backgroundColor(0xF9CF93)
.textAlign(TextAlign.Center).fontColor(Color.White).fontSize('12vp')
}.margin(5)
Column() {
Text("width(vp2px(220) + 'px')")
.width(vp2px(220) + 'px').height(40).backgroundColor(0xF9CF93)
.textAlign(TextAlign.Center).fontColor(Color.White).fontSize('12vp')
}.margin(5)
Column() {
Text("fontSize('12fp')")
.width(220).height(40).backgroundColor(0xF9CF93)
.textAlign(TextAlign.Center).fontColor(Color.White).fontSize('12fp')
}.margin(5)
}.width('100%')
}
}
}
```
![en-us_image_0000001267607893](figures/en-us_image_0000001267607893.gif)
# Resource Access
## Accessing Application Resources
To reference an application resource in a project, use the `"$r('app.type.name')"` format. **app** indicates the resource defined in the **resources** directory of the application. **type** indicates the resource type (or the location where the resource is stored). The value can be **color**, **float**, **string**, **plural**, or **media**. **name** indicates the resource name, which you set when defining the resource.
When referencing resources in the **rawfile** sub-directory, use the ```"$rawfile('filename')"``` format. Currently, **$rawfile** allows only the **\<Image>** component to reference image resources. **filename** indicates the relative path of a file in the **rawfile** directory, and the file name must contain the file name extension. Note that the relative path cannot start with a slash (/).
> **NOTE**
>
> Resource descriptors accept only strings, such as `'app.type.name'`, and cannot be combined.
>
> `$r` returns a **Resource** object. To obtain the corresponding string, use [getString](../reference/apis/js-apis-resource-manager.md#getstring).
In the **.ets** file, you can use the resources defined in the **resources** directory.
```ts
Text($r('app.string.string_hello'))
.fontColor($r('app.color.color_hello'))
.fontSize($r('app.float.font_hello'))
}
Text($r('app.string.string_world'))
.fontColor($r('app.color.color_world'))
.fontSize($r('app.float.font_world'))
}
Text($r('app.string.message_arrive', "five of the clock")) // Reference string resources. The second parameter of $r is used to replace %s.
.fontColor($r('app.color.color_hello'))
.fontSize($r('app.float.font_hello'))
}
Text($r('app.plural.eat_apple', 5, 5)) // Reference plural resources. The first parameter indicates the plural resource, and the second parameter indicates the number of plural resources. The third parameter indicates the substitute of %d.
.fontColor($r('app.color.color_world'))
.fontSize($r('app.float.font_world'))
}
Image($r('app.media.my_background_image')) // Reference media resources.
Image($rawfile('test.png')) // Reference an image in the rawfile directory.
Image($rawfile('newDir/newTest.png')) // Reference an image in the rawfile directory.
```
## Accessing System Resources
System resources include colors, rounded corners, fonts, spacing, character strings, and images. By using system resources, you can develop different applications with the same visual style.
To reference a system resource, use the ```"$r('sys.type.resource_id')"``` format. Wherein: **sys** indicates a system resource; **type** indicates the resource type, which can be **color**, **float**, **string**, or **media**; **resource_id** indicates the resource ID.
```ts
Text('Hello')
.fontColor($r('sys.color.ohos_id_color_emphasize'))
.fontSize($r('sys.float.ohos_id_text_size_headline1'))
.fontFamily($r('sys.string.ohos_id_text_font_family_medium'))
.backgroundColor($r('sys.color.ohos_id_color_palette_aux1'))
Image($r('sys.media.ohos_app_icon'))
.border({color: $r('sys.color.ohos_id_color_palette_aux1'), radius: $r('sys.float.ohos_id_corner_radius_button'), width: 2})
.margin({top: $r('sys.float.ohos_id_elements_margin_horizontal_m'), bottom: $r('sys.float.ohos_id_elements_margin_horizontal_l')})
.height(200)
.width(300)
```
## Resource File Examples
The content of the **color.json** file is as follows:
```json
{
"color": [
{
"name": "color_hello",
"value": "#ffff0000"
},
{
"name": "color_world",
"value": "#ff0000ff"
}
]
}
```
The content of the **float.json** file is as follows:
```json
{
"float":[
{
"name":"font_hello",
"value":"28.0fp"
},
{
"name":"font_world",
"value":"20.0fp"
}
]
}
```
The content of the **string.json** file is as follows:
```json
{
"string":[
{
"name":"string_hello",
"value":"Hello"
},
{
"name":"string_world",
"value":"World"
},
{
"name":"message_arrive",
"value":"We will arrive at %s."
}
]
}
```
The content of the **plural.json** file is as follows:
```json
{
"plural":[
{
"name":"eat_apple",
"value":[
{
"quantity":"one",
"value":"%d apple"
},
{
"quantity":"other",
"value":"%d apples"
}
]
}
]
}
```
# Button
The **\<Button>** component is usually activated by user clicks to perform a specific action, for example, submitting a form. Buttons are classified as capsule, circle, or normal buttons. For details, see [Button](../reference/arkui-ts/ts-basic-components-button.md).
## Creating a Button
You can create a button that contains or does not contain child components.
- Create a button that contains child components.
`Button(options?: {type?: ButtonType, stateEffect?: boolean})`, where `type` indicates the button type, and `stateEffect` indicates whether to enable the click effect for the button.
```
Button({ type: ButtonType.Normal, stateEffect: true }) {
Row() {
Image($r('app.media.loading')).width(20).height(20).margin({ left: 12 })
Text('loading').fontSize(12).fontColor(0xffffff).margin({ left: 5, right: 12 })
}.alignItems(VerticalAlign.Center)
}.borderRadius(8).backgroundColor(0x317aff).width(90)
```
![en-us_image_0000001260555857](figures/en-us_image_0000001260555857.png)
- Create a button that does not contain child components.
`Button(label?: string, options?: { type?: ButtonType, stateEffect?: boolean })`, where `label` indicates whether the button contains child components.
```
Button('Ok', { type: ButtonType.Normal, stateEffect: true })
.borderRadius(8)
.backgroundColor(0x317aff)
.width(90)
```
![en-us_image_0000001215796030](figures/en-us_image_0000001215796030.png)
## Setting the Button Type
Use the **type** parameter to set the button type to **Capsule**, **Circle**, or **Normal**.
- Capsule button (default type)
```ts
Button('Disable', { type: ButtonType.Capsule, stateEffect: false })
.backgroundColor(0x317aff)
.width(90)
```
![en-us_image_0000001215645452](figures/en-us_image_0000001215645452.png)
- Circle button
```ts
Button('Circle', { type: ButtonType.Circle, stateEffect: false })
.backgroundColor(0x317aff)
.width(90)
.height(90)
```
![en-us_image_0000001215965420](figures/en-us_image_0000001215965420.png)
## Setting Styles
- Set the border radius:
In general cases, you can use universal attributes to define the button styles. For example, you can use the **borderRadius** attribute to set the border radius.
```ts
Button('circle border', { type: ButtonType.Normal })
.borderRadius(20)
```
![en-us_image_0000001190463780](figures/en-us_image_0000001190463780.png)
- Set the text style:
Add a font style for text displayed on the button.
```ts
Button('font style', { type: ButtonType.Normal })
.fontSize(20)
.fontColor(Color.Red)
.fontWeight(800)
```
![en-us_image_0000001189744672](figures/en-us_image_0000001189744672.png)
- Set the background color:
You can do so by adding the **backgroundColor** attribute.
```ts
Button('background color').backgroundColor(0xF55A42)
```
![en-us_image_0000001235146483](figures/en-us_image_0000001235146483.png)
- Assign a function to the button:
In this example, we are creating a button with the delete function.
```ts
Button({ type: ButtonType.Circle, stateEffect: true }) {
Image($r('app.media.ic_public_delete_filled')).width(30).height(30)
}.width(55).height(55).margin({ left: 20 }).backgroundColor(0xF55A42)
```
![en-us_image_0000001260373911](figures/en-us_image_0000001260373911.png)
## Adding Events
The **\<Button>** component is usually used to trigger actions. You can bind the **onClick** event to the button to have it respond with custom behavior after being clicked.
```ts
Button('Ok', { type: ButtonType.Normal, stateEffect: true })
.onClick(()=>{
console.info('Button onClick')
})
```
## Example Scenarios
- Using the Button for Startup
You can use the button for any UI element that involves the startup operation. The button triggers the predefined event based on the user's operation. For example, you can use a button in the **\<List>** container to redirect the user to another page.
```ts
// xxx.ets
import router from '@ohos.router'
@Entry
@Component
struct ButtonCase1 {
build() {
List({ space: 4 }) {
ListItem() {
Button("First").onClick(() => {
router.push({ url: 'xxx' })
})
}
ListItem() {
Button("Second").onClick(() => {
router.push({ url: 'yyy' })
})
}
ListItem() {
Button("Third").onClick(() => {
router.push({ url: 'zzz' })
})
}
}
.listDirection(Axis.Vertical)
.backgroundColor(0xDCDCDC).padding(20)
}
}
```
![en-us_image_0000001235626467](figures/en-us_image_0000001235626467.png)
- Using the Button for Submitting Forms
On the user login/registration page, you can use a button to submit a login or registration request.
```ts
// xxx.ets
@Entry
@Component
struct ButtonCase2 {
build() {
Column() {
TextInput({ placeholder: 'input your username' }).margin({ top: 20 })
TextInput({ placeholder: 'input your password' }).type(InputType.Password).margin({ top: 20 })
Button('Register').width(300).margin({ top: 20 })
}.padding(20)
}
}
```
![en-us_image_0000001190466492](figures/en-us_image_0000001190466492.png)
# Resource File Categories
## resources Directory
All the application resource files, such as strings, images, and audio files, are stored in the **resources** directory, allowing you to easily access, use, and maintain them. The **resources** directory consists of two types of sub-directories: the **base** sub-directory and qualifiers sub-directories, and the **rawfile** sub-directory. For details, see Categories of the **resources** directory.
Example of the **resources** directory:
```
resources
|---base // Default sub-directory
| |---element
| | |---string.json
| |---media
| | |---icon.png
|---en_GB-vertical-car-mdpi // Example of a qualifiers sub-directory, which needs to be created on your own
| |---element
| | |---string.json
| |---media
| | |---icon.png
|---rawfile // Default sub-directory
```
**Table 1** Categories of the **resources** directory
| Category | base and Qualifiers Sub-directories | rawfile Sub-directory |
| ----------- | ---------------------------------------- | ---------------------------------------- |
| Structure | Sub-directories are structured in two levels. The directory name must comply with specified naming conventions so that its target resource file in the correct directory can be matched based on the device status.<br> The **base** sub-directory and qualifiers sub-directories are the first level of sub-directories under **resources**.<br>- The **base** sub-directory is generated by default. If no qualifiers sub-directories in the **resources** directory of the application match the device status, the resource file in the **base** sub-directory will be automatically referenced.<br>- You need to create qualifiers sub-directories on your own. Each directory name consists of one or more qualifiers that represent the application scenarios or device characteristics. For details, see [Qualifiers Sub-directories](#qualifiers-sub-directories).<br>Resource group sub-directories are located at the second level of sub-directories to store basic elements such as strings, colors, and boolean values, as well as resource files such as media, animations, and layouts. For details, see [Resource Group Sub-directories](#resource-group-sub-directories). | You can create multiple levels of sub-directories with custom directory names. They can be used to store various resource files.<br>However, resource files in the **rawfile** sub-directory will not be matched based on the device status. |
| Compilation | Resource files in the sub-directories are compiled into binary files, and each resource file is assigned an ID. | Resource files in the sub-directory are directly packed into the application without being compiled, and no IDs will be assigned to the resource files. |
| Reference | Resource files in the sub-directories are referenced based on the resource type and resource name. | Resource files in the sub-directories are referenced based on the specified file path and file name. |
## Qualifiers Sub-directories
The name of a qualifiers sub-directory consists of one or more qualifiers that represent the application scenarios or device characteristics, covering the mobile country code (MCC), mobile network code (MNC), language, script, country or region, screen orientation, device type, color mode, and screen density. The qualifiers are separated using underscores (_) or hyphens (-). When creating a qualifiers sub-directory, you need to understand the directory naming conventions and the rules for matching qualifiers sub-directories and the device status.
**Naming Conventions for Qualifiers Sub-directories**
- Qualifiers are ordered in the following sequence: *MCC*MNC-language_script_country/region-screen orientation-device type-color mode-screen density_. You can select one or multiple qualifiers to name your sub-directory based on your application scenarios and device characteristics.
- Separation between qualifiers: The language, script, and country/region qualifiers are separated using underscores (\_); the MNC and MCC qualifiers are also separated using underscores (\_); other qualifiers are separated using hyphens (-). For example, **zh_Hant_CN** and **zh_CN-car-ldpi**.
- Value range of qualifiers: The value of each qualifier must meet the requirements. Otherwise, the resource files in the sub-directory cannot be matched.
**Table 2** Requirements for qualifier values
| Qualifier Type | Description and Value Range |
| ------------------ | ---------------------------------------- |
| MCC&amp;MNC | Indicates the MCC and MNC, which are obtained from the network where the device is registered. The MCC can be either followed by the MNC with an underscore (*) in between or be used independently. For example, **mcc460** represents China, and **mcc460*mnc00** represents China Mobile.<br>For details about the value range, refer to **ITU-T E.212** (the international identification plan for public networks and subscriptions). |
| Language | Indicates the language used by the device. The value consists of two or three lowercase letters, for example, **zh** indicates Chinese, **en** indicates English, and **mai** indicates Maithili.<br>For details about the value range, refer to **ISO 639** (codes for the representation of names of languages). |
| Script | Indicates the script type used by the device. The value starts with one uppercase letter followed by three lowercase letters, for example, **Hans** indicates simplified Chinese and **Hant** indicates traditional Chinese.<br>For details about the value range, refer to **ISO 15924** (codes for the representation of names of scripts). |
| Country/Region | Indicates the country or region where a user is located. The value consists of two or three uppercase letters or three digits, for example, **CN** indicates China and **GB** indicates the United Kingdom.<br>For details about the value range, refer to **ISO 3166-1** (codes for the representation of names of countries and their subdivisions). |
| Screen orientation | Indicates the screen orientation of the device. The value can be:<br>- **vertical**: portrait orientation<br>- **horizontal**: landscape orientation |
| Device type | Indicates the device type. The value can be:<br>- **car**: head units<br>- **tv**: smart TVs<br>- **wearable**: wearables |
| Color mode | Indicates the color mode of the device. The value can be:<br>- **dark**: dark mode<br>- **light**: light mode |
| Screen density | Indicates the screen density of the device, in dpi. The value can be:<br>- **sdpi**: screen density with small-scale dots per inch (SDPI). This value is applicable for devices with a DPI range of (0, 120].<br>- **mdpi**: screen density with medium-scale dots per inch (MDPI). This value is applicable for devices with a DPI range of (120, 160].<br>- **ldpi**: screen density with large-scale dots per inch (LDPI). This value is applicable for devices with a DPI range of (160, 240].<br>- **xldpi**: screen density with extra-large-scale dots per inch (XLDPI). This value is applicable for devices with a DPI range of (240, 320].<br>- **xxldpi**: screen density with extra-extra-large-scale dots per inch (XXLDPI). This value is applicable for devices with a DPI range of (320, 480].<br>- **xxxldpi**: screen density with extra-extra-extra-large-scale dots per inch (XXXLDPI). This value is applicable for devices with a DPI range of (480, 640]. |
**Rules for Matching Qualifiers Sub-directories and Device Resources**
- Qualifiers are matched with the device resources in the following priorities: MCC&amp;MNC &gt; locale (options: language, language_script, language_country/region, and language_script_country/region) &gt; screen orientation &gt; device type &gt; color mode &gt; screen density
- If the qualifiers sub-directories contain the **MCC, MNC, language, script, screen orientation, device type, and color mode** qualifiers, their values must be consistent with the current device status so that the sub-directories can be used for matching the device resources. For example, the qualifiers sub-directory **zh_CN-car-ldpi** cannot be used for matching the resource files labeled **en_US**.
## Resource Group Sub-directories
You can create resource group sub-directories (including element, media, animation, layout, graphic, and profile) in the **base** and qualifiers sub-directories to store resource files of specific types. For details, see Resource group sub-directories.
**Table 3** Resource group sub-directories
| Resource Group Sub-directory | Description | Resource File |
| ---------------------------- | ---------------------------------------- | ---------------------------------------- |
| element | Element resources. Each type of data is represented by a JSON file. The options are as follows:<br>- **boolean**: boolean data<br>- **color**: color data<br>- **float**: floating-point data<br>- **intarray**: array of integer<br>- **integer**: integer data<br>- **pattern**: pattern data<br>- **plural**: plural form data<br>- **strarray**: array of strings<br>- **string**: string data | It is recommended that files in the **element** sub-directory be named the same as the following files, each of which can contain only data of the same type:<br>- boolean.json<br>- color.json<br>- float.json<br>- intarray.json<br>- integer.json<br>- pattern.json<br>- plural.json<br>- strarray.json<br>- string.json |
| media | Media resources, including non-text files such as images, audios, and videos. | The file name can be customized, for example, **icon.png**. |
| profile | Other types of files, which are stored in their raw formats. | The file name can be customized. |
### Media Resource Types
**Table 4** Image resource types
| Format | File Name Extension |
| ------ | ------------------- |
| JPEG | .jpg |
| PNG | .png |
| GIF | .gif |
| SVG | .svg |
| WEBP | .webp |
| BMP | .bmp |
**Table 5** Audio and video resource types
| Format | File Name Extension |
| ------------------------------------ | ------------------- |
| H.263 | .3gp <br>.mp4 |
| H.264 AVC <br> Baseline Profile (BP) | .3gp <br>.mp4 |
| MPEG-4 SP | .3gp |
| VP8 | .webm <br> .mkv |
## Creating a Resource File
You can create a sub-directory and its files under the **resources** directory based on the above descriptions of the qualifiers sub-directories and resource group sub-directories.
DevEco Studio provides a wizard for you to create resource directories and resource files.
- Creating a Resource Directory and Resource File
Right-click the **resources** directory and choose **New > Resource File**.
If no qualifier is selected, the file is created in a resource type sub-directory under **base**. If one or more qualifiers are selected, the system automatically generates a sub-directory and creates the file in this sub-directory.
The created sub-directory is automatically named in the format of **Qualifiers.Resource type**. For example, if you create a sub-directory by setting **Orientation** to **Vertical** and **Resource type** to **Graphic**, the system automatically generates a sub-directory named **vertical.graphic**.
![create-resource-file-1](figures/create-resource-file-1.png)
- Creating a Resource Directory
Right-click the **resources** directory and choose **New > Resource Directory**. This operation creates a sub-directory only.
Select a resource group type and set qualifiers. Then the system automatically generates the sub-directory name. The sub-directory is automatically named in the format of **Qualifiers.Resource group**. For example, if you create a sub-directory by setting **Orientation** to **Vertical** and **Resource type** to **Graphic**, the system automatically generates a sub-directory named **vertical.graphic**.
![create-resource-file-2](figures/create-resource-file-2.png)
- Creating a Resource File
Right-click a sub-directory under **resources** and choose **New > XXX Resource File**. This operation creates a resource file under this sub-directory.
For example, you can create an element resource file in the **element** sub-directory.
![create-resource-file-3](figures/create-resource-file-3.png)
# Getting to Know Components
Before customizing a component, get to know what the [component and decorator](#components-and-decorators) are and initialize the component. Then, [modify the component attributes and constructor parameters](#modifying-component-attributes-and-constructor-parameters) to customize a component.
## Components and Decorators
In a declarative UI, all pages are composed of components. The data structure of the component is struct, and the decorator @Component is the component-based flag. The struct decorated by @Component indicates that the struct has the component capability.
The method for declaring a custom component is as follows:
```ts
@Component
struct MyComponent {}
```
In a DevEco Studio project template, MyComponent is a custom component that can display text in the center. You can describe your UI structures in the build method of the component, by complying with the API constraints of the Builder.
```ts
interface Builder {
build: () => void
}
```
The component decorated by @Entry is the main entry, or in other words, the root node, of the page. Note that a page must have one and only one @Entry. Only the @Entry decorated component and its child components are displayed on the page.
@Component and @Entry are basic and important decorators. To put it simply, a decorator assigns a capability to an object to be decorated. For example, @Entry assigns the capability of the page entry, and @Component assigns the component capability.
With a basic knowledge of the component and decorator, you are ready to develop a diet application.
## Modifying Component Attributes and Constructor Parameters
When you create a system component, the default style is used. You can change the display of the component by changing its attributes and styles.
1. Modify the fontSize attribute of the **\<Text>** component to change the font size of the component. In this example, the font size is set to 26 and font weight 500. Two configuration methods are available for setting the font weight:
1. The value of the number type ranges from 100 to 900. The default value is 400. A larger value indicates a thicker font.
2. fontWeight is a built-in enumeration type. Its value can be Lighter, Normal, Bold, or Bolder.
The attribute method must follow the component and be connected by the operator ".". You can also configure multiple attributes of the component in method chaining mode.
```
@Entry
@Component
struct MyComponent {
build() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Text('Hello World')
.fontSize(26)
.fontWeight(500)
}
.width('100%')
.height('100%')
}
}
```
![en-us_image_0000001267767845](figures/en-us_image_0000001267767845.png)
2. Change the display content of the **\<Text>** component from Hello World to Tomato by modifying the constructor parameters of the **\<Text>** component.
```
@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_0000001267887829](figures/en-us_image_0000001267887829.png)
# Creating a Declarative UI Project
Before creating a project, you need to install DevEco Studio.
1. Open DevEco Studio and click **Create Project**. If there is already a project, choose **File** > **New** > **New project**.
![en-us_image_0000001168956332](figures/en-us_image_0000001168956332.png)
2.
On the page for selecting an ability template, select **Empty Ability**.
![en-us_image_0000001168059158](figures/en-us_image_0000001168059158.png)
3. On the project configuration page, set **Project name** to **HealthyDiet**, **Project type** to **Application**, **Compile API** to **8**, and **UI Syntax** to **eTS**. By default, DevEco Studio saves the project to drive C. You can change the save path by setting **Save location**. When you are done, click **Finish**.
![en-us_image_0000001167746622](figures/en-us_image_0000001167746622.png)
4. After the project is created, open the **app.ets** file.
The **app.ets** file provides the **onCreate** and **onDestroy** methods for the application lifecycle. **onCreate** is called when an application is created, and **onDestroy** is called when an application is destroyed. Global variables can be declared in the **app.ets** file, wherein the declared data and methods are shared by the entire application.
```
export default {
onCreate() {
console.info('Application onCreate')
},
onDestroy() {
console.info('Application onDestroy')
},
}
```
5. In the project navigation tree, open **index.ets**. This page displays the current UI description. The declarative UI framework automatically generates a component-based struct, which complies with the Builder API declaration. The current layout and components are declared in the build method.
```
@Entry
@Component
struct MyComponent {
build() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Text('Hello World')
.fontSize(50)
.fontWeight(FontWeight.Bold)
}
.width('100%')
.height('100%')
}
}
```
6. Click **Previewer** on the right to open the **Previewer** window. **Hello World** is displayed in the middle and in bold.
If the **Previewer** button is unavailable, choose **Settings** > **SDK Manager** > **OpenHarmony SDK** > **Tools** to check whether the Previewer is installed.
![en-us_image_0000001222807756](figures/en-us_image_0000001222807756.png)
7. Install the application and run the application. Connect the device to the computer. After the IDE identifies the device, click **Run'entry'**.
![en-us_image_0000001148858818](figures/en-us_image_0000001148858818.png)
Before the installation, you must configure an application signature. For details, see **Configuring the OpenHarmony App Signature**. After the installation is complete, click the **Run** icon on the screen to open the application. **Hello World** is displayed in the center of the screen.
![en-us_image_0000001267647841](figures/en-us_image_0000001267647841.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 among modules on different devices.
3. Provides a flexible spacing adjustment method for applications to keep up with layout in special scenarios.
To implement grid layout, the declarative paradigm provides the [\<GridContainer>](../reference/arkui-ts/ts-container-gridcontainer.md) component, which works with the **useSizeType** attribute of its child components to implement grid layout.
## Grid System
The grid system works in terms of gutter, margin, and column.
![en-us_image_0000001217236574](figures/en-us_image_0000001217236574.png)
1. Gutter:
Spacing between elements. You can define different clutter values for different device sizes as part of the overall grid layout specifications. For better results, make sure the gutter is not greater than the margin.
2. Margin:
Spacing around an item in the grid container. You can define different margin values for different device sizes as part of the overall grid layout specifications.
3. Column:
Main tool for positioning items in the grid layout. The grid container is divided into various numbers of columns based on the device size. The width per column is calculated based on the total number of columns while respecting the margin and clutter specifications.
### Grid Breakpoints
Breakpoints in the grid system are set in terms of the device's screen pixel density. The grid system defines a set of breakpoint rules based on the mapping between the numbers of columns and device screen widths.
Depending on the screen width, devices are classified into different width types. The table below provides the mappings of the grid breakpoint ranges, device width types, default total number of columns, margin, and gutter.
| Grid Breakpoint Range | Device Width Type| Description | columns | gutter | margin |
| ----------------------- | ------ | --------- | ------- | ------ | ------ |
| 0 &lt; Device screen width &lt; 320 vp | XS | Device of the minimum size.| 2 | 12 vp | 12 vp |
| 320 vp &lt;= Horizontal width &lt; 600 vp| SM | Small-sized device. | 4 | 24 vp | 24 vp |
| 600 vp &lt;= Horizontal width &lt; 840 vp| MD | Medium-sized device.| 8 | 24 vp | 32 vp |
| 840 vp &lt;= Device screen width | LG | Large-sized device. | 12 | 24 vp | 48 vp |
## Usage
Create a grid container, define the grid layout, and set the number of columns for child components in the grid container under different device width types.
### Creating a Grid Container
Use the `GridContainer(options?: { columns?: number | 'auto', sizeType?: SizeType, gutter?: Length, margin?: Length})` API to create a grid container. All child components in the grid container follow the grid layout.
- Use the **columns**, **gutter**, and **margin** parameters to define your grid layout.
In the example below, the grid container is divided into six columns, with the gutter (spacing between columns) of 10 vp and the margin (spacing around a column) of 20 vp.
```ts
GridContainer({ columns: 6, gutter: 10, margin: 20 }) {}
```
In the example below, the grid container does not have any parameter set. In this case, it follows the default layout, as in the case when **sizeType** is set to **SizeType.Auto**.
```ts
GridContainer() {}
```
On a small-sized device (**SizeType.SM**), the grid container is divided into four columns by default, with the gutter of 24 vp and the margin of 24 vp. On a medium-sized device (**SizeType.MD**), the grid container is divided into eight columns by default, with the gutter of 24 vp and the margin of 32 vp.
- You can also use **sizeType** to configure child components in the grid container to follow the grid settings of a specific device width type, as shown below:
```ts
GridContainer({ sizeType: SizeType.SM }) {
Row() {
Text('1')
.useSizeType({
xs: { span: 2, offset: 0 },
sm: { span: 3, offset: 0 },
md: { span: 6, offset: 2 },
lg: { span: 8, offset: 2 },
})
}
}
```
In the preceding example, the **\<Text>** component uses the grid settings of the **SizeType.SM** type regardless of the actual device width type. That is, the **\<Text>** component takes up three columns and is placed in the first column.
### Grid Settings of Child Components in the Grid Container
Use the universal attribute **useSizeType** to configure how child components are positioned in the grid container. **useSizeType** comes with the **span** and **offset** sub-attributes. **span** indicates the number of columns occupied by the child component; **offset** indicates the column offset, that is, the column where the component is located. The sample code is as follows:
```ts
GridContainer() {
Row() {
Text('1')
.useSizeType({
xs: { span: 2, offset: 0 },
sm: { span: 2, offset: 0 },
md: { span: 6, offset: 2 },
lg: { span: 8, offset: 2 },
})
}
}
```
In the preceding example, `sm: { span: 2, offset: 0 }` indicates that on a medium-sized device, the **\<Text>** component takes up two columns and is placed in the first column of the grid container.
![en-us_image_0000001218108719](figures/en-us_image_0000001218108719.png)
## Example Scenario
The grid layout helps ensure proper display of components among different device width types, eliminating the hassle of writing a large amount of code for device compatibility.
```ts
@Entry
@Component
struct GridContainerExample {
build() {
Column({ space: 5 }) {
GridContainer({ columns: 6 }) {
Flex({justifyContent:FlexAlign.SpaceAround}) {
Text('1')
.useSizeType({
xs: { span: 2, offset: 0 },
sm: { span: 2, offset: 0 },
md: { span: 1, offset: 0 },
lg: { span: 1, offset: 0 },
})
.height(100).backgroundColor(0x4682B4).textAlign(TextAlign.Center)
Text('2')
.useSizeType({
xs: { span: 2, offset: 0 },
sm: { span: 2, offset: 0 },
md: { span: 4, offset: 0 },
lg: { span: 4, offset: 0 },
})
.height(100).backgroundColor(0x46F2B4).textAlign(TextAlign.Center)
Text('3')
.useSizeType({
xs: { span: 2, offset: 0 },
sm: { span: 2, offset: 0 },
md: { span: 1, offset: 0 },
lg: { span: 1, offset: 0 },
})
.height(100).backgroundColor(0x46A2B4).textAlign(TextAlign.Center)
}
}.width('80%').backgroundColor('gray')
}.width('100%').margin({ top: 15 })
}
}
```
Effect on a small-sized device (**SizeType.SM**):
![en-us_image_0000001218108718](figures/en-us_image_0000001218108718.png)
Effect on a medium-sized device (**SizeType.MD**):
![en-us_image_0000001262748569](figures/en-us_image_0000001262748569.png)
\ No newline at end of file
......@@ -672,8 +672,3 @@ After correct configuration, compilation, and burning, the device uses the wifi_
The adaptation process of other components is similar to that of other vendors.
## To-do
- Adding BLE support
- Diversifying Wi-Fi test commands
......@@ -15,4 +15,5 @@ The following table describes the macros for configuring the kernel heap memory.
> ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
>
> Ensure that the specified heap memory range is not used by other modules. Otherwise, functions of the heap memory will be damaged due to the heap memory corruption.
......@@ -84,8 +84,8 @@ The following uses Raspberry Pi 3b \(BCM2837\) as an example to describe how to
# Configure the build environment, and use clang provided by the project to build the Raspberry Pi kernel source code.
export PATH=$PROJ_ROOT/prebuilts/clang/ohos/linux-x86_64/llvm/bin:$PROJ_ROOT/prebuilts/gcc/linux-x86/arm/gcc-linaro-7.5.0-arm-linux-gnueabi/bin/:$PATH
export MAKE_OPTIONS="ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- CC=clang HOSTCC=clang"
export PRODUCT_PATH=vendor/hisilicon/hispark_taurus_linux
```
export PRODUCT_PATH=vendor/hisilicon/hispark_taurus_linux
```
3. Comment out the flags that cannot be recognized by **clang**.
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册