介紹
HarmonyOS ArkUI提供了豐富多樣的UI組件,您可以使用這些組件輕松地編寫出更加豐富、漂亮的界面。在本篇Codelab中,您將通過(guò)一個(gè)簡(jiǎn)單的購(gòu)物社交應(yīng)用示例,學(xué)習(xí)如何使用常用的基礎(chǔ)組件和容器組件。
本示例主要包含:“登錄”、“首頁(yè)”、“我的”三個(gè)頁(yè)面,效果圖如下:
相關(guān)概念
- [Text]:顯示一段文本的組件。
- [Image]:圖片組件,支持本地圖片和網(wǎng)絡(luò)圖片的渲染展示。
- [TextInput]:可以輸入單行文本并支持響應(yīng)輸入事件的組件。
- [Button]:按鈕組件,可快速創(chuàng)建不同樣式的按鈕。
- [LoadingProgress]:用于顯示加載動(dòng)效的組件。
- [Flex]:應(yīng)用彈性方式布局子組件的容器組件。
- [Column]:沿垂直方向布局的容器。
- [Row]:沿水平方向布局容器。
- [List]:列表包含一系列相同寬度的列表項(xiàng)。適合連續(xù)、多行呈現(xiàn)同類數(shù)據(jù),例如圖片和文本。
- [Swiper]:滑動(dòng)容器,提供切換子組件顯示的能力。
- [Grid]:網(wǎng)格容器,由“行”和“列”分割的單元格所組成,通過(guò)指定“項(xiàng)目”所在的單元格做出各種各樣的布局。
環(huán)境搭建
軟件要求
- [DevEco Studio]版本:DevEco Studio 3.1 Release。
- OpenHarmony SDK版本:API version 9。
硬件要求
- 開(kāi)發(fā)板類型:[潤(rùn)和RK3568開(kāi)發(fā)板]。
- OpenHarmony系統(tǒng):3.2 Release。
環(huán)境搭建
我們首先要完成開(kāi)發(fā)環(huán)境的搭建,本示例以RK3568開(kāi)發(fā)板為例,參照以下步驟進(jìn)行:
- [獲取OpenHarmony系統(tǒng)版本]:標(biāo)準(zhǔn)系統(tǒng)解決方案(二進(jìn)制)。以3.2 Release版本為例:
- 搭建燒錄環(huán)境。
- [完成DevEco Device Tool的安裝]
- [完成RK3568開(kāi)發(fā)板的燒錄](méi)
- 搭建開(kāi)發(fā)環(huán)境。
- 開(kāi)始前請(qǐng)參考[工具準(zhǔn)備],完成DevEco Studio的安裝和開(kāi)發(fā)環(huán)境配置。
- 開(kāi)發(fā)環(huán)境配置完成后,請(qǐng)參考[使用工程向?qū)創(chuàng)建工程(模板選擇“Empty Ability”)。
- 工程創(chuàng)建完成后,選擇使用[真機(jī)進(jìn)行調(diào)測(cè)]。
- 鴻蒙開(kāi)發(fā)指導(dǎo)文檔:[
qr23.cn/FBD4cY
]
代碼結(jié)構(gòu)解讀
本篇Codelab只對(duì)核心代碼進(jìn)行講解,對(duì)于完整代碼,我們會(huì)在附件下載和gitee源碼中提供下載方式。
├──entry/src/main/ets // 代碼區(qū)
│ ├──common
│ │ └──constants
│ │ └──CommonConstants.ets // 公共常量類
│ ├──entryability
│ │ └──EntryAbility.ts // 程序入口類
│ ├──pages
│ │ ├──LoginPage.ets // 登錄界面
│ │ └──MainPage.ets // 主界面
│ ├──view
│ │ ├──Home.ets // 首頁(yè)
│ │ └──Setting.ets // 設(shè)置頁(yè)
│ └──viewmodel
│ ├──ItemData.ets // 列表數(shù)據(jù)實(shí)體類
│ └──MainViewModel.ets // 主界面視圖Model
└──entry/src/main/resources // 應(yīng)用資源目錄
`HarmonyOS與OpenHarmony鴻蒙文檔籽料:mau123789是v直接拿`
實(shí)現(xiàn)“登錄”頁(yè)面
本節(jié)主要介紹“登錄”頁(yè)面的實(shí)現(xiàn),效果圖如下:
界面使用Column容器組件布局,由Image、Text、TextInput、Button、LoadingProgress等基礎(chǔ)組件構(gòu)成,主要代碼如下:
// LoginPage.ets
@Entry
@Component
struct LoginPage {
...
build() {
Column() {
Image($r('app.media.logo'))
...
Text($r('app.string.login_page'))
...
Text($r('app.string.login_more'))
...
TextInput({ placeholder: $r('app.string.account') })
...
TextInput({ placeholder: $r('app.string.password') })
...
Row() {
Text($r('app.string.message_login')).blueTextStyle()
Text($r('app.string.forgot_password')).blueTextStyle()
}
....
Button($r('app.string.login'), { type: ButtonType.Capsule })
....
Text($r('app.string.register_account'))
....
if (this.isShowProgress) {
LoadingProgress()
....
}
Blank()
Text($r('app.string.other_login_method'))
....
Row({ space: CommonConstants.LOGIN_METHODS_SPACE }) {
this.imageButton($r('app.media.login_method1'))
this.imageButton($r('app.media.login_method2'))
this.imageButton($r('app.media.login_method3'))
}
}
....
}
}
獲取用戶輸入
當(dāng)用戶登錄前,需要獲取用戶輸入的帳號(hào)和密碼才能執(zhí)行登錄邏輯。給TextInput設(shè)置onChange事件,在onChange事件里面實(shí)時(shí)獲取用戶輸入的文本信息。
// LoginPage.ets
TextInput({ placeholder: $r('app.string.account') })
.maxLength(CommonConstants.INPUT_ACCOUNT_LENGTH)
.type(InputType.Number)
.inputStyle()
.onChange((value: string) = > {
this.account = value;
})
控制LoadingProgress顯示和隱藏
給登錄按鈕綁定onClick事件,調(diào)用login方法模擬登錄。定義變量isShowProgress結(jié)合條件渲染if用來(lái)控制LoadingProgress的顯示和隱藏。當(dāng)用戶點(diǎn)擊按鈕時(shí)設(shè)置isShowProgress為true,即顯示LoadingProgress;使用定時(shí)器setTimeout設(shè)置isShowProgress 2秒后為false,即隱藏LoadingProgress,然后執(zhí)行跳轉(zhuǎn)到首頁(yè)的邏輯。
// LoginPage.ets
@Entry
@Component
struct LoginPage {
@State account: string = '';
@State password: string = '';
@State isShowProgress: boolean = false;
private timeOutId = null;
...
login() {
if (this.account === '' || this.password === '') {
prompt.showToast({
message: $r('app.string.input_empty_tips')
})
} else {
this.isShowProgress = true;
if (this.timeOutId === null) {
this.timeOutId = setTimeout(() = > {
this.isShowProgress = false;
this.timeOutId = null;
router.replaceUrl({ url: 'pages/MainPage' });
}, CommonConstants.LOGIN_DELAY_TIME);
}
}
}
...
build() {
Column() {
...
Button($r('app.string.login'), { type: ButtonType.Capsule })
....
.onClick(() = > {
this.login();
})
...
if (this.isShowProgress) {
LoadingProgress()
.color($r('app.color.loading_color'))
.width($r('app.float.login_progress_size'))
.height($r('app.float.login_progress_size'))
.margin({ top: $r('app.float.login_progress_margin_top') })
}
...
}
...
}
}
實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)
頁(yè)面間的跳轉(zhuǎn)可以使用router模塊相關(guān)API來(lái)實(shí)現(xiàn),使用前需要先導(dǎo)入該模塊,然后使用router.replace()方法實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)。
// LoginPage.ets
import router from '@ohos.router';
login() {
if (this.account === '' || this.password === '') {
...
} else {
this.isShowProgress = true;
if (this.timeOutId === -1) {
this.timeOutId = setTimeout(() = > {
this.isShowProgress = false;
this.timeOutId = -1;
router.replaceUrl({ url: 'pages/MainPage' });
}, CommonConstants.LOGIN_DELAY_TIME);
}
}
}
實(shí)現(xiàn)“首頁(yè)”和“我的”頁(yè)面
定義資源數(shù)據(jù)
由于“首頁(yè)”和“我的”頁(yè)面中有多處圖片和文字的組合,因此提取出ItemData類。在MainViewModel.ets文件中對(duì)頁(yè)面使用的資源進(jìn)行定義,在MainViewModel.ets文件中定義數(shù)據(jù)。
// ItemData.ets
export default class PageResource {
title: Resource;
img?: Resource;
others?: Resource;
constructor(title: Resource, img?: Resource, others?: Resource) {
this.title = title;
this.img = img;
this.others = others;
}
}
// MainViewModel.ets
import ItemData from './temData';
export class MainViewModel {
...
getFirstGridData(): Array< ItemData > {
let firstGridData: ItemData[] = [
new ItemData($r('app.string.my_love'), $r('app.media.love')),
new ItemData($r('app.string.history_record'), $r('app.media.record')),
...
];
return firstGridData;
}
...
}
export default new MainViewModel();
實(shí)現(xiàn)頁(yè)面框架
從前面介紹章節(jié)的示意圖可以看出,本示例由兩個(gè)tab頁(yè)組成,使用Tabs組件來(lái)實(shí)現(xiàn),提取tabBar的公共樣式,同時(shí)設(shè)置TabContent和Tabs的backgroundColor來(lái)實(shí)現(xiàn)底部tabBar欄背景色突出的效果。
// MainPage.ets
Tabs({
barPosition: BarPosition.End,
controller: this.tabsController
}) {
TabContent() {
...
}
...
.backgroundColor($r('app.color.mainPage_backgroundColor')) // “首頁(yè)”的頁(yè)面背景色
.tabBar(this.TabBuilder(CommonConstants.HOME_TITLE, CommonConstants.HOME_TAB_INDEX,
$r('app.media.home_selected'), $r('app.media.home_normal')))
...
}
...
.backgroundColor(Color.White) // 底部tabBar欄背景色
...
.onChange((index: number) = > {
this.currentIndex = index;
})
...
實(shí)現(xiàn)“首頁(yè)”內(nèi)容
“首頁(yè)”效果如下所示:
從上面效果如可以看出“首頁(yè)”由三部分內(nèi)容組成分別是輪播圖、24柵格圖、44柵格圖。首先使用Swiper組件實(shí)現(xiàn)輪播圖,無(wú)需設(shè)置圖片大小。
// Home.ets
Swiper(this.swiperController) {
ForEach(mainViewModel.getSwiperImages(), (img: Resource) = > {
Image(img).borderRadius($r('app.float.home_swiper_borderRadius'))
}, (img: Resource) = > JSON.stringify(img.id))
}
...
.autoPlay(true)
...
然后使用Grid組件實(shí)現(xiàn)2*4柵格圖,代碼如下
// Home.ets
Grid() {
ForEach(mainViewModel.getFirstGridData(), (item: ItemData) = > {
GridItem() {
Column() {
Image(item.img)
.width($r('app.float.home_homeCell_size'))
.height($r('app.float.home_homeCell_size'))
Text(item.title)
.fontSize($r('app.float.little_text_size'))
.margin({ top: $r('app.float.home_homeCell_margin') })
}
}
}, (item: ItemData) = > JSON.stringify(item))
}
.columnsTemplate('1fr 1fr 1fr 1fr')
.rowsTemplate('1fr 1fr')
...
使用Grid組件實(shí)現(xiàn)4*4柵格列表欄,其中單個(gè)柵格中有一張背景圖片和兩行字體不同的文本,因此在Column組件中放置兩個(gè)Text組件,并設(shè)置背景圖,注意Grid組件必須設(shè)置高度,否則可能出現(xiàn)頁(yè)面空白。
// Home.ets
Grid() {
ForEach(mainViewModel.getSecondGridData(), (secondItem: ItemData) = > {
GridItem() {
Column() {
Text(secondItem.title)
...
Text(secondItem.others)
...
}
.alignItems(HorizontalAlign.Start)
}
...
.backgroundImage(secondItem.img)
.backgroundImageSize(ImageSize.Cover)
...
}, (secondItem: ItemData) = > JSON.stringify(secondItem))
}
...
.height($r('app.float.home_secondGrid_height'))
.columnsTemplate('1fr 1fr')
.rowsTemplate('1fr 1fr')
...
實(shí)現(xiàn)“我的”頁(yè)內(nèi)容
“我的”頁(yè)面效果圖如下:
使用List組件結(jié)合ForEach語(yǔ)句來(lái)實(shí)現(xiàn)頁(yè)面列表內(nèi)容,其中引用了settingCell子組件,列表間的灰色分割線可以使用Divider屬性實(shí)現(xiàn),代碼實(shí)現(xiàn)如下:
// Setting.ets
List() {
ForEach(mainViewModel.getSettingListData(), (item: ItemData) = > {
ListItem() {
this.settingCell(item)
}
.height($r('app.float.setting_list_height'))
}, (item: ItemData) = > JSON.stringify(item))
}
...
.divider({ // 設(shè)置分隔線
...
})
...
@Builder settingCell(item: ItemData) {
Row() {
Row({ space: CommonConstants.COMMON_SPACE }) {
Image(item.img)
...
Text(item.title)
...
}
if (item.others === null) {
Image($r("app.media.right_grey"))
...
} else {
Toggle({ type: ToggleType.Switch, isOn: false })
}
}
.justifyContent(FlexAlign.SpaceBetween) // 相鄰元素之間距離相同
...
}
審核編輯 黃宇
-
開(kāi)發(fā)板
+關(guān)注
關(guān)注
25文章
4943瀏覽量
97191 -
組件
+關(guān)注
關(guān)注
1文章
505瀏覽量
17802 -
鴻蒙
+關(guān)注
關(guān)注
57文章
2309瀏覽量
42740 -
HarmonyOS
+關(guān)注
關(guān)注
79文章
1967瀏覽量
30017 -
OpenHarmony
+關(guān)注
關(guān)注
25文章
3659瀏覽量
16151
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論