网站首页 > 知识剖析 正文
下拉刷新和上拉滚动加载
开源项目地址:找不到了,有缘再见
核心代码分析:【做了一些改动,简化了一些实现】
import { Const, PageState, RefreshState } from "./Const";
@Component
export struct RefreshLayout {
build() {
Row() {
Image('xxxx')
.margin({
left: Const.RefreshLayout_TEXT_MARGIN_LEFT,
})
.width(30)
.height(30)
}
.padding({bottom: 4})
.clip(true)
.width(Const.FULL_WIDTH)
.justifyContent(FlexAlign.Center)
.alignItems(VerticalAlign.Bottom)
}
}
@Component
export struct LoadingLayout {
build() {
Row() {
Text('正在加载...')
.margin({
left: Const.RefreshLayout_TEXT_MARGIN_LEFT,
bottom: Const.RefreshLayout_TEXT_MARGIN_BOTTOM
})
.textAlign(TextAlign.Center)
}
.padding({top: 6, bottom: 6})
.clip(true)
.width(Const.FULL_WIDTH)
.justifyContent(FlexAlign.Center)
}
}
@Component
export struct NoMoreLayout {
build() {
Row() {
Text($r('app.string.prompt_message'))
.margin({ left: Const.NoMoreLayoutConstant_NORMAL_PADDING })
.fontSize(Const.NoMoreLayoutConstant_TITLE_FONT)
.textAlign(TextAlign.Center)
}
.width(Const.FULL_WIDTH)
.justifyContent(FlexAlign.Center)
.height(Const.CUSTOM_LAYOUT_HEIGHT)
}
}
@Component
export struct ListWrapComponent {
@Builder doNothingBuilder() {};
@BuilderParam mainContent : () => void = this.doNothingBuilder
refreshCallback: Function = () => {}
// 监听滚动加载
@Consume('isLoading') isLoading: boolean
// 记录手指摁下的y坐标,px单位
@State downY: number = 0
// 记录手指滑动过程中的最后的y坐标
@State lastMoveY: number = 0
// 记录List滚动条可见范围内的起始索引
@State startIndex: number = 0
// 记录List滚动条可见范围内的结束索引
@State endIndex: number = 0
// 记录手指滑动过程中的偏移量
@State offsetY: number = 0
// 触发下拉刷新的最小手指滑动距离
@State isRefreshing: boolean = false
// 控制是否执行滚动加载
@Prop hasMore: boolean = true
// 记录是下拉刷新还是滚动加载
@State isPullRefreshOperation: boolean = false
// 控制是否能执行滚动加载
@State isCanLoadMore: boolean = false
// 控制是否能执行下拉刷新
@State isCanRefresh: boolean = false
// 记录页面的状态
@State pageState: PageState = PageState.Success
// 触发下拉刷新的最小手指滑动距离
pullDownRefreshHeight: number = Const.CUSTOM_LAYOUT_HEIGHT;
// list 组件
@Builder ListLayout() {
Stack({alignContent: Alignment.Top}) {
// 下拉刷新显示的组件
RefreshLayout()
.height(px2vp(this.offsetY))
// 列表放三个项,记录滚动的索引
List() {
ListItem()
ListItem(){
// 内容通过builder全部放里面
this.mainContent()
}
// 滚动加载显示的组件
ListItem(){
if (this.hasMore) {
LoadingLayout()
} else {
NoMoreLayout()
}
}
}
.width(Const.FULL_WIDTH)
.height(Const.FULL_HEIGHT)
// Remove the rebound effect.
.edgeEffect(EdgeEffect.None)
.offset({ x: 0, y: px2vp(this.offsetY)})
.onScrollIndex((start: number, end: number) => {
// Listen to the first index of the current list.
this.startIndex = start;
this.endIndex = end;
})
.onReachEnd(() => {
this.isLoading = true
})
}
}
// 失败显示的组件
@Builder FailLayout() {
Image($r('app.media.none'))
.height(Const.FULL_HEIGHT)
.width(Const.FULL_WIDTH)
}
pullRefreshState(state: number) {
let that = this
switch (state) {
case RefreshState.DropDown:
that.isCanRefresh = false;
that.isRefreshing = false;
break;
case RefreshState.Release:
that.isCanRefresh = true;
that.isRefreshing = false;
break;
case RefreshState.Refreshing:
that.offsetY = vp2px(that.pullDownRefreshHeight);
that.isCanRefresh = true;
that.isRefreshing = true;
break;
case RefreshState.Success:
that.isCanRefresh = true;
that.isRefreshing = true;
break;
case RefreshState.Fail:
that.isCanRefresh = true;
that.isRefreshing = true;
break;
default:
break;
}
}
touchMoveLoadMore(event: TouchEvent) {
let that = this
if (that.isLoading) {
// if (that.endIndex === that.newsData.length - 1 || that.endIndex === that.newsData.length) {
that.offsetY = Math.min(event.touches[0].y - that.downY, vp2px(that.pullDownRefreshHeight));
if (Math.abs(that.offsetY) > vp2px(that.pullDownRefreshHeight)) {
that.isCanLoadMore = true;
that.offsetY = -vp2px(that.pullDownRefreshHeight) + that.offsetY * Const.Y_OFF_SET_COEFFICIENT;
}
}
}
touchMovePullRefresh(event: TouchEvent) {
let that = this
// 滚动条在list的第一项时,才执行下拉刷新
if (that.startIndex === 0) {
that.isPullRefreshOperation = true;
let height = vp2px(that.pullDownRefreshHeight);
// 计算下拉的距离
that.offsetY = Math.min(event.touches[0].y - that.downY, vp2px(that.pullDownRefreshHeight));
// 判断下拉记录大于配置的下拉距离时更新页面状态
if (that.offsetY >= height) {
this.pullRefreshState(RefreshState.Release);
// that.offsetY = height + that.offsetY * Const.Y_OFF_SET_COEFFICIENT;
} else {
this.pullRefreshState(RefreshState.DropDown);
}
if (that.offsetY < 0) {
that.offsetY = 0;
that.isPullRefreshOperation = false;
}
}
}
// 刷新结束时,动画
closeRefresh(isRefreshSuccess: boolean) {
let that = this;
if (that.isCanRefresh === true) {
this.pullRefreshState(isRefreshSuccess ? RefreshState.Success : RefreshState.Fail);
}
// 显示动画,动画效果相关的配置
animateTo({
duration: Const.RefreshConstant_CLOSE_PULL_DOWN_REFRESH_TIME,
onFinish: () => {
this.pullRefreshState(RefreshState.DropDown);
that.isPullRefreshOperation = false;
}
}, () => {
// offsetY 的改变执行会插入动画
that.offsetY = 0;
})
}
async touchUpPullRefresh() {
let that = this
if (that.isCanRefresh === true) {
that.offsetY = vp2px(that.pullDownRefreshHeight);
this.pullRefreshState(RefreshState.Refreshing);
await this.refreshCallback()
this.closeRefresh(true)
} else {
this.closeRefresh(false);
}
}
// 滚动加载
touchUpLoadMore() {
let that = this;
animateTo({
duration: Const.ANIMATION_DURATION,
// onFinish: () => {
// this.pullRefreshState(RefreshState.DropDown);
// that.isPullRefreshOperation = false;
// }
}, () => {
that.offsetY = 0;
})
if ((that.isCanLoadMore === true) && (that.hasMore === true)) {
// 执行异步操作,滚动加载调用后端接口
that.isLoading = true;
} else {
this.closeLoadMore();
}
}
closeLoadMore() {
let that = this
that.isCanLoadMore = false;
that.isLoading = false;
}
listTouchEvent(event: TouchEvent) {
let that = this
switch (event.type) {
case TouchType.Down:
that.downY = event.touches[0].y;
that.lastMoveY = event.touches[0].y;
break;
case TouchType.Move:
if ((that.isRefreshing === true) || (that.isLoading === true)) {
return;
}
let isDownPull = event.touches[0].y - that.lastMoveY > 0;
// 判断下拉还是下拉
if (((isDownPull === true) || (that.isPullRefreshOperation === true)) && (that.isCanLoadMore === false)) {
// Finger movement, processing pull-down refresh.
this.touchMovePullRefresh(event);
} else {
// Finger movement, processing load more.
this.touchMoveLoadMore(event);
}
that.lastMoveY = event.touches[0].y;
break;
case TouchType.Cancel:
break;
case TouchType.Up:
if ((that.isRefreshing === true) || (that.isLoading === true)) {
return;
}
if ((that.isPullRefreshOperation === true)) {
// Lift your finger and pull down to refresh.
this.touchUpPullRefresh();
} else {
// Fingers up, handle loading more.
this.touchUpLoadMore();
}
break;
default:
break;
}
}
build() {
Row() {
Column() {
if (this.pageState === PageState.Success) {
this.ListLayout()
} else if (this.pageState === PageState.Loading) {
LoadingLayout()
} else {
this.FailLayout()
}
}
.width(Const.FULL_WIDTH)
.height(Const.FULL_HEIGHT)
.justifyContent(FlexAlign.Center)
.onTouch((event: TouchEvent | undefined) => {
if (event) {
if (this.pageState === PageState.Success) {
this.listTouchEvent(event);
}
}
})
}
.height(Const.FULL_HEIGHT)
.width(Const.FULL_WIDTH)
}
}
- 上一篇: iOS UIView动画实践(一):揭开Animation的神秘面纱
- 下一篇: 动画篇--碎片动画
猜你喜欢
- 2025-05-11 刷爆全网的动态排序条形图,竟然被DeepSeek一秒生成!
- 2025-05-11 【动画】使用Qt实现标签动画的示例
- 2025-05-11 详细讲述iOS自定义转场
- 2025-05-11 Echarts仿电梯运行图
- 2025-05-11 重温乔布斯的感动,让你的iPhone后台切换程序样式变会ios 6时代
- 2025-05-11 猜猜微信拍一拍是怎么用Flutter实现的?
- 2025-05-11 HarmonyOS NEXT - 页签布局(Tabs)
- 2025-05-11 MFC转QT:Qt高级特性 - 动画框架
- 2025-05-11 Qt动画框架
- 2025-05-11 Flutter 中文文档:在 Flutter 应用里实现动画效果
- 最近发表
- 标签列表
-
- xml (46)
- css animation (57)
- array_slice (60)
- htmlspecialchars (54)
- position: absolute (54)
- datediff函数 (47)
- array_pop (49)
- jsmap (52)
- toggleclass (43)
- console.time (63)
- .sql (41)
- ahref (40)
- js json.parse (59)
- html复选框 (60)
- css 透明 (44)
- css 颜色 (47)
- php replace (41)
- css nth-child (48)
- min-height (40)
- xml schema (44)
- css 最后一个元素 (46)
- location.origin (44)
- table border (49)
- html tr (40)
- video controls (49)