HarmonyOS 鸿蒙 ArkTs(5.0.1 13)实现Scroll下拉到顶刷新/上拉触底加载
效果展示
使用方法
ts
import LoadingText from "../components/LoadingText"
import PageToRefresh from "../components/PageToRefresh"
import FooterBar from "../components/FooterBar"
class PageToRefreshController {
onScrollTop = () => {}
}
@Entry
@Component
struct Index {
//滚动回顶部方法start
private PageToRefreshRef = new PageToRefreshController();
onScrollTop(){
this.PageToRefreshRef.onScrollTop()
}
//滚动回顶部方法end
onSearch() {
// 刷新数据
this.loading = true;
setTimeout(()=>{
this.loading = false;
this.simpleList = [1, 2, 3, 4, 5,6,7,8,9,10,11,12,13,14,15]
},1000)
}
onReachBottom(){
// 触底加载
if(!this.finish){
this.loading = true;
setTimeout(()=>{
this.loading = false;
this.finish = true;
this.simpleList = this.simpleList.concat([16,17,15,19,20,21,22,23,24,25,26,27,28,29,30])
},1000)
}
}
// 判断是否需要显示滚动到顶部的按钮(scroll滚动的位置)
@State scrollY:number = 0;
// 数据
@State simpleList: Array<number> = [1, 2, 3, 4, 5,6,7,8,9,10,11,12,13,14,15];
@State loading:boolean = false;
@State finish:boolean = false;
build() {
Stack({ }) {
Flex({ direction: FlexDirection.Column }) {
// 顶部
Column(){
// 自定义顶部的组件
Text('顶部标题').fontColor(0xffffff).fontSize(14)
}
.width('100%')
.padding(10)
.flexShrink(0)
.backgroundColor(0xFC5531)
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP])
// 内容
Stack(){
//调用组件
PageToRefresh({controller: this.PageToRefreshRef, scrollY: this.scrollY, refreshPull: () => {this.onSearch()}, reachBottom: () =>{this.onReachBottom()}}){
Column(){
Row(){
// 自定义内容
Text('这里可以定义滚动后固定在顶部的内容').fontColor(0xffffff).fontSize(14)
}.width('100%').height(50).backgroundColor(0x232020).justifyContent(FlexAlign.Center)
List() {
ForEach(this.simpleList, (item: number, index: number) => {
ListItem(){
Row(){
Text(item.toString()).fontColor(0xffffff).fontSize(24)
}.width('100%').height(80).backgroundColor(0x1BA035).margin({bottom: 10}).justifyContent(FlexAlign.Center)
}
}, (item: number) => item.toString())
ListItem(){
LoadingText({loading: this.loading, finish: this.finish, onPullData: () => {
//点击直接加载数据
this.onReachBottom()
}})
}
}
}.alignItems(HorizontalAlign.Start).justifyContent(FlexAlign.Start).constraintSize({minHeight: '100%'})
}
if(this.scrollY >= 50){
Row(){
Text('我已经固定在顶部啦').fontColor(0xffffff).fontSize(14)
}.position({x:0,y:0}).width('100%').height(50).backgroundColor(0x232020).justifyContent(FlexAlign.Center)
}
}.flexGrow(1).flexShrink(1)
//你自定义的底部tabbar组件(仅供示例)
FooterBar({
scrollTop: this.scrollY >= 500, //判断是否显示滚动到顶部
onGoTop: () => {
//点击滚动到顶部的方法
this.onScrollTop();
}
}).flexShrink(0)
}.width('100%').height('100%')
}
}
}
新建PageToRefresh 组件
ts
import LoadingText from "./LoadingText"
class PageToRefreshController {
onScrollTop = () => {}
}
@Component
export default struct PageToRefresh {
refreshPull?: () => void
reachBottom?: () => void
scroller: Scroller = new Scroller()
private controller: PageToRefreshController = new PageToRefreshController();
aboutToAppear() {
if (this.controller) {
this.controller.onScrollTop = this.onScrollTop;
}
}
private onScrollTop = () => {
//滚动回顶部
this.scroller.scrollTo({ xOffset: 0, yOffset: 0, animation: { duration: 1000, curve: Curve.Ease } })
}
@Link scrollY: number
private currentOffsetY: number = 0;
@State refreshStatus: boolean = false;
@State refreshText: string = '正在刷新';
@State pullUpText: string = '正在加载';
@State isRefreshing: boolean = false;
@State isCanLoadMore: boolean = false;
@State ArrData: string[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
@State newArr: string [] = ['10', '11']
putDownPullUpRefresh(event?: TouchEvent): void {
if (event === undefined) {
return;
}
switch (event.type) {
case TouchType.Down:
this.currentOffsetY = event.touches[0].y;
break;
case TouchType.Move:
let isDownPull = event.touches[0].y - this.currentOffsetY > 50;
if (isDownPull && this.isCanLoadMore === false && this.scrollY <= 20) {
this.refreshStatus = true;
}
if(this.scroller.isAtEnd()){
console.log('滚动到底部')
this.isCanLoadMore = true;
}
break;
case TouchType.Cancel:
break;
case TouchType.Up:
if (this.refreshStatus) {
console.log('下拉刷新')
this.refreshStatus = false;
if(this.refreshPull) this.refreshPull();
}
if (this.isCanLoadMore) {
console.log('上拉加载')
this.isCanLoadMore = false;
if(this.reachBottom) this.reachBottom();
}
break;
default:
break;
}
}
@Builder
putDown() {
Row() {
LoadingText({loading: true, finish: false, loadingText: this.refreshText})
}
.justifyContent(FlexAlign.Center)
.width('100%')
}
@Builder
PullUp() {
Row() {
LoadingText({loading: true, finish: false, loadingText: this.pullUpText})
}
.justifyContent(FlexAlign.Center)
.width('94%')
.height('5%')
}
@Builder
slot() {
Stack(){}.flexShrink(0)
};
@BuilderParam component: () => void = this.slot;
build() {
Column() {
Scroll(this.scroller) {
Column() {
if (this.refreshStatus) {
this.putDown()
}
this.component();
}
}
.width('100%')
.onWillScroll(() => {
this.scrollY = this.scroller.currentOffset().yOffset;
})
.onTouch((event?: TouchEvent) => {
this.putDownPullUpRefresh(event);
})
}.width('100%').height('100%').backgroundColor(0xf4f4f4)
}
}
加载文字
ts
@Component
export default struct LoadingText {
onPullData?: () => void
@Prop loadingText: string = "加载中...";
@Prop loading: boolean = false;
@Prop finishText: string = "- 我是有底线的 -";
@Prop finish: boolean = false;
build() {
Row() {
if(this.loading){
LoadingProgress().width(20).height(20).margin({ right: 10 }).color(0x999999)
Text(this.loadingText).fontSize(12).fontColor(0x999999).margin({left: 4})
}else if(this.finish){
Text(this.finishText).fontSize(12).fontColor(0x999999)
}else{
Text("轻轻上拉加载更多").fontSize(12).fontColor(0x999999).onClick(()=>{
if (this.onPullData) {
this.onPullData()
}
})
}
}.alignItems(VerticalAlign.Center).justifyContent(FlexAlign.Center).width('100%').height(24)
}
}