介紹
本篇Codelab是基于ArkTS的聲明式開(kāi)發(fā)范式實(shí)現(xiàn)的樣例,主要介紹了數(shù)據(jù)請(qǐng)求和touch事件的使用。包含以下功能:
- 數(shù)據(jù)請(qǐng)求。
- 列表下拉刷新。
- 列表上拉加載。
相關(guān)概念
- [List組件]:列表包含一系列相同寬度的列表項(xiàng)。
- [Tabs]:通過(guò)頁(yè)簽進(jìn)行內(nèi)容視圖切換。
- [TabContent]:僅在Tabs中使用,對(duì)應(yīng)一個(gè)切換頁(yè)簽的內(nèi)容視圖。
- [數(shù)據(jù)請(qǐng)求]:提供HTTP數(shù)據(jù)請(qǐng)求能力。
- [觸摸事件onTouch]:觸摸動(dòng)作觸發(fā)調(diào)用該方法。
相關(guān)權(quán)限
添加網(wǎng)絡(luò)權(quán)限:ohos.permission.INTERNET。
使用說(shuō)明
服務(wù)端搭建流程
- 搭建nodejs環(huán)境:本篇Codelab的服務(wù)端是基于nodejs實(shí)現(xiàn)的,需要安裝nodejs,如果您本地已有nodejs環(huán)境可以跳過(guò)此步驟。
- 構(gòu)建局域網(wǎng)環(huán)境:測(cè)試本Codelab時(shí)要確保運(yùn)行服務(wù)端代碼的電腦和測(cè)試機(jī)連接的是同一局域網(wǎng)下的網(wǎng)絡(luò),您可以用您的手機(jī)開(kāi)一個(gè)個(gè)人熱點(diǎn),然后將測(cè)試機(jī)和運(yùn)行服務(wù)端代碼的電腦都連接您的手機(jī)熱點(diǎn)進(jìn)行測(cè)試。
- 運(yùn)行服務(wù)端代碼:在本項(xiàng)目的HttpServerOfNews目錄下打開(kāi)命令行工具,輸入npm install 安裝服務(wù)端依賴包,安裝成功后輸入npm start點(diǎn)擊回車。看到“服務(wù)器啟動(dòng)成功!”則表示服務(wù)端已經(jīng)在正常運(yùn)行。
- 連接服務(wù)器地址:打開(kāi)命令行工具,輸入ipconfig命令查看本地ip,將本地ip地址復(fù)制到src/main/ets/common/constant/CommonConstants.ets文件下的23行,注意只替換ip地址部分,不要修改端口號(hào),保存好ip之后即可運(yùn)行Codelab進(jìn)行測(cè)試。
前端使用說(shuō)明
- 點(diǎn)擊應(yīng)用進(jìn)入主頁(yè)面,頁(yè)面使用tabBar展示新聞分類,tabContent展示新聞列表,新聞分類和新聞列表通過(guò)請(qǐng)求nodejs服務(wù)端獲取。
- 點(diǎn)擊頁(yè)簽或左右滑動(dòng)頁(yè)面,切換標(biāo)簽并展示對(duì)應(yīng)新聞?lì)愋偷臄?shù)據(jù)。
- 新聞列表頁(yè)面,滑動(dòng)到新聞列表首項(xiàng)數(shù)據(jù),接著往下滑動(dòng)會(huì)觸發(fā)下拉刷新操作,頁(yè)面更新初始4條新聞數(shù)據(jù),滑動(dòng)到新聞列表最后一項(xiàng)數(shù)據(jù),往上拉會(huì)觸發(fā)上拉加載操作,新聞列表會(huì)在后面加載4條新聞數(shù)據(jù)。
環(huán)境搭建
鴻蒙開(kāi)發(fā)指導(dǎo)文檔:[gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md
]
軟件要求
- [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)境搭建
完成本篇Codelab我們首先要完成開(kāi)發(fā)環(huán)境的搭建,本示例以RK3568開(kāi)發(fā)板為例,參照以下步驟進(jìn)行:
- [獲取OpenHarmony系統(tǒng)版本]:標(biāo)準(zhǔn)系統(tǒng)解決方案(二進(jìn)制)。以3.2Release版本為例:
- 搭建燒錄環(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è)]。
代碼結(jié)構(gòu)解讀
本篇Codelab只對(duì)核心代碼進(jìn)行講解,對(duì)于完整代碼,我們會(huì)在gitee中提供。
├──entry/src/main/ets // ArkTS代碼區(qū)
│ ├──common
│ │ ├──constant
│ │ │ └──CommonConstant.ets // 公共常量類
│ │ └──utils
│ │ ├──HttpUtil.ets // 網(wǎng)絡(luò)請(qǐng)求方法
│ │ ├──Logger.ets // 日志工具類
│ │ ├──PullDownRefresh.ets // 下拉刷新方法
│ │ └──PullUpLoadMore.ets // 上拉加載更多方法
│ ├──entryability
│ │ └──EntryAbility.ts // 程序入口類
│ ├──pages
│ │ └──Index.ets // 主頁(yè)面
│ ├──view
│ │ ├──CustomRefreshLoadLayout.ets // 下拉刷新、上拉加載布局文件
│ │ ├──LoadMoreLayout.ets // 上拉加載布局封裝
│ │ ├──NewsItem.ets // 新聞數(shù)據(jù)
│ │ ├──NewsList.ets // 新聞列表
│ │ ├──NoMoreLayout.ets // 上拉停止布局封裝
│ │ ├──RefreshLayout.ets // 下拉刷新布局封裝
│ │ └──TabBar.ets // 新聞?lì)愋晚?yè)簽
│ └──viewmodel
│ ├──NewsModel.ets // 新聞模型類
│ └──NewsViewModel.ets // 新聞ViewModel
├──entry/src/main/resources // 資源文件目錄
└──HttpServerOfNews // 服務(wù)端代碼
構(gòu)建主界面
本章節(jié)將介紹新聞列表頁(yè)面的實(shí)現(xiàn),用tabBar展示新聞分類,tabContent展示新聞列表,效果圖如圖所示:
在TabBar.ets文件中的aboutToAppear()方法里獲取新聞分類。
// TabBar.ets
aboutToAppear() {
// 請(qǐng)求服務(wù)端新聞?lì)悇e
NewsViewModel.getNewsTypeList().then((typeList: NewsTypeBean[]) = > {
this.tabBarArray = typeList;
}).catch((typeList: NewsTypeBean[]) = > {
this.tabBarArray = typeList;
});
}
在NewsList.ets文件中的aboutToAppear()方法里獲取新聞數(shù)據(jù),將數(shù)據(jù)加載到新聞列表頁(yè)面ListLayout布局中。
// NewsList.ets
changeCategory() {
this.newsModel.currentPage = 1;
NewsViewModel.getNewsList(this.newsModel.currentPage, this.newsModel.pageSize, Const.GET_NEWS_LIST)
.then((data: NewsData[]) = > {
this.newsModel.pageState = PageState.Success;
if (data.length === this.newsModel.pageSize) {
this.newsModel.currentPage++;
this.newsModel.hasMore = true;
} else {
this.newsModel.hasMore = false;
}
this.newsModel.newsData = data;
})
.catch((err: string | Resource) = > {
promptAction.showToast({
message: err,
duration: Const.ANIMATION_DURATION
});
this.newsModel.pageState = PageState.Fail;
});
}
aboutToAppear() {
// 請(qǐng)求服務(wù)端新聞數(shù)據(jù)
this.changeCategory();
}
...
@Builder ListLayout() {
List() {
...
ForEach(this.newsModel.newsData, (item: NewsData) = > {
ListItem() {
// 新聞數(shù)據(jù)
NewsItem({ newsData: item })
}
.height($r('app.float.news_list_height'))
.backgroundColor($r('app.color.white'))
.margin({ top: $r('app.float.news_list_margin_top') })
.borderRadius(Const.NewsListConstant_ITEM_BORDER_RADIUS)
}, (item: NewsData, index?: number) = > JSON.stringify(item) + index)
...
}
...
}
數(shù)據(jù)請(qǐng)求
在module.json5文件中配置如下權(quán)限:
// module.json5
"requestPermissions": [
{
"name": "ohos.permission.INTERNET",
"reason": "$string:dependency_reason",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "inuse"
}
}
]
這一章節(jié),將基于新聞數(shù)據(jù)請(qǐng)求來(lái)介紹如何從服務(wù)端請(qǐng)求數(shù)據(jù)。
導(dǎo)入http模塊,封裝httpRequestGet方法,調(diào)用者傳入url地址和所需參數(shù)發(fā)起網(wǎng)絡(luò)數(shù)據(jù)請(qǐng)求。
// HttpUtil.ets
import http from '@ohos.net.http';
...
export function httpRequestGet(url: string): Promise< ResponseResult > {
let httpRequest = http.createHttp();
// 發(fā)送數(shù)據(jù)請(qǐng)求
let responseResult = httpRequest.request(url, {
method: http.RequestMethod.GET,
readTimeout: Const.HTTP_READ_TIMEOUT,
header: {
'Content-Type': ContentType.JSON
},
connectTimeout: Const.HTTP_READ_TIMEOUT,
extraData: {}
});
let serverData: ResponseResult = new ResponseResult();
// 處理數(shù)據(jù),并返回
return responseResult.then((value: http.HttpResponse) = > {
Logger.info(`http value ${JSON.stringify(value)}`);
if (value.responseCode === Const.HTTP_CODE_200) {
// 獲取返回?cái)?shù)據(jù)
let result = `${value.result}`;
let resultJson: ResponseResult = JSON.parse(result);
if (resultJson.code === Const.SERVER_CODE_SUCCESS) {
serverData.data = resultJson.data;
}
serverData.code = resultJson.code;
serverData.msg = resultJson.msg;
} else {
serverData.msg = `${$r('app.string.http_error_message')}&${value.responseCode}`;
}
return serverData;
}).catch(() = > {
serverData.msg = $r('app.string.http_error_message');
return serverData;
})
}
在NewsViewModel.ets文件中封裝getNewsList方法,調(diào)用httpRequestGet方法請(qǐng)求服務(wù)端,用Promise異步保存返回的新聞數(shù)據(jù)列表。
// NewsViewModel.ets
// 獲取服務(wù)端新聞數(shù)據(jù)列表
getNewsList(currentPage: number, pageSize: number, path: string): Promise< NewsData[] > {
return new Promise(async (resolve: Function, reject: Function) = > {
let url = `${Const.SERVER}/${path}`;
url += '?currentPage=' + currentPage + '&pageSize=' + pageSize;
httpRequestGet(url).then((data: ResponseResult) = > {
if (data.code === Const.SERVER_CODE_SUCCESS) {
resolve(data.data);
} else {
Logger.error('getNewsList failed', JSON.stringify(data));
reject($r('app.string.page_none_msg'));
}
}).catch((err: Error) = > {
Logger.error('getNewsList failed', JSON.stringify(err));
reject($r('app.string.http_error_message'));
});
});
}
下拉刷新
本章節(jié)將以下拉刷新的功能效果來(lái)介紹touch事件的使用。效果圖如圖所示:
創(chuàng)建一個(gè)下拉刷新布局CustomLayout,動(dòng)態(tài)傳入刷新圖片和刷新文字描述。
// CustomRefreshLoadLayout.ets
build() {
Row() {
// 下拉刷新圖片
Image(this.customRefreshLoadClass.imageSrc)
...
// 下拉刷新文字
Text(this.customRefreshLoadClass.textValue)
...
}
...
}
將下拉刷新的布局添加到NewsList.ets文件中新聞列表布局ListLayout里面,監(jiān)聽(tīng)ListLayout組件的onTouch事件實(shí)現(xiàn)下拉刷新。
// NewsList.ets
build() {
Column() {
if (this.newsModel.pageState === PageState.Success) {
this.ListLayout()
}
...
}
...
.onTouch((event: TouchEvent | undefined) = > {
if (event) {
if (this.newsModel.pageState === PageState.Success) {
listTouchEvent(this.newsModel, event);
}
}
})
}
...
@Builder ListLayout() {
List() {
ListItem() {
RefreshLayout({
refreshLayoutClass: new CustomRefreshLoadLayoutClass(this.newsModel.isVisiblePullDown, this.newsModel.pullDownRefreshImage,
this.newsModel.pullDownRefreshText, this.newsModel.pullDownRefreshHeight)
})
...
}
}
...
}
- 在onTouch事件中,listTouchEvent方法判斷觸摸事件是否滿足下拉條件。如下listTouchEvent所示:
- 在touchMovePullRefresh方法中,我們將對(duì)下拉的偏移量與下拉刷新布局的高度進(jìn)行對(duì)比,如果大于布局高度并且在新聞列表的頂部,則表示達(dá)到刷新條件。如下touchMovePullRefresh所示:
- 在pullRefreshState方法中我們會(huì)對(duì)下拉刷新布局中的狀態(tài)圖片和描述進(jìn)行改變,如下pullRefreshState所示:
- 當(dāng)手指松開(kāi),才執(zhí)行刷新操作。
// PullDownRefresh.ets
export function listTouchEvent(newsModel: NewsModel, event: TouchEvent) {
switch (event.type) {
...
case TouchType.Move:
if ((newsModel.isRefreshing === true) || (newsModel.isLoading === true)) {
return;
}
let isDownPull = event.touches[0].y - newsModel.lastMoveY > 0;
if (((isDownPull === true) || (newsModel.isPullRefreshOperation === true)) && (newsModel.isCanLoadMore === false))
{
// 手指移動(dòng),處理下拉刷新
touchMovePullRefresh(newsModel, event);
}
...
break;
}
}
export function touchMovePullRefresh(newsModel: NewsModel, event: TouchEvent) {
if (newsModel.startIndex === 0) {
newsModel.isPullRefreshOperation = true;
let height = vp2px(newsModel.pullDownRefreshHeight);
newsModel.offsetY = event.touches[0].y - newsModel.downY;
// 滑動(dòng)偏移量大于下拉刷新布局高度,滿足刷新條件。
if (newsModel.offsetY >= height) {
pullRefreshState(newsModel, RefreshState.Release);
newsModel.offsetY = height + newsModel.offsetY * Const.Y_OFF_SET_COEFFICIENT;
} else {
pullRefreshState(newsModel, RefreshState.DropDown);
}
if (newsModel.offsetY < 0) {
newsModel.offsetY = 0;
newsModel.isPullRefreshOperation = false;
}
}
}
export function pullRefreshState(newsModel: NewsModel, state: number) {
switch (state) {
...
case RefreshState.Release:
newsModel.pullDownRefreshText = $r('app.string.release_refresh_text');
newsModel.pullDownRefreshImage = $r('app.media.ic_pull_up_refresh');
newsModel.isCanRefresh = true;
newsModel.isRefreshing = false;
break;
case RefreshState.Refreshing:
newsModel.offsetY = vp2px(newsModel.pullDownRefreshHeight);
newsModel.pullDownRefreshText = $r('app.string.refreshing_text');
newsModel.pullDownRefreshImage = $r('app.media.ic_pull_up_load');
newsModel.isCanRefresh = true;
newsModel.isRefreshing = true;
break;
case RefreshState.Success:
newsModel.pullDownRefreshText = $r('app.string.refresh_success_text');
newsModel.pullDownRefreshImage = $r('app.media.ic_succeed_refresh');
newsModel.isCanRefresh = true;
newsModel.isRefreshing = true;
break;
...
default:
break;
}
}
上拉加載也是通過(guò)touch事件來(lái)實(shí)現(xiàn)的。
-
鴻蒙
+關(guān)注
關(guān)注
57文章
2393瀏覽量
43072 -
HarmonyOS
+關(guān)注
關(guān)注
79文章
1983瀏覽量
30593 -
OpenHarmony
+關(guān)注
關(guān)注
25文章
3749瀏覽量
16606
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
HarmonyOS開(kāi)發(fā)案例:【W(wǎng)eb組件實(shí)現(xiàn)抽獎(jiǎng)】
![<b class='flag-5'>HarmonyOS</b><b class='flag-5'>開(kāi)發(fā)案</b>例:【W(wǎng)eb組件實(shí)現(xiàn)抽獎(jiǎng)】](https://file1.elecfans.com/web2/M00/DE/34/wKgaomYs8UKAOssaAIuvbARKFBc975.jpg)
HarmonyOS IoT 硬件開(kāi)發(fā)案例分享
【潤(rùn)和直播課預(yù)告@華為開(kāi)發(fā)者學(xué)院】HarmonyOS設(shè)備開(kāi)發(fā)基礎(chǔ)課程|HiSpark WiFi-IoT 智能小車套件開(kāi)發(fā)案例
Linux應(yīng)用開(kāi)發(fā)手冊(cè)之Python開(kāi)發(fā)案例
許思維老師HarmonyOS IoT硬件開(kāi)發(fā)案例分享
![許思維老師<b class='flag-5'>HarmonyOS</b> IoT硬件<b class='flag-5'>開(kāi)發(fā)案</b>例分享](https://file.elecfans.com/web1/M00/CC/97/o4YBAF-aKPeAcD_CAAPXIjiUBjQ116.png)
華為開(kāi)發(fā)者HarmonyOS零基礎(chǔ)入門(mén):UI組件設(shè)計(jì)開(kāi)發(fā)實(shí)踐
![華為<b class='flag-5'>開(kāi)發(fā)</b>者<b class='flag-5'>HarmonyOS</b>零基礎(chǔ)入門(mén):UI組件設(shè)計(jì)<b class='flag-5'>開(kāi)發(fā)</b>實(shí)踐](https://file.elecfans.com/web2/M00/19/1B/poYBAGFzeQ2AaiUNAAWi7fViCKk130.png)
華為開(kāi)發(fā)者HarmonyOS零基礎(chǔ)入門(mén):應(yīng)用數(shù)據(jù)加載顯示模型
![華為<b class='flag-5'>開(kāi)發(fā)</b>者<b class='flag-5'>HarmonyOS</b>零基礎(chǔ)入門(mén):應(yīng)用<b class='flag-5'>數(shù)據(jù)</b><b class='flag-5'>加載</b>顯示模型](https://file.elecfans.com/web2/M00/19/1C/poYBAGFzep-AdeXsAAbaKeOTZgM950.png)
零基礎(chǔ)入門(mén)HarmonyOS-應(yīng)用數(shù)據(jù)按需加載顯示模型
![零基礎(chǔ)入門(mén)<b class='flag-5'>HarmonyOS</b>-應(yīng)用<b class='flag-5'>數(shù)據(jù)</b>按需<b class='flag-5'>加載</b>顯示模型](https://file.elecfans.com/web2/M00/19/27/pYYBAGFzm7mAS9XxAAbPRlLUn9I969.png)
華為開(kāi)發(fā)者分論壇HarmonyOS學(xué)生公開(kāi)課-OpenHarmony Codelabs開(kāi)發(fā)案例
![華為<b class='flag-5'>開(kāi)發(fā)</b>者分論壇<b class='flag-5'>HarmonyOS</b>學(xué)生公開(kāi)課-OpenHarmony Codelabs<b class='flag-5'>開(kāi)發(fā)案</b>例](https://file.elecfans.com/web2/M00/19/8E/pYYBAGF00USAR-78AAD-2dnL6jM191.jpg)
評(píng)論