HarmonyOS ArkUI之列表下拉刷新、加載更多(TS)
想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
簡(jiǎn)介
本項(xiàng)目界面搭建基于ArkUI中TS擴(kuò)展的聲明式開(kāi)發(fā)范式,關(guān)于語(yǔ)法和概念直接看官網(wǎng)官方文檔地址:
基于TS擴(kuò)展的聲明式開(kāi)發(fā)范式1、基于TS擴(kuò)展的聲明式開(kāi)發(fā)范式2
本文介紹列表刷新:
- 下拉刷新
- 上拉加載更多
效果演示

主要知識(shí)點(diǎn)
列表容器(List)、觸摸事件(onTouch)、位置設(shè)置(offset)、顯示動(dòng)畫(huà)(animateTo)
實(shí)現(xiàn)思路
主要根據(jù)List中的回調(diào)方法onScrollIndex()監(jiān)聽(tīng)當(dāng)前列表首尾索引,根據(jù)觸摸事件onTouch()處理下拉和上拉。
下拉刷新效果
1、容器布局Column垂直結(jié)構(gòu): 下拉刷新、列表。父容器設(shè)置touch事件,如果當(dāng)列表無(wú)數(shù)據(jù)或者數(shù)據(jù)少,可以全局響應(yīng)。
初始偏移量
下拉刷新: 1(負(fù))自身高度。在屏幕頂部之外。
列表:0,默認(rèn)在頂部。
(部分關(guān)鍵代碼)
- ......
- // 下拉刷新的布局高度
- private pullRefreshHeight = 70
- // 列表y坐標(biāo)偏移量
- @State offsetY: number = 0
- build() {
- Column() {
- // 下拉刷新
- Flex() {
- ......
- }
- .width('100%')
- .height(this.pullRefreshHeight)
- .offset({ x: 0, y: `${vp2px(-this.pullRefreshHeight) + this.offsetY}px` }) // 布局跟著列表偏移量移動(dòng)
- // 列表
- List(){
- ......
- }
- .offset({ x: 0, y: `${this.offsetY}px` }) // touch事件計(jì)算的偏移量單位是px,記得加上單位
- .onScrollIndex((start, end) => { // 監(jiān)聽(tīng)當(dāng)前列表首位索引
- console.info(`${start}=start============end=${end}`)
- this.startIndex = start
- this.endIndex = end
- })
- }
- .width('100%')
- .height('100%')
- .onTouch((event) => this.listTouchEvent(event)) // 父容器設(shè)置touch事件,當(dāng)列表無(wú)數(shù)據(jù)也可以下拉刷新。
- }
- ......
2、touch觸摸事件:
- 手指移動(dòng)下拉改變偏移量;
- 手指抬起根據(jù)是否可以刷新:顯示刷新?tīng)顟B(tài);
- 請(qǐng)求數(shù)據(jù)成功后,關(guān)閉刷新?tīng)顟B(tài)。
(部分關(guān)鍵代碼)
- ......
- // 按下的y坐標(biāo)
- private downY = 0
- listTouchEvent(event: TouchEvent){
- switch (event.type) {
- case TouchType.Down: // 手指按下
- // 記錄按下的y坐標(biāo)
- this.downY = event.touches[0].y
- break
- case TouchType.Move: // 手指移動(dòng)
- // 當(dāng)首部索引位于0
- if (this.startIndex == 0) {
- // 下拉刷新布局高度
- var height = vp2px(this.pullRefreshHeight)
- // 滑動(dòng)的偏移量
- this.offsetY = event.touches[0].y - this.downY
- // 偏移量大于下拉刷新布局高度,達(dá)到刷新條件
- if (this.offsetY >= height) {
- // 可以刷新了
- this.isCanRefresh = true
- // 狀態(tài)1:松開(kāi)刷新
- this.pullRefreshState(1)
- // 偏移量的值緩慢增加
- this.offsetY = height + this.offsetY * 0.15
- } else {
- // 狀態(tài)0:下拉刷新
- this.pullRefreshState(0)
- }
- }
- break
- case TouchType.Up: // 手指抬起
- case TouchType.Cancel: // 觸摸意外中斷:來(lái)電界面
- // 是否可以刷新
- if (this.isCanRefresh) {
- console.info('======執(zhí)行下拉刷新========')
- // 偏移量為下拉刷新布局高度
- this.offsetY = vp2px(this.pullRefreshHeight)
- // 狀態(tài)2:正在刷新
- this.pullRefreshState(2)
- // 模擬耗時(shí)操作
- setTimeout(() => {
- // 刷新數(shù)據(jù)
- this.refreshData()
- // 關(guān)閉刷新
- this.closeRefresh()
- }, 2000)
- } else {
- console.info('======關(guān)閉下拉刷新!未達(dá)到條件========')
- // 關(guān)閉刷新
- this.closeRefresh()
- }
- break
- }
- }
- ......
以上關(guān)鍵代碼就能實(shí)現(xiàn)下拉刷新
下拉不釋放繼續(xù)上拉可以取消下拉刷新,未達(dá)到條件:動(dòng)畫(huà)收回。

到達(dá)條件:如果一直下拉,下拉偏移量緩慢增加(阻力效果),手指抬起偏移量回到下拉刷新布局高度,等待主動(dòng)關(guān)閉刷新。

上拉加載更多
相對(duì)下拉刷新,上拉加載更多實(shí)現(xiàn)方式比較簡(jiǎn)單。
1、布局結(jié)構(gòu):
就是在List末尾加上ListItem(),當(dāng)?shù)搅俗詈笠晃唬屏窟_(dá)到加載更多的條件,動(dòng)態(tài)顯示布局。
(部分關(guān)鍵代碼)
- ......
- // 上拉加載的布局默認(rèn)高度
- private loadMoreDefaultHeight = 70
- // 上拉加載的布局是否顯示
- @State isVisibleLoadMore: boolean = false
- build() {
- Column() {
- // 下拉刷新
- ......
- // 列表
- List(){
- ForEach(this.list, item => {
- ListItem() {
- Column() {
- Text(`我是測(cè)試內(nèi)容${item}`)
- .padding(15)
- .fontSize(18)
- }
- }
- }, item => item.toString())
- // =======================新增代碼start==============================
- // 加載更多布局
- ListItem(){
- Flex() {
- ......
- }
- .width('100%')
- .height(this.loadMoreHeight)
- .visibility(this.isVisibleLoadMore ? Visibility.Visible : Visibility.None) // 是否顯示布局
- }
- // =======================新增代碼end==============================
- }
- .offset({ x: 0, y: `${this.offsetY}px` }) // touch事件計(jì)算的偏移量單位是px,記得加上單位
- .onScrollIndex((start, end) => { // 監(jiān)聽(tīng)當(dāng)前列表首位索引
- console.info(`${start}=start============end=${end}`)
- this.startIndex = start
- this.endIndex = end
- })
- }
- .width('100%')
- .height('100%')
- .onTouch((event) => this.listTouchEvent(event)) // 父容器設(shè)置touch事件,當(dāng)列表無(wú)數(shù)據(jù)也可以下拉刷新。
- }
- ......
2、touch觸摸事件:
手指移動(dòng)上拉改變偏移量進(jìn)行判斷是否顯示布局;
手指抬起偏移量置為0,請(qǐng)求數(shù)據(jù)成功后,關(guān)閉刷新?tīng)顟B(tài)。
(部分關(guān)鍵代碼)
- ......
- // 按下的y坐標(biāo)
- private downY = 0
- listTouchEvent(event: TouchEvent){
- switch (event.type) {
- case TouchType.Down: // 手指按下
- // 記錄按下的y坐標(biāo)
- this.downY = event.touches[0].y
- break
- case TouchType.Move: // 手指移動(dòng)
- // 因?yàn)榧虞d更多是在列表后面新增一個(gè)item,當(dāng)一屏能夠展示全部列表,endIndex 為 length+1
- if (this.endIndex == this.list.length - 1 || this.endIndex == this.list.length) {
- // 滑動(dòng)的偏移量
- this.offsetY = event.touches[0].y - this.downY
- // 達(dá)到加載更多條件
- if (Math.abs(this.offsetY) > vp2px(this.loadMoreHeight)/2) {
- this.isCanLoadMore = true
- // 顯示布局
- this.isVisibleLoadMore = true
- // 偏移量緩慢增加
- this.offsetY = - vp2px(this.loadMoreHeight) + this.offsetY * 0.1
- }
- }
- }
- break
- case TouchType.Up: // 手指抬起
- case TouchType.Cancel: // 觸摸意外中斷:來(lái)電界面
- animateTo({
- duration: 200, // 動(dòng)畫(huà)時(shí)長(zhǎng)
- }, () => {
- // 偏移量設(shè)置為0
- this.offsetY = 0
- })
- if (this.isCanLoadMore) {
- console.info('======執(zhí)行加載更多========')
- // 加載中...
- this.isLoading = true
- // 模擬耗時(shí)操作
- setTimeout(() => {
- this.closeLoadMore()
- this.loadMoreData()
- }, 2000)
- } else {
- console.info('======關(guān)閉加載更多!未達(dá)到條件========')
- this.closeLoadMore()
- }
- break
- }
- }
- ......
結(jié)尾
每天進(jìn)步一點(diǎn)點(diǎn)、需要付出努力億點(diǎn)點(diǎn)。
完整代碼加了優(yōu)化,代碼量比較多,就不單獨(dú)貼出來(lái)了
https://gitee.com/liangdidi/ListPullRefreshLoadMoreDemo(需要登錄才能看到演示圖)
想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)