赞
踩
使用ArkTS语言开发下拉菜单,需要考虑一下内容:
1、List的edgeEffect、LazyForEach
2、opacity属性可以设置整个组件的透明度
3、容器的.markAnchor可以和offset搭配使用
半成品代码实例、仿京东首页差一部分内容
import display from '@ohos.display'; import media from '@ohos.multimedia.media'; import { MyDataSource } from '../widget/list/datasource'; @Entry @Component struct TBListPage { @State firstListData : string[] = ['item value: 0', 'item value: 1', 'item value: 2', 'item value: 3', 'item value: 4', 'item value: 5'] private data: MyDataSource = new MyDataSource(this.firstListData) private scrollerForScroll: Scroller = new Scroller() private scrollerForList: Scroller = new Scroller() @State offsetY : number = 0 @State startIndex : number = 0 @State endIndex : number = 0 // 下拉刷新的布局高度 private pullRefreshHeight = 70 // 下拉刷新文字:下拉刷新、松开刷新、正在刷新、刷新成功 @State pullRefreshText: string= '下拉刷新' // 是否可以刷新:未达到刷新条件,收缩回去 private isCanRefresh = false // 是否正在刷新:刷新中不进入触摸逻辑 private isRefreshing: boolean = false // 是否已经进入了下拉刷新操作 private isPullRefreshOperation = false private lastMoveY = 0 // 上拉加载的布局默认高度 private loadMoreHeight = 70 // 上拉加载的布局是否显示 @State isVisibleLoadMore: boolean = false // 是否可以加载更多 private isCanLoadMore = false // 是否加载中:加载中不进入触摸逻辑 private isLoading: boolean = false private downY = 0 private showAd : boolean = false; private topbarHeight : number = 30 private adHeight : number = 60 build() { Stack(){ Column(){ Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { Image($r("app.media.flo")) .width(18) .height(18) Text(this.pullRefreshText) .margin({ left: 7, bottom: 1 }) .fontSize(17) } .backgroundColor(Color.Red) .width('100%') .height(vp2px(this.adHeight)) .offset({ x: 0, y: `${this.offsetY}px` }) //列表 List({scroller : this.scrollerForList}){ LazyForEach(this.data, (item: string) => { ListItem() { Row() { Text(`${item}`) .width('100%') .height(100) .fontSize(20) .margin({top : 10}) .fontColor(Color.White) .textAlign(TextAlign.Center) .borderRadius(10) .backgroundColor(0x007DFF) } } }, item => item) } .edgeEffect(EdgeEffect.None) .onScrollIndex((start, end) => { this.startIndex = start this.endIndex = end }) .offset({ x: 0, y: `${this.offsetY}px` }) .onReachEnd(()=>{ if (this.data.totalCount() < 15) { this.data.pushData('item value: ' + this.data.totalCount()) this.data.pushData('item value: ' + this.data.totalCount()) this.data.pushData('item value: ' + this.data.totalCount()) this.data.pushData('item value: ' + this.data.totalCount()) this.data.pushData('item value: ' + this.data.totalCount()) } }).onTouch((event) => this.listTouchEvent(event)) .height('100%') .width('100%') }.markAnchor({x:0, y:`${vp2px(this.adHeight + this.topbarHeight)}px`}) Column(){ Text('首页') .fontColor(Color.White) .fontSize(24) .margin(6) TextInput() .backgroundColor(Color.White) }.opacity(this.getTopBarOpacity()) .backgroundColor(Color.Blue) .padding({left: 10, right: 10}) .width('100%') .height(vp2px(this.topbarHeight)) } .height('100%') .alignContent(Alignment.TopStart) } getTopBarOpacity() : number{ if (this.offsetY == 0) { return 1; } if (this.offsetY > 100) { return 0 } return ((100 - this.offsetY) * 0.01) } listTouchEvent(event: TouchEvent){ switch (event.type) { case TouchType.Down: // 手指按下 // 记录按下的y坐标 this.downY = event.touches[0].y this.lastMoveY = event.touches[0].y break case TouchType.Move: // 手指移动 // 下拉刷新中 或 加载更多中,不进入处理逻辑 if(this.isRefreshing || this.isLoading){ console.info('========Move刷新中,返回=========') return } // 判断手势 let isDownPull = event.touches[0].y - this.lastMoveY > 0 // 下拉手势 或 已经进入了下拉刷新操作 if ((isDownPull || this.isPullRefreshOperation) && !this.isCanLoadMore) { this.touchMovePullRefresh(event) } else { this.touchMoveLoadMore(event) } this.lastMoveY = event.touches[0].y break case TouchType.Up: // 手指抬起 case TouchType.Cancel: // 触摸意外中断:来电界面 // 刷新中 或 加载更多中,不进入处理逻辑 if(this.isRefreshing || this.isLoading){ console.info('========Up刷新中,返回=========') return } if (this.isPullRefreshOperation) { this.touchUpPullRefresh() } else { this.touchUpLoadMore() } break } } //============================================加载更多================================================== // 手指移动,处理加载更多 touchMoveLoadMore(event:TouchEvent) { // 因为加载更多是在列表后面新增一个item,当一屏能够展示全部列表,endIndex 为 length+1 if (this.endIndex == this.data.totalCount() - 1 || this.endIndex == this.data.totalCount()) { // 滑动的偏移量 this.offsetY = event.touches[0].y - this.downY if (Math.abs(this.offsetY) > vp2px(this.loadMoreHeight)/2) { // 可以刷新了 this.isCanLoadMore = true // 显示加载更多布局 this.isVisibleLoadMore = true // 偏移量缓慢增加 this.offsetY = - vp2px(this.loadMoreHeight) + this.offsetY * 0.1 } } } // 手指抬起,处理加载更多 touchUpLoadMore() { animateTo({ duration: 200, // 动画时长 }, () => { // 偏移量设置为0 this.offsetY = 0 }) if (this.isCanLoadMore) { console.info('======执行加载更多========') // 加载中... this.isLoading = true // 模拟耗时操作 setTimeout(() => { this.closeLoadMore() this.loadMoreData() }, 2000) } else { console.info('======关闭加载更多!未达到条件========') this.closeLoadMore() } } // 关闭加载更多 closeLoadMore() { this.isCanLoadMore = false this.isLoading = false this.isVisibleLoadMore = false } // 手指抬起,处理下拉刷新 touchUpPullRefresh(){ // 是否可以刷新 if (this.isCanRefresh) { console.info('======执行下拉刷新========') // 偏移量为下拉刷新布局高度 this.offsetY = vp2px(this.pullRefreshHeight) // 状态2:正在刷新 this.pullRefreshState(2) // 模拟耗时操作 setTimeout(() => { this.refreshData() this.closeRefresh() }, 2000) } else { console.info('======关闭下拉刷新!未达到条件========') // 关闭刷新 this.closeRefresh() } } // 刷新测试数据 private refreshData(){ // this.list = [] // for (var i = 0; i < 10; i++) { // this.list.push(i) // } } // 加载更多测试数据 private loadMoreData(){ // let initValue = this.list[this.list.length-1] + 1 // for (var i = initValue; i < initValue + 10; i++) { // this.list.push(i) // } } // 关闭刷新 closeRefresh() { // 如果允许刷新,延迟进入,为了显示刷新中 setTimeout(() => { var delay = 50 if (this.isCanRefresh) { // 状态3:刷新成功 this.pullRefreshState(3) // 为了显示刷新成功,延迟执行收缩动画 delay = 500 } animateTo({ duration: 150, // 动画时长 delay: delay, // 延迟时长 onFinish: () => { // 状态0:下拉刷新 this.pullRefreshState(0) this.isPullRefreshOperation = false } }, () => { this.offsetY = 0 }) }, this.isCanRefresh ? 500 : 0) } // 手指移动,处理下拉刷新 touchMovePullRefresh(event:TouchEvent){ // 当首部索引位于0 if (this.startIndex == 0) { this.isPullRefreshOperation = true // 下拉刷新布局高度 var height = vp2px(this.pullRefreshHeight) // 滑动的偏移量 this.offsetY = event.touches[0].y - this.downY // 偏移量大于下拉刷新布局高度,达到刷新条件 if (this.offsetY >= height) { // 状态1:松开刷新 this.pullRefreshState(1) // 偏移量的值缓慢增加 this.offsetY = height + this.offsetY * 0.15 } else { // 状态0:下拉刷新 this.pullRefreshState(0) } if (this.offsetY < 0) { this.offsetY = 0 this.isPullRefreshOperation = false } } } // 下拉刷新状态 // 0下拉刷新、1松开刷新、2正在刷新、3刷新成功 pullRefreshState(state:number){ switch (state) { case 0: // 初始状态 this.pullRefreshText = '下拉刷新' this.isCanRefresh = false this.isRefreshing = false this.showAd = false break; case 1: this.pullRefreshText = '松开刷新' this.isCanRefresh = true this.isRefreshing = false break; case 2: this.offsetY = vp2px(this.pullRefreshHeight) this.pullRefreshText = '正在刷新' this.isCanRefresh = true this.isRefreshing = true break; case 3: this.pullRefreshText = '刷新成功' this.isCanRefresh = true this.isRefreshing = true break; case 4: this.offsetY = vp2px(this.adHeight) this.pullRefreshText = '展示广告' this.isCanRefresh = false this.isRefreshing = false this.showAd = true break; } } }
项目迟点提交。
参考来源:
https://gitee.com/liangdidi/ListPullRefreshLoadMoreDemo/blob/master/entry/src/main/ets/default/pages/index.ets
https://developer.harmonyos.com/cn/docs/documentation/doc-references-V3/ts-universal-attributes-location-0000001427584824-V3
鸿蒙有中文的开发文档,对于初次使用ArkTS的开发者来说很友好。有过Flutter开发经验的会较快上手。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。