2

With the release of HarmonyOS 3.0 Beta, a large number of JS/eTS API interfaces have been added to API Version 8. I believe many developers can't wait to experience the development of HamronyOS applications based on eTS. In this issue of Codelab, we will implement a HarmonyOS course application based on API Version 8 to help you learn eTS's declarative UI description, loop rendering, state data management and other mechanisms, and experience the minimalist and efficient development based on eTS.

1. Overall introduction

In the course application interface, the left side is the course classification navigation bar, and the right side is the course content of each category. When the user slides up and down the right course content, the left navigation bar will jump to the corresponding course category. When a user clicks a course category in the left navigation bar, the clicked content will be highlighted, and the course content on the right will jump to the course list of the corresponding category.
So how to efficiently implement such a HarmonyOS course application based on eTS? Next, we will analyze it from three dimensions: declarative UI description, loop rendering data, and state data management.
1. Declarative UI description <br>The interface layout is the skeleton of the UI interface, which determines the interaction and visual style of the application interface. In this example, we will declaratively combine and extend a series of basic components of eTS, and use programming syntax close to natural semantics to intuitively describe the UI interface, including parameter construction configuration, attribute configuration, event configuration, and sub-component configuration, etc. .
Compared with Java-based imperative development, eTS adopts a declarative programming syntax that is closer to natural semantics, allowing developers to describe the UI interface more intuitively, effectively reducing the developer's start-up cost and greatly improving the UI. The construction efficiency of the interface enables extremely simple and efficient development.
2. Circular rendering data <br>The data model is the body of the UI interface, describing the static characteristics, dynamic behavior and constraints of the data in the interface. In this example, we will intuitively define the data model of each module in the interface through eTS, including name, logo, picture link, etc., and write the corresponding data according to the data model. Finally, use the loop rendering mechanism (ForEach) provided by eTS to loop and render the written data to the corresponding interface.
Compared with Java-based imperative development, when eTS renders data, the UI update does not require the developer to use the code to actively refresh, but is handed over to the framework layer for automatic processing. The developer does not need to care about how the framework implements UI drawing and rendering to realize the interface Efficient rendering of data.
3. State data management <br>The linkage effect is the soul of the UI interface, which realizes the dynamic interaction between the layout and data in the interface. In this example, we will use the state data management mechanism provided by eTS to decorate the state attributes owned by the component. When the variable of the component changes, the component will re-render and update the UI interface data to achieve the linkage effect.
Compared with Java-based imperative development, eTS provides developers with a clear page update rendering process and pipeline through decorators with different functions. What the developer has to do is to define the mapping relationship between the data and the UI, and then only need to monitor the state of the data through the state decorator, and the UI can be automatically refreshed, which greatly reduces the maintenance work of the programmer on the UI.
The above are the core principles of implementing course applications. Below we will bring you the specific implementation of each part.

2. Build the interface layout

eTS provides a variety of layout methods, which not only retains the classic flexible layout capabilities, but also provides list, grid, grid layout and atomic layout capabilities that adapt to the development of multi-resolution scenes. As shown in Figure 1, in this example, the overall layout uses Row to lay out containers in the horizontal direction, and set the background color to white. The nested Scroll and List containers in Row are used as the navigation bar layout and course content layout of the application interface respectively. Come for you one by one.

 图1 整体布局

1. Navigation bar layout <br>The navigation bar of the application interface is implemented by the scrollable container component Scroll, and the Text component embedded in the Scroll is used to display the name of the "course category", as shown in Figure 2:

 图2 导航栏布局

The Scroll container must have a built-in child component. We use the vertical layout container Column and set the padding height to height('100%'). The name of "Course Classification" is implemented using the Text component, and the text color is fontColor(0x696969), the text size is fontSize(16), the text alignment is textAlign(TextAlign.Center), the height is height(60), and the width is width('100%').
The relevant code is as follows:

 Scroll() {
  Column() {
    ForEach(this.tabArray.map((item1, index1) => {
      return { index: index1, data: item1 };
    }), item => {
      Text(item.data.superName)
        .fontColor(0x696969)
        .backgroundColor(this.index == item.index ? 0xffffff : null)
        .fontSize(16)
        .width('100%')
        .height(60)
        .textAlign(TextAlign.Center)
        .onClick(() => {
          if (this.index != item.index) {
            this.index = item.index
            this.scroller.scrollToIndex(item.data.position)
          }
        })
    }, item => '' + item.data)
  }.height('100%')
}.width(100).height('100%').backgroundColor(0xdddddd).scrollBar(BarState.Off)

2. Course content layout <br>The layout of the course content part of the application is implemented using the List list container, and the ForEach loop is used to render the listArray (course content) data, as shown in Figure 3, the course content layout includes the header and the course information. section (the header corresponds to the navigation bar on the left), below we will introduce the implementation of the layout of the header and course information respectively.

 图3 课程内容布局

(1) Header layout <br>The header is implemented using the Text component, and the text color is fontColor(0x696969), the text size is fontSize(20), the height is height(40), and the width is width('100%' ) padding, padding({ left: 10 }), and backgroundColor(0xefefef). At the same time, the ListItem in the head is set with the sticky(Sticky.Normal) property, so that it can show a fixed effect when it slides to the top. code show as below:

 if (item.tag) {
  ListItem() {
    Text(item.courseName)
      .fontColor(0x696969)
      .fontSize(20)
      .height(40)
      .width('100%')
      .padding({ left: 10 })
      .backgroundColor(0xefefef)
  }.sticky(Sticky.Normal)

(2) Course information layout <br>The course information part uses the Stack stack container, the height is set to height(120), and the alignment of the sub-components in the container is set to Alignment.TopStart. The Stack component is embedded with Image, Text, and Divider components, which are used to present information such as course pictures, course titles, course prices, and dividing lines. details as follows:
●The pictures of the course are rendered using the Image component, set the width to width(130), the height to height(100), and the zoom type of the picture to objectFit(ImageFit.Fill), so that the picture fills the component, and set the left and top margins to be equal. 10, to center-align its components. code show as below:

 Image(item.imageUrl)
  .objectFit(ImageFit.Cover)
  .width(130)
  .height(100)
  .margin({ left: 10, top: 10 })

●Use the Text component for the course name, set the text color to fontColor(0x363636), the text size to fontSize(14), the maximum number of displayed lines is maxLines(2), and the display method when the text is too long is TextOverflow.Clip, which means Make a cropped display. code show as below:

 Text(item.courseName)
  .fontColor(0x363636)
  .fontSize(14)
  .margin({ left: 150, top: 12 })
  .maxLines(2)
  .textOverflow({ overflow: TextOverflow.Clip })

●The price of the course uses the Text component, set the text color to fontColor(0xff6600), the text size to fontSize(24), and use the absolute positioning position({ x: 0, y: '100%' }) to display the component first. Below the outer border at the bottom of the Stack container, and then use the anchor point to position markAnchor({ x: 0, y: '100%' }) to offset the component upwards with its own height, so that the component is displayed at the bottom of the Stack container. code show as below:

 Text(item.price == 0 ? '免费' : '¥' + item.price)
  .fontColor(0xff6600)
  .fontSize(24)
  .position({ x: 0, y: '100%' })
  .markAnchor({ x: 0, y: '100%' })
  .margin({ bottom: 18, left: 150 })

●The dividing line is implemented using the Divider component, and the color of the dividing line is set to color(0xefefef), the width of the dividing line is strokeWidth(0.7), and the left and right margins ({ left: 10, right: 10 }) are both 10, and use Absolute positioning position({ x: 0, y: '100%' }) and anchor positioning markAnchor({ x: 0, y: '100%' }) make the dividing line appear at the bottom of the Stack container. code show as below:

 Divider()
  .margin({ left: 10, right: 10 })
  .color(0xefefef)
  .strokeWidth(0.7)
  .position({ x: 0, y: '100%' })
  .markAnchor({ x: 0, y: '100%' })

So far, we have implemented the interface layout, at this point the interface is just a skeleton without any content. Next we will add the data model for the application interface.

3. Build a data model

In this chapter, we will introduce the definition of the data model, the presetting of the data, and the loading of the data in this example.
1. Define the data model <br>This example needs to define two data models, namely "Course Category" in the left navigation bar and "Course Content" on the right side of the application interface. in:
● The "Course Category" in the navigation bar defines the properties of name (superName) and position (position). The data model is defined as follows:

 export class TabItem {
  position: number; // 点击该分类时课程内容滑动到的位置
  superName: string; // 课程分类标题


  constructor(position: number, superName: string) {
    this.position = position;
    this.superName = superName;
  }
}

● "Course Content" defines the name of the course (courseName), the image address of the course (imageUrl), the price of the course (price), the variable to determine whether the course header of this category (tag), and the index of the course category corresponding to the course Position (index), the data model is defined as follows:

 export class CourseItem {
  tag: boolean; // 是否此类别课程的头部
  index: number; // 课程所对应课程类别的索引位置
  courseName: string; // 课程名称
  imageUrl: string; // 图片地址
  price: number; // 价格
  constructor(tag: boolean, index: number, courseName: string, imageUrl: string, price: number) {
    this.tag = tag;
    this.index = index;
    this.courseName = courseName;
    this.imageUrl = imageUrl;
    this.price = price;
  }
}

2. Preset data <br>In the entry/src/main/ets/MainAbility/Model.ets file, put the prepared simulation data, where superId is the unique identifier of the course classification, and id is the unique identifier of the course content . The format is as follows:

 const LinkData: any[] = [
  { 
    "superId": 1, 
    "superName": "热门课程", 
    "id": 1, 
    "courseName": "应用市场介绍", 
    "imageUrl": "/image/image1.jpg", 
    "price": 0 
  }, 
{
    "superId": 1,
    "superName": "热门课程",
    "id": 2,
    "courseName": "上架流程",
    "imageUrl": "/image/image2.jpg",
    "price": 100
  },
  ... 
]

3. Load data to update UI
This section describes how to load locally constructed mock data and render it to the interface.
(1) Load data <br>In the index.ets file, get the preset data through getLinkData().

 aboutToAppear() {
  // 延时数据加载
  setTimeout(() => {
    let linkDataItem = getLinkData();
    this.tabArray = linkDataItem.tabArray;
    this.listArray = linkDataItem.listArray;
    this.requestSuccess = true;
  }, 2000)
}

(2) Rendering ● The navigation bar uses the ForEach loop to render the tabArray (course classification) data. The first parameter of ForEach here needs to be traversed using the map() method of the array. code show as below:

 let superId: number = 0
model.forEach((item) => {
  if (superId != item.superId) {
    let tabItem = new TabItem(this.listArray.length, item.superName);
    this.tabArray.push(tabItem)


    let courseItem = new CourseItem(true, this.tabArray.length - 1, item.superName, '', 0);
    this.listArray.push(courseItem)
  }
})

● The course content uses the ForEach loop to render the listArray (course content) data. code show as below:

 ForEach(this.listArray, item => {
  if (item.tag) {
    ListItem() {
    ......
    }.sticky(Sticky.Normal)
  } else {
    ListItem() {
      Stack({ alignContent: Alignment.TopStart }) {
      ......
      }.height(120)
    }
  }
}, item => '' + item)

Through the above introduction, we have realized the layout of the application interface and the presentation of interface data. At this time, the interface is like a shell without a soul. Below we will introduce the realization of the linkage effect in the application.

4. Realize interface linkage

The linkage effects in this example include highlighting the navigation bar, sliding the course content to correspond to the navigation bar change, and clicking the course category in the navigation bar to jump to the corresponding course content.

1. Navigation bar highlighting <br>To realize the navigation bar highlighting, mainly through the @State decorator to monitor the state change of the index value of the navigation bar course classification, when the user slides the course content or clicks the navigation bar, the index corresponding to the course classification When the value changes, the build method will be called to refresh the UI, so as to modify the background color of the navigation bar. The relevant code is as follows:

 @State index: number= 0; // 导航栏课程分类的索引
Text(item.data.superName)
  .backgroundColor(this.index == item.index ? 0xffffff : null)

2. Swipe on the right, change on the left <br>Sliding the course content on the right corresponds to the change of the left navigation bar. The onScrollIndex() event is mainly used to determine the course category to which the current course content belongs. When the user slides the course content, the index value of the corresponding course content If it changes, the index value of the course category in the navigation bar will also change. The relevant code is as follows:

 private scroller: Scroller = new Scroller()
List({ scroller: this.scroller }) {
  ......
  }
.onScrollIndex((firstIndex: number) => {
  if (this.index != this.listArray[firstIndex].index) {
    this.index = this.listArray[firstIndex].index
  }
})

3. Click on the left, jump on the right <br>Click on the left navigation bar to jump to the corresponding course content, mainly through the onClick() event to monitor user clicks, when the user clicks on the navigation bar, the scrollToIndex() method will classify the courses according to the clicked navigation bar The index value jumps to the corresponding course content interface. The relevant code is as follows:

 Text(item.data.superName)
  .onClick(() => {
    if (this.index != item.index) {
      this.index = item.index
      this.scroller.scrollToIndex(item.data.position)
    }
  })

The above is the entire content of this issue. Through the study of this issue of Codelab, I believe that you have mastered eTS's declarative UI description, loop rendering, state data management and other mechanisms. Hurry up and strike while the iron is hot to develop more interesting applications!


HarmonyOS开发者
72 声望36 粉丝

提供HarmonyOS关键技术解析、版本更新、开发者实践和活动资讯,欢迎各位开发者加入HarmonyOS生态,一起创造无限可能!