通過電競快覽應用學ArkUI
前言
為了能夠進一步對OpenHarmony3.0內置應用做修改,我需要先掌握ArkUI聲明式開發(fā)范式的相關知識。通過電競快覽應用示例能夠快速學習ArkUI聲明式開發(fā),并對相關知識點進行擴展學習,最終達到對組件、布局、動效和數據狀態(tài)管理的初步掌握,并將最終的健康飲食應用程序安裝到燒錄了OpenHarmony3.0的Hi3516開發(fā)板上。
ArkUI是一套構建HarmonyOS應用界面的聲明式UI開發(fā)框架。它使用極簡的UI信息語法、豐富的UI組件、以及實時預覽工具,幫助您提升HarmonyOS應用界面開發(fā)效率30%。您只需使用一套TS/JS API,就能在多個HarmonyOS設備上提供生動而流出的用戶界面體驗。 ——引自HarmonyOS應用開發(fā)官網
準備
- DevEco Studio 3.0 Beta1
- OpenHarmony SDK 3.0
- 數據來源于天行數據API
通過電競快覽應用學ArkUI
1. 創(chuàng)建電競快覽應用(OpenHarmony版)
配置OpenHarmony SDK
打開DevEco Studio,點擊左下角Configure,選擇Setting進入設置界面。在SDK Manager菜單下選擇OpenHarmony SDK,自定義存放路徑,并勾選安裝Platforms下的SDK及Tools下的Previewer和Toolchains。


點擊Create Project創(chuàng)建項目
在Choose Your Ability Template界面下拉到最底部,選擇[Standard]Empty Ability模板

配置工程界面完善項目信息

Project name:項目名稱
Project type:項目類型
Bundle name:包名稱
Save location:項目保存地址
Language:選擇編程語言(eTS)
Compatible API version:選擇兼容版本(API Version 7)
Device type:設備類型(Phone)
點擊Finish,等待項目構建完成。
目錄結構說明

app.ets 全局應用邏輯和應用生命周期管理。
pages 存放所有組件頁面。
common 存放公共代碼(可選)。
resources 存放資源配置文件。
刪除config.json文件中js -> pages標簽下的pages/second
刪除index.ets文件中的一些代碼,如圖所示:

刪除second.ets文件
2. 術語
認識@Entry
用@Entry裝飾的自定義組件用作頁面的默認入口組件,加載頁面時,將首先創(chuàng)建并呈現@Entry裝飾的自定義組件。單個源文件中,可以存在多個自定義組件,但最多可以使用@Entry裝飾一個自定義組件。
認識@Component
在聲明式UI中,所有的頁面都是由組件構成。使用@Component裝飾的struct表示該結構體具有組件化能力,能夠成為一個獨立的組件,這種類型的組件也稱為自定義組件。
build函數
build函數用于定義組件的聲明式UI描述。
認識@Builder
@Builder裝飾器定義了一個如何渲染自定義組件的方法。通過其可以在一個自定義組件內快速生成多個布局內容。比如要在一個頁面顯示多個名稱,使用多個Text組件顯得代碼臃腫且冗余,使用@Builder定義一個公用的方法,在組件中引入,代碼量少且達到了復用。
3. 構建電競新聞列表

由圖可知,整個界面分為上下兩部分,上部分顯示標題欄,下部分顯示電競新聞列表。
頁面中使用的組件
1)Column組件
Column組件是容器組件,是沿垂直方向布局的容器。相對的就存在沿水平方向布局的容器,其為Row組件,這兩個組件我將它們定位為線性布局容器。
可以通過alignItems屬性來設置子組件在水平方向上的對齊格式。提供了Start、Center、End三種對齊方式。
- Column() {
- //子組件
- }
- .alignItems(HorizontalAlign.Center)
2)Flex組件
Flex是彈性布局組件,其提供五個屬性來控制子組件的顯示方式。
- Flex({direction: FlexDirection.Row,wrap: FlexWrap.NoWrap, justifyContent: FlexAlign.Start, alignItems: ItemAlign.Stretch, alginContent: FlexAlign.Start})
direction:描述子組件在Flex容器上排列的方向,即主軸方向,也就是子組件是以橫向排列還是縱向排列。
FlexDirection.Row:橫向(行方向)排列,從行起始位置開始。
FlexDirection.RowReverse:橫向(行方向)排列,與Row方向相反,即從行尾開始排列。
FlexDirection.Column:縱向(列方向)排列,從列起始位置開始。
FlexDirection.ColumnReverse:縱向(列方向)排列,與Column方向相反,即從列尾開始排列。
- Flex({direction: FlexDirection.Row}) {}
- Flex({direction: FlexDirection.RowReverse}) {}
- Flex({direction: FlexDirection.Column}) {}
- Flex({direction: FlexDirection.ColumnReverse}) {}

wrap: Flex容器中子組件是單行/列還是多行/列排列。
justifyContent:子組件在Flex容器主軸上的對齊格式。
alignItems:子組件在Flex容器交叉軸上的對齊格式。
alignContent:交叉軸中有額外的空間時,多行內容的對齊方式。僅在wrap為Wrap或WrapReverse下生效。
3)List組件
List組件用于顯示一系列相同寬度的列表項,比如顯示新聞列表、商品列表等。List組件和ListItem組件一起使用。ListItem用于展示具體的數據項。
- List() {
- ListItem() {
- //組合數據項
- }
- ListItem() {
- //組合數據項
- }
- }
4)Text組件
Text用于呈現一段信息。
- Text(){}
5)Image組件
Image組件用于渲染展示圖片。
- Image(){}
通用組件屬性和事件
width():設置組件自身的寬度。
height():設置組件自身的高度。
margin():設置外邊距屬性。
padding():設置內邊距屬性。
backgroundColor:設置組件的背景色。
borderRadius:設置元素的邊框圓角半徑。
和搭積木一樣,把需要的組件組裝到一起,完成列表頁的構建。
- import router from '@system.router';
- import {ESports} from '../model/ESports.ets';
- import {initOnStartup} from '../model/ESportsList.ets';
- @Component
- struct ESportsListItem {
- private eSportsItem: ESports
- build() {
- Flex({justifyContent:FlexAlign.Start, alignItems: ItemAlign.Center}) {
- Image(this.eSportsItem.picUrl)
- .objectFit(ImageFit.Contain)
- .height('100%')
- .width(120)
- .margin({right: 16, left: 16})
- Column() {
- Text(this.eSportsItem.title)
- .fontSize(14)
- .fontWeight(FontWeight.Bold)
- .maxLines(1)
- .textOverflow({overflow: TextOverflow.Ellipsis})
- Text(this.eSportsItem.description)
- .fontSize(12)
- .fontColor('#cccccc')
- .maxLines(2)
- .textOverflow({overflow: TextOverflow.Ellipsis})
- .margin({top: 10})
- }
- .padding(4)
- }
- .height(100)
- .backgroundColor(0xF5F5F5)
- .borderRadius(8)
- }
- }
- @Entry
- @Component
- struct Index {
- private eSportsItems: ESports[] = initOnStartup()
- build() {
- Column() {
- Flex({justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center}) {
- Text('電競快覽')
- .fontSize(20)
- .fontWeight(FontWeight.Bold)
- .margin(20)
- }
- List({space: 4, initialIndex: 0}) {
- ForEach(this.eSportsItems, item => {
- ListItem() {
- ESportsListItem({eSportsItem: item})
- }
- .onClick(() => {
- router.push({
- uri: 'pages/content',
- params: {eSports: item}
- })
- })
- }, item => item.id.toString())
- }
- .width('96%')
- }
- .width("100%")
- .height("100%")
- .backgroundColor(0xE5E5E5)
- .padding({top: 5})
- }
- }
4. 構建電競新聞內容

- import router from '@system.router';
- import {ESports} from '../model/ESports.ets';
- @Component
- struct PageTitle {
- private eSports: ESports;
- build() {
- Flex({alignItems: ItemAlign.Start}) {
- Image($r('app.media.back'))
- .width(20)
- .height('100%')
- Text(this.eSports.title)
- .height('100%')
- .fontSize(16)
- .fontWeight(FontWeight.Bold)
- .maxLines(1)
- .textOverflow({overflow: TextOverflow.Ellipsis})
- .margin({left: 16})
- }
- .height(61)
- .backgroundColor('#FFedf2f5')
- .padding({top: 13, bottom: 14, left: 12})
- .onClick(() => {
- router.back();
- })
- }
- }
- @Entry
- @Component
- struct Content {
- private eSports: ESports = router.getParams().eSports;
- build() {
- Column() {
- Stack({alignContent: Alignment.TopStart}) {
- PageTitle({eSports: this.eSports})
- }
- Column() {
- Text(this.eSports.title)
- .width('80%')
- .fontSize(14)
- .fontWeight(FontWeight.Bold)
- .maxLines(2)
- .textOverflow({overflow: TextOverflow.Ellipsis})
- .textAlign(TextAlign.Center)
- .padding({top: 8, bottom: 8})
- Row({space: 12}) {
- Text(this.eSports.source)
- .fontSize(12)
- .fontColor('#CCCCCC')
- Text(this.eSports.ctime)
- .fontSize(12)
- .fontColor('#CCCCCC')
- }
- .margin({top: 5, bottom: 10})
- Text(this.eSports.description)
- .width('90%')
- .height(80)
- .lineHeight(20)
- .fontSize(12)
- .maxLines(5)
- .textOverflow({overflow: TextOverflow.Ellipsis})
- .borderRadius(10)
- .backgroundColor(0xE5E5E5)
- .padding(10)
- }
- }
- .alignItems(HorizontalAlign.Center)
- .height('100%')
- .width('100%')
- }
- }
這里預留一個思考題:
新聞內容是使用文本編輯器或者直接使用的是URL鏈接地址,如何讓它能夠在頁面中直接顯示?