HarmonyOS 鸿蒙Next中用Swiper实现List优先滑动的实例

HarmonyOS 鸿蒙Next中用Swiper实现List优先滑动的实例 方便大家,使用直接复制过去就能使用.

一、配置route_map.json,增加下面内容

{
      "name": "SwiperListPage",
      "pageSourceFile": "src/main/ets/pages/SwiperListPage.ets",
      "buildFunction": "SwiperListPageBuilder",
      "data": {
        "description": "this is SwiperListPage"
      }
    }

二、在pages目录新建一个SwiperListPage.ets文件,文件内容如下 :

@Builder
export function SwiperListPageBuilder() {
  SwiperListPage();
}

// 标题数据类型
interface TestTitleBean {
  title: string;
  id: string;
}

// 测试数据类型
interface TDMzicEpsData {
  imageUrl?: string;
  title?: string;
}

@Entry
@Component
struct SwiperListPage {
  private titleListScroller: Scroller = new Scroller();
  private swiperController: SwiperController = new SwiperController();
  // 当前激活的索引(标题和Swiper共用)
  @State activeIndex: number = 0;
  // 标题数据
  private titleData: TestTitleBean[] = [];
  // 所有分组的测试数据(每个标题对应一组测试)
  private groupDatas: TDMzicEpsData[][] = [];

  aboutToAppear(): void {
    // 初始化标题数据
    this.titleData = [
      { title: '标题1', id: '1' },
      { title: '标题2', id: '2' },
      { title: '标题3', id: '3' },
      { title: '标题4', id: '4' },
      { title: '标题5', id: '5' }
    ];

    // 初始化每个分组的测试数据
    for (let i = 0; i < 5; i++) {
      const videos: TDMzicEpsData[] = [];
      for (let j = 0; j < 10; j++) {
        videos.push({
          imageUrl: `http://example.com/group${i}_video${j}.png`,
          title: `组${i + 1}-测试${j + 1}`
        });
      }
      this.groupDatas.push(videos);
    }
  }

  scrollToIndex(): void {
    if (this.titleListScroller) {
      this.titleListScroller.scrollToIndex(this.activeIndex, false, ScrollAlign.CENTER);
    }
  }

  // 标题视图
  @Builder
  titleView(item: TestTitleBean, index: number) {
    Text(item.title)
      .fontSize(16)
      .padding({ left: 12, right: 12, top: 8, bottom: 8 })
      .backgroundColor(this.activeIndex === index ? '#409eff' : '#f5f5f5')
      .fontColor(this.activeIndex === index ? '#fff' : '#333')
      .borderRadius(4)
      .onClick(() => {
        this.activeIndex = index;
        this.swiperController.changeIndex(index); // 点击标题,Swiper切换到对应页
      });
  }

  build() {
    Column() {
      List({ scroller: this.titleListScroller }) {
        ForEach(this.titleData, (item: TestTitleBean, index: number) => {
          ListItem() {
            this.titleView(item, index)
          }
        }, (item: TestTitleBean, index: number) => `${item.id}_${index}`)
      }
      .listDirection(Axis.Horizontal)
      .scrollBar(BarState.Off)
      .alignSelf(ItemAlign.Start)
      .width('100%')
      .height(50)
      .margin({ bottom: 10 })
      .onAppear(() => {
        this.scrollToIndex()
      })

      Swiper(this.swiperController) {
        ForEach(this.groupDatas, (datas: TDMzicEpsData[], _groupIndex: number) => {
          // 每个Swiper页面对应一个List,显示该分组的测试数据
          List({ space: 10 }) {
            ForEach(datas, (item: TDMzicEpsData, _index: number) => {
              ListItem() {
                Stack({ alignContent: Alignment.TopEnd }) {
                  Stack() {
                    Image('https://via.placeholder.com/100x50')
                      .width(100)
                      .height(50)
                      .borderRadius(4)

                    Text(item.title || '测试A')
                      .fontSize(15)
                      .fontWeight(FontWeight.Medium)
                      .maxLines(1)

                  }
                  .height('100%')
                  //.aspectRatio(1)


                }
                .width(100)
                .height(50)
                .backgroundColor(Color.Gray)
                .borderRadius(4)
                .margin({ bottom: 10 })
              }
            })
          }
          .width('100%')
          .height(80)
          .listDirection(Axis.Horizontal)
          .scrollBar(BarState.Off)
          .friction(1.5)
          .lanes(1)
          .padding(10)
          .edgeEffect(EdgeEffect.None)
          .nestedScroll({
            scrollForward: NestedScrollMode.SELF_FIRST,
            scrollBackward: NestedScrollMode.SELF_FIRST
          })
        })
      }
      .vertical(false)
      .indicator(false)
      .loop(false)
      .onChange((index: number) => {
        this.activeIndex = index;
        // 滑动Swiper,对应的标题居中
        this.titleListScroller.scrollToIndex(index, true, ScrollAlign.CENTER);
      })
      .index(this.activeIndex)

    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Start)
    .padding(16)
  }
}

上面的核心代码是:

.nestedScroll({

scrollForward: NestedScrollMode.SELF_FIRST,

scrollBackward: NestedScrollMode.SELF_FIRST

})

它是负责Swiper中的List优先滑动的,不然后会出现数据显示不完整的冲突.

三.在 Index.ets文件中随便找个元素,加入点击功能即可

  Text("点我用Swiper实现List优先滑动的实例 ")
    .onClick(() => {
        this.pathStack.pushPathByName('SwiperListPage', null);
     })

到此,你可以把它用在你的项目上了.


更多关于HarmonyOS 鸿蒙Next中用Swiper实现List优先滑动的实例的实战教程也可以访问 https://www.itying.com/category-93-b0.html

4 回复

学习了,

更多关于HarmonyOS 鸿蒙Next中用Swiper实现List优先滑动的实例的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS Next中,Swiper组件默认会拦截触摸事件。要实现List优先滑动,可以使用onTouch事件监听。在Swiper的onTouch事件中,通过事件对象的stopPropagation方法阻止事件冒泡,同时结合List组件的滚动状态进行判断。当List处于可滚动状态时,阻止Swiper的默认滑动行为,使触摸事件优先传递给List。具体实现需在Swiper组件上设置onTouch回调,并在回调中根据条件调用event.stopPropagation()

这是一个非常典型的在HarmonyOS Next中实现Swiper内嵌List且优先处理List滚动的解决方案。您提供的代码清晰地展示了如何通过nestedScroll属性来解决嵌套滚动冲突问题。

核心要点分析:

  1. 嵌套滚动配置是关键

    .nestedScroll({
      scrollForward: NestedScrollMode.SELF_FIRST,
      scrollBackward: NestedScrollMode.SELF_FIRST
    })
    

    这个配置确保了当用户在水平List上滑动时,List会优先消耗滚动事件,只有在List滚动到边界后,Swiper才会响应切换页面的操作。

  2. 双向联动机制

    • 标题List与Swiper通过activeIndex状态保持同步
    • 点击标题时,通过swiperController.changeIndex(index)切换Swiper页面
    • 滑动Swiper时,通过onChange回调更新标题位置并居中显示
  3. 滚动控制优化

    • 使用Scroller控制标题List的滚动位置
    • 设置friction(1.5)增加List滚动手感
    • edgeEffect(EdgeEffect.None)禁用边缘效果避免干扰

这种实现方式适用于需要横向分类浏览内容的场景,如商品分类浏览、视频分类等。代码结构清晰,组件职责明确,是一个很好的参考示例。

回到顶部