1.第一步:安装

ohpm install @ohos/pulltorefresh@2.0.1

2.第二步:使用

import { PullToRefresh } from '@ohos/pulltorefresh'

// 需绑定列表或宫格组件
private scroller: Scroller = new Scroller();
  
PullToRefresh({
// 必传项,列表组件所绑定的数据
data: $data,
// 必传项,需绑定传入主体布局内的列表或宫格组件
scroller: this.scroller,
// 必传项,自定义主体布局,内部有列表或宫格组件
customList: () => {
  // 一个用@Builder修饰过的UI方法
  this.getListView();
},
// 可选项,下拉刷新回调
onRefresh: () => {
  return new Promise<string>((resolve, reject) => {
    // 模拟网络请求操作,请求网络2秒后得到数据,通知组件,变更列表数据
    setTimeout(() => {
      resolve('刷新成功');
      this.data = this.dataNumbers;
    }, 2000);
  });
},
// 可选项,上拉加载更多回调
onLoadMore: () => {
  return new Promise<string>((resolve, reject) => {
    // 模拟网络请求操作,请求网络2秒后得到数据,通知组件,变更列表数据
    setTimeout(() => {
      resolve('');
      this.data.push("增加的条目" + this.data.length);
    }, 2000);
  });
},
customLoad: null,
customRefresh: null,
})

3.完整实例代码

import { NavBar } from '../components/NavBar/NavBar'
import router from '@ohos.router';
import { Condition, ConditionList } from '../model/Condition';
import { PullToRefresh } from '@ohos/pulltorefresh'
import { ProductModel } from '../model/ProductModel';
import { getProductList } from '../common/home';
import { config } from '../common/config';
import { ReplacePath } from '../common/utils';
import promptAction from '@ohos.promptAction';

@Entry
@Component
struct ProductListPage {
    @State opacityValue: number = 100;
    @State selectIndex: number = 0;
    @State conditionListData: Condition [] = [
        new Condition("数据接口", false, [new ConditionList("Type-C", false)]),
        new Condition("指纹识别", false, [new ConditionList("侧边指纹", false), new ConditionList("屏下指纹", false)]),
        new Condition("电池容量", false, [new ConditionList("4600mAh", false), new ConditionList("4610mAh", false), new ConditionList("4500mAh", false), new ConditionList("4880mAh", false)]),
        new Condition("CPU主频", false, [new ConditionList("最高3.2GHz", false), new ConditionList("最高3.3GHz", false)]),
        new Condition("CPU型号", false, [new ConditionList("骁龙8+", false), new ConditionList("第三代骁龙8+", false)]),
        new Condition("存储容量", false, [new ConditionList("最高1024GB", false), new ConditionList("最高512GB", false)]),
    ]
    @State toggleIndex: number = 0;
    @State isSelect: boolean = false;
    @State priceSort: number = -1; //价格排序
    @State topAreaHeight: number = 0;
    @State filterAreaHeight: number = 0;
    @State productListData: ProductModel[] = [];
    @State cid: string = "";
    @State page: number = 1;
    @State isMore: boolean = true; //是否还有更多产品

    // 需绑定列表或宫格组件
    private scroller: Scroller = new Scroller();

    aboutToAppear() {
        this.cid = router.getParams()['cid'];
        this.getProductList(this.cid);
    }

    getProductList(cid: string, resolve?: (value: string | PromiseLike<string>) => void) {
        getProductList({ cid: cid, page: this.page }).then(res => {
            if (res.code === 200) {
                let tempData: ProductModel[] = res['result'];
                this.productListData = this.productListData.concat(tempData);
                this.isMore = tempData.length < 10 ? false : true; //每页十条
                if (this.isMore) {
                    this.page++;
                }
                if (resolve) {
                    resolve("")
                }
            } else {
                promptAction.showToast({
                    message: res.message,
                    bottom: 200
                })
            }

        }).catch((err) => {
            promptAction.showToast({
                message: err.message,
                bottom: 200
            })
        })
    }

    //搜索区域
    @Builder SearchArea() {
        Row() {
            Image($r("app.media.back_icon_black"))
                .width("42lpx")
                .height("42lpx")
                .margin({ right: '20lpx' })
                .onClick(() => {
                    router.back();
                })

            Search({
                placeholder: "手机",
            })
                .layoutWeight(1)
                .height("100lpx")
                .placeholderFont({ size: 12, weight: 400 })
                .textFont({ size: 12, weight: 400 })
            Text("搜索").fontSize('32lpx')
                .margin({ left: "30lpx" })

        }
        .justifyContent(FlexAlign.SpaceBetween)
        .width('100%')
        .height('120lpx')
        .padding({ left: '34lpx', right: '34lpx' })
        .backgroundColor("#fff")
        .onTouch(() => {
            this.toggleIndex = 0;
            this.isSelect = false;
        })
    }

    @Builder TopAreaWidget() {
        Column() {
            NavBar({
                title: "",
                isCustom: true,
                opacityValue: $opacityValue
            }) {
                this.SearchArea()
            }
        }.onAreaChange((oldValue: Area, newValue: Area) => {
            this.topAreaHeight = Number(newValue.height);
        })
    }

    //筛选区域
    @Builder FilterWidget() {
        Column() {
            Row() {
                Text("综合")
                    .layoutWeight(1)
                    .height('90lpx')
                    .fontSize("36lpx")
                    .textAlign(TextAlign.Center)
                    .fontWeight(this.selectIndex === 0 ? FontWeight.Bold : FontWeight.Normal)
                    .fontColor(this.selectIndex === 0 ? $r("app.color.theme_color") : '#000')
                    .onClick(() => {
                        this.selectIndex = 0;
                        this.priceSort = -1;
                    })

                Text("销量")
                    .layoutWeight(1)
                    .height('90lpx')
                    .fontSize("36lpx")
                    .textAlign(TextAlign.Center)
                    .fontWeight(this.selectIndex === 1 ? FontWeight.Bold : FontWeight.Normal)
                    .fontColor(this.selectIndex === 1 ? $r("app.color.theme_color") : '#000')
                    .onClick(() => {
                        this.selectIndex = 1;
                        this.priceSort = -1;
                    })

                Row() {
                    Text("价格")
                        .fontSize("36lpx")
                        .fontWeight(this.selectIndex === 2 ? FontWeight.Bold : FontWeight.Normal)
                        .fontColor(this.selectIndex === 2 ? $r("app.color.theme_color") : '#000')

                    Column() {
                        Image($r("app.media.up_arrow_svg"))
                            .width('18lpx')
                            .height('18lpx')
                            .fillColor(this.priceSort === 0 ? $r("app.color.theme_color") : "#ffc6c6c6")
                        Image($r("app.media.down_arrow_svg"))
                            .width('18lpx')
                            .height('18lpx')
                            .fillColor(this.priceSort === 1 ? $r("app.color.theme_color") : "#ffc6c6c6")
                    }
                    .justifyContent(FlexAlign.Center)
                    .alignItems(HorizontalAlign.Center)
                    .margin({
                        left: '8lpx'
                    })
                }
                .layoutWeight(1)
                .height('90lpx')
                .justifyContent(FlexAlign.Center)
                .onClick(() => {
                    this.selectIndex = 2;

                    if (this.priceSort === -1) {
                        this.priceSort = 0
                    } else if (this.priceSort === 0) {
                        this.priceSort = 1
                    } else if (this.priceSort === 1) {
                        this.priceSort = 0
                    }
                })

                Text("新品优先")
                    .layoutWeight(1)
                    .height('90lpx')
                    .fontSize("36lpx")
                    .textAlign(TextAlign.Center)
                    .fontWeight(this.selectIndex === 3 ? FontWeight.Bold : FontWeight.Normal)
                    .fontColor(this.selectIndex === 3 ? $r("app.color.theme_color") : '#000')
                    .onClick(() => {
                        this.selectIndex = 3;
                        this.priceSort = -1;
                    })

                Text("筛选")
                    .layoutWeight(1)
                    .height('90lpx')
                    .textAlign(TextAlign.Center)
                    .fontSize("36lpx")
                    .onClick(() => {

                    })
            }
            .width('100%')
            .height('90lpx')
            .justifyContent(FlexAlign.SpaceAround)
            .backgroundColor("#fff")
            .onTouch(() => {
                this.toggleIndex = 0;
                this.isSelect = false;
            })

            Scroll() {
                Row({ space: "30lpx" }) {
                    ForEach(this.conditionListData, (item: Condition, key) => {
                        Row() {
                            Text(item.name).fontSize('30lpx')
                            Image($r("app.media.down_arrow_svg"))
                                .width("18lpx")
                                .height('18lpx')
                                .fillColor("#ffc6c6c6")
                                .margin({ left: '8lpx', top: '8lpx' })
                        }
                        .height(this.toggleIndex === key && this.isSelect ? '76lpx' : '66lpx')
                        .backgroundColor("#f4f4f4")
                        .padding({ left: '16lpx', right: '16lpx', top: '18lpx' })
                        .margin({
                            bottom: this.toggleIndex === key && this.isSelect ? 0 : '10lpx'
                        })
                        .alignItems(VerticalAlign.Top)
                        .borderRadius({
                            topLeft: 2,
                            topRight: 2,
                            bottomLeft: this.toggleIndex === key && this.isSelect ? 0 : 2,
                            bottomRight: this.toggleIndex === key && this.isSelect ? 0 : 2
                        })
                        .onClick(() => {
                            item.checked = !item.checked;
                            this.isSelect = this.toggleIndex === key && this.isSelect ? false : true;
                            this.toggleIndex = key;
                        })

                    })
                }
                .height('90lpx')
                .justifyContent(FlexAlign.Start)
                .alignItems(VerticalAlign.Bottom)
                .padding({ left: '30lpx', right: '30lpx' })

            }
            .width("100%")
            .height('90lpx')
            .scrollable(ScrollDirection.Horizontal)
            .scrollBar(BarState.Off)
            .backgroundColor("#fff")
        }.onAreaChange((oldValue: Area, newValue: Area) => {
            this.filterAreaHeight = Number(newValue.height);
        })
    }

    //筛选下拉弹窗
    @Builder FilterPopupWidget() {
        if (this.isSelect) {
            Stack({ alignContent: Alignment.Top }) {
                Column() {
                }
                .width('100%')
                .height('100%')
                .backgroundColor("#000")
                .opacity(0.2)
                .offset({
                    y: '180lpx'
                })
                .onTouch(() => {
                    this.toggleIndex = 0;
                    this.isSelect = false;
                })

                FilterListDataView({ datas: this.conditionListData[this.toggleIndex], currentIndex: this.toggleIndex });
            }.height("100%")
        }
    }

    //帮你挑
    @Builder HelpYouChoose() {
        Column() {
            Row() {
                Column() {
                    Text("手机「帮你挑」")
                        .fontSize("34lpx")
                        .fontColor($r("app.color.theme_color"))
                        .textAlign(TextAlign.Start)
                        .width('100%')
                    Text("帮你轻松选手机")
                        .fontColor("#999")
                        .fontSize("30lpx")
                        .margin({ top: '6lpx' })
                        .textAlign(TextAlign.Start)
                        .width('100%')
                }
                .layoutWeight(1)
                .justifyContent(FlexAlign.Start)

                Row() {
                    Text("去看看")
                        .fontSize("28lpx")
                        .fontColor("#999")
                    Image($r("app.media.right_arrow_icon"))
                        .width("20lpx")
                        .height("20lpx")
                }
            }.width("100%")
            .justifyContent(FlexAlign.SpaceBetween)
            .margin({ bottom: '20lpx' })


            Grid() {
                GridItem() {
                    Column() {
                        Column() {
                            Image("https://cdn.cnbj1.fds.api.mi-img.com/mi-mall/5c57d3a5e8a2fde79bcffce9d5344c80.png?thumb=1&w=120&h=120&f=webp&q=100")
                                .width("100%")
                                .height('100%')
                        }
                        .width("200lpx")
                        .height('200lpx')
                        .backgroundColor("#f4f4f4")
                        .borderRadius(6)
                        .padding("30lpx")

                        Text("Xiaomi MIX系列")
                            .fontSize("28lpx")
                            .fontWeight(500)
                            .margin({ top: "20lpx" })
                    }
                }

                GridItem() {
                    Column() {
                        Column() {
                            Image("https://cdn.cnbj1.fds.api.mi-img.com/mi-mall/16cb4384a3d34bf7b480f3b9ce4f00a7.png?thumb=1&w=120&h=120&f=webp&q=100")
                                .width("100%")
                                .height('100%')
                        }
                        .width("200lpx")
                        .height('200lpx')
                        .backgroundColor("#f4f4f4")
                        .borderRadius(6)
                        .padding("30lpx")

                        Text("Xiaomi 数字旗舰")
                            .fontSize("28lpx")
                            .fontWeight(500)
                            .margin({ top: "20lpx" })
                    }
                }

                GridItem() {
                    Column() {
                        Column() {
                            Image("https://cdn.cnbj1.fds.api.mi-img.com/mi-mall/c29ce8888fee7bf46aa56cfdb5367b06.png?thumb=1&w=120&h=120&f=webp&q=100")
                                .width("100%")
                                .height('100%')
                        }
                        .width("200lpx")
                        .height('200lpx')
                        .backgroundColor("#f4f4f4")
                        .borderRadius(6)
                        .padding("30lpx")

                        Text("Redmi K系列")
                            .fontSize("28lpx")
                            .fontWeight(500)
                            .margin({ top: "20lpx" })
                    }
                }

                GridItem() {
                    Column() {
                        Column() {
                            Image("https://cdn.cnbj1.fds.api.mi-img.com/mi-mall/2d2df29f703cb991f0425ef37ac1aa96.png?thumb=1&w=120&h=120&f=webp&q=100")
                                .width("100%")
                                .height('100%')
                        }
                        .width("200lpx")
                        .height('200lpx')
                        .backgroundColor("#f4f4f4")
                        .borderRadius(6)
                        .padding("30lpx")

                        Text("Redmi Turbo系列")
                            .fontSize("28lpx")
                            .fontWeight(500)
                            .margin({ top: "20lpx" })
                    }
                }
            }.columnsTemplate('1fr 1fr 1fr 1fr')
            .rowsTemplate("1fr")
            .columnsGap("30lpx")
            .height('300lpx')

        }
        .backgroundColor("#fff")
        .borderRadius(8)
        .padding("20lpx")
        .margin({ left: "20lpx", right: "20lpx", top: "20lpx" })
    }

    //产品组件
    @Builder ProductWidget() {
        Scroll(this.scroller) {
            Column({ space: "20lpx" }) {
                this.HelpYouChoose();

                ForEach(this.productListData, (item: ProductModel, key) => {
                    Column() {
                        Row() {
                            Image(config.HOST_NAME + ReplacePath(item.pic))
                                .width("280lpx")
                                .height("280lpx")
                                .borderRadius(6)

                            Column() {
                                Text(item.title)
                                    .width('100%')
                                    .fontSize("38lpx")
                                    .fontWeight(FontWeight.Bold)
                                    .textAlign(TextAlign.Start)
                                Text(item.sub_title)
                                    .width('100%')
                                    .fontSize('32lpx')
                                    .textAlign(TextAlign.Start)
                                    .fontColor("#999")
                                    .margin({
                                        top: "16lpx",
                                        bottom: "16lpx"
                                    })
                                    .textOverflow({
                                        overflow: TextOverflow.Ellipsis,
                                    })
                                    .maxLines(2)


                                Row() {
                                    Column() {
                                        Text("CPU")
                                            .fontSize("26lpx")
                                            .fontColor("#444")
                                            .fontWeight(500)
                                        Text("Helio G25")
                                            .fontSize("24lpx")
                                            .fontColor("#888")
                                            .margin({
                                                top: "10lpx"
                                            })
                                    }

                                    Divider()
                                        .vertical(true)
                                        .height('50lpx')
                                        .strokeWidth(1)
                                        .color("#ffeeeeee")
                                        .margin({ left: "10lpx", right: "10lpx" })

                                    Column() {
                                        Text("高清拍摄")
                                            .fontSize("26lpx")
                                            .fontColor("#444")
                                            .fontWeight(500)
                                        Text("2000w像素")
                                            .fontSize("24lpx")
                                            .fontColor("#888")
                                            .margin({
                                                top: "10lpx"
                                            })
                                    }

                                    Divider()
                                        .vertical(true)
                                        .height('50lpx')
                                        .strokeWidth(1)
                                        .color("#ffeeeeee")
                                        .margin({ left: "10lpx", right: "10lpx" })

                                    Column() {
                                        Text("超大屏")
                                            .fontSize("26lpx")
                                            .fontColor("#444")
                                            .fontWeight(500)
                                        Text("6.7寸")
                                            .fontSize("24lpx")
                                            .fontColor("#888")
                                            .margin({
                                                top: "10lpx"
                                            })
                                    }
                                }.width("100%")
                                .justifyContent(FlexAlign.SpaceAround)

                                Row() {
                                    Text("¥").fontSize("26lpx").fontWeight(FontWeight.Bold)
                                    Text(`${item.price}`).fontSize("38lpx").fontWeight(FontWeight.Bold)
                                    Text("起").fontSize("26lpx").fontWeight(FontWeight.Bold)
                                }
                                .width("100%")
                                .justifyContent(FlexAlign.Start)
                                .alignItems(VerticalAlign.Bottom)
                                .margin({ top: "20lpx" })

                                Row() {
                                    Text("0条评价").fontSize("28lpx")
                                        .fontColor("#999")
                                        .margin({ right: "30lpx" })
                                    Text("99.3%满意").fontSize("28lpx")
                                        .fontColor("#999")
                                }.width("100%")
                                .justifyContent(FlexAlign.Start)
                                .margin({ top: "20lpx" })

                            }
                            .layoutWeight(1)
                            .justifyContent(FlexAlign.Start)
                            .padding({ left: "20lpx" })
                        }
                        .width("100%")
                        .backgroundColor("#fff")
                        .borderRadius(8)
                        .padding("30lpx")
                        .alignItems(VerticalAlign.Top)

                    }.padding({
                        left: "20lpx",
                        right: "20lpx"
                    })
                }, item => item)

                if (!this.isMore) {
                    Text("—————我也是有底线的—————")
                        .width("100%")
                        .margin({
                            top: "20lpx",
                            bottom: "20lpx"
                        })
                        .textAlign(TextAlign.Center)
                        .fontSize("32lpx")
                        .fontColor("#999")
                }

            }.width("100%")
            .padding({ bottom: "20lpx" })
        }

    }

    build() {
        Stack({ alignContent: Alignment.Top }) {

            //商品内容
            Column() {

                PullToRefresh({
                    // 必传项,列表组件所绑定的数据
                    data: $productListData,
                    // 必传项,需绑定传入主体布局内的列表或宫格组件
                    scroller: this.scroller,
                    // 必传项,自定义主体布局,内部有列表或宫格组件
                    customList: () => {
                        // 一个用@Builder修饰过的UI方法
                        //商品列表
                        this.ProductWidget();
                    },
                    // 可选项,下拉刷新回调
                    onRefresh: () => {
                        return new Promise<string>((resolve, reject) => {
                            // 模拟网络请求操作,请求网络2秒后得到数据,通知组件,变更列表数据
                            this.page = 0;
                            this.productListData = [];
                            this.getProductList(this.cid, resolve)
                        });
                    },
                    // 可选项,上拉加载更多回调
                    onLoadMore: () => {
                        return new Promise<string>((resolve, reject) => {
                            // 模拟网络请求操作,请求网络2秒后得到数据,通知组件,变更列表数据
                            this.getProductList(this.cid, resolve)
                        });
                    },
                    customLoad: null,
                    customRefresh: null,
                })


                // 足迹按钮
                Stack({ alignContent: Alignment.BottomEnd }) {
                    RelativeContainer() {
                        Button() {
                            Image($r("app.media.zuji_svg"))
                                .width("54lpx")
                                .height("54lpx")
                                .fillColor("#666")
                        }
                        .alignRules({
                            right: {
                                anchor: "__container__",
                                align: HorizontalAlign.End
                            },
                            bottom: {
                                anchor: "__container__",
                                align: VerticalAlign.Bottom
                            }
                        })
                        .width("100lpx")
                        .height('100lpx')
                        .backgroundColor("#fff")
                        .shadow({
                            radius: 20,
                            color: "rgba(0,0,0,0.2)"
                        })
                        .id("btn1")
                    }
                    .width("100lpx")
                    .height("100lpx")
                    .offset({
                        x: -15,
                        y: -120
                    })
                }.width("100%")

            }
            .width('100%')
            .height('100%')
            .backgroundColor("#f4f4f4")
            .padding({ top: this.topAreaHeight + this.filterAreaHeight })

            //顶部内容
            Column() {
                this.TopAreaWidget();
                //筛选条件组件
                this.FilterWidget();
                this.FilterPopupWidget();
            }

        }
    }
}

//筛选条件列表组件
@Component
struct FilterListDataView {
    @ObjectLink datas: Condition
    @Prop currentIndex: number

    build() {
        Column() {
            List() {
                ForEach(this.datas.list, (item: ConditionList, key) => {
                    ListItem() {
                        Row() {
                            Text(item.name)
                                .fontSize("34lpx")
                                .fontColor(item.checked ? $r("app.color.theme_color") : "#000")
                            Image($r("app.media.select_svg"))
                                .width("44lpx")
                                .height("44lpx")
                                .fillColor($r("app.color.theme_color"))
                                .visibility(item.checked ? Visibility.Visible : Visibility.Hidden)
                        }.width('100%')
                        .justifyContent(FlexAlign.SpaceBetween)
                        .padding({ left: "30lpx", right: "30lpx", top: "30lpx", bottom: "30lpx" })
                        .onClick(() => {
                            this.datas.list[key].checked = !this.datas.list[key].checked;
                            this.datas.list = [...this.datas.list]
                        })
                    }

                })
            }
            .divider({
                strokeWidth: 1,
                startMargin: 10,
                endMargin: 10
            })

            Row({ space: "30lpx" }) {
                Button("重置")
                    .layoutWeight(1)
                    .height('100lpx')
                    .fontSize("34lpx")
                    .onClick(() => {
                        for (let i = 0; i < this.datas.list.length; i++) {
                            this.datas.list[i].checked = false
                        }
                        this.datas.list = [...this.datas.list];
                    })
                Button("确定")
                    .layoutWeight(1)
                    .height('100lpx')
                    .fontSize("34lpx")
                    .fontColor(Color.White)
                    .backgroundColor($r("app.color.theme_color"))
                    .onClick(() => {
                        console.log(this.currentIndex + '')
                    })
            }
            .width("100%")
            .padding({ left: "30lpx", right: "30lpx", top: "30lpx", bottom: "30lpx" })
            .justifyContent(FlexAlign.SpaceBetween)
        }
        .width("100%")
        .backgroundColor("#f4f4f4")

    }
}

4.安装之后编译报错

> hvigor ERROR: Failed :entry:default@MergeProfile... 
> hvigor ERROR: The compatibleSdkVersion 9 cannot be smaller than version 10 declared in library [:library] 
          as the library might be using APIS not available in 9

5.解决方式
修改oh-package.json5文件内容:
修改之前:

{
  "license": "",
  "devDependencies": {
    "@ohos/hypium": "1.0.6"
  },
  "author": "",
  "name": "xiaomi",
  "description": "Please describe the basic information.",
  "main": "",
  "version": "1.0.0",
  "dependencies": {
    "@ohos/pulltorefresh": "^2.0.1"
  }
}

修改之后:(就是去掉@ohos/pulltorefresh 版本前面的^f符号)

{
  "license": "",
  "devDependencies": {
    "@ohos/hypium": "1.0.6"
  },
  "author": "",
  "name": "xiaomi",
  "description": "Please describe the basic information.",
  "main": "",
  "version": "1.0.0",
  "dependencies": {
    "@ohos/pulltorefresh": "2.0.1"
  }
}

5.点击Sync Now,编译成功

CompatibleSDK_ci


CompatibleSDK_harmonyos_02