HarmonyOS鸿蒙Next中左右滑动数据延迟Tabs+ForEach+TabContent解决方案

HarmonyOS鸿蒙Next中左右滑动数据延迟Tabs+ForEach+TabContent解决方案

一个页面有三个list列表,可以左右滑动,可以点击切换 目前我的页面主代码如下所示:

Tabs({
  barPosition: BarPosition.Start,
  controller: this.subController
}) {
  ForEach(this.mTitleList, (item: string, index: number) {
    TabContent() {
      Refresh({
        refreshing: $$this.isRefreshing,
        builder: CustomheaderRefreshlayout()
      }) {
        this.itemLayout()
      }.onStateChange((refreshStatus: RefreshStatus) {
        switch (refreshStatus) {
          case RefreshStatus.Inactive:
            break
          case RefreshStatus.Drag:
            break
          case RefreshStatus.OverDrag:
            break
          case RefreshStatus.Refresh:
            break
          case RefreshStatus.Done:
            break
        }
      }).onRefreshing(() {
        setTimeout(() {
          this.isRefreshing = false
        }, 2000)
        console.log('onRefreshing test')
      })
    }
    .tabBar(this.TabBuilder(item, index))
    .width('100%')
    .height('100%')
    .backgroundColor($r('app.color.project_main_bg_color'))
  })
}
.scrollable(true) //允许滑动
.width(CommonConstant.FULL_WIDTH)
.barHeight($r('app.float.default_46'))
.barMode(BarMode.Fixed) //Fixed 宽度平分, scrollable可滑动
.onChange((index: number) {
  this.currentIndex = index;
})

@ Builder
itemLayout() {
  Column() {
    Text(this.currentIndex == 0 ? '快讯' : this.currentIndex == 1 ? "提醒" : '消息')
      .fontColor(Color.Red)
      .fontSize($r('app.float.default_text_size_24'))
      .fontWeight(FontWeight.Bold)
  }.alignItems(HorizontalAlign.Center)
  .justifyContent(FlexAlign.Center)
  .width("100%")
  .height("100%")
}

滑动的时候,滑到当前页面有1秒仍然是显示上个页面的数据,比较滞后,有没有相关属性或方法设置,滑动后快速显示滑动后页面的内容?


更多关于HarmonyOS鸿蒙Next中左右滑动数据延迟Tabs+ForEach+TabContent解决方案的实战教程也可以访问 https://www.itying.com/category-93-b0.html

4 回复

itemLayout 里面内容跟currentIndex绑定了。界面加载完成后,三个界面是 ‘快讯 快讯 快讯’。滑动过程中,currentIndex没变。界面也没有变,就一直看到 ‘快讯’。onChange执行后,currentIndex就成1 三个界面 就成‘提醒 提醒 提醒’。感觉是保留了上个界面。其实是因为你三个界面都一样。

把itemLayout里的current干掉就好了

更多关于HarmonyOS鸿蒙Next中左右滑动数据延迟Tabs+ForEach+TabContent解决方案的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


您可以参考如下demo:

// xxx.ets
import ComponentUtils from '@ohos.arkui.UIContext';
import { router } from '@kit.ArkUI';
@Entry
@Component
struct TabsExample {
  @State currentIndex: number = 0
  @State animationDuration: number = 300
  @State indicatorLeftMargin: number = 0
  @State indicatorWidth: number = 0
  private tabsWidth: number = 0
  private componentUtils: ComponentUtils.ComponentUtils = this.getUIContext().getComponentUtils()
  @State isRefreshing: boolean = false
  private subController: TabsController = new TabsController(); //Tab 控制器
  @State mMessageType: number = 1
  @State private mTitleList: Array<string> = []
  aboutToAppear(): void {
    if (this.mMessageType == 1) {
      this.mTitleList.push('安顿快讯')
      this.mTitleList.push('疾病提醒')
      this.mTitleList.push('互动消息')
    } else if (this.mMessageType == 2) {
      this.mTitleList.push('紧急提示')
      this.mTitleList.push('疾病提醒')
      this.mTitleList.push('器官异常')
    }
  }
  @Builder
  TabBuilder(title: string, index: number) {
    Column() {
      Text(title)
        .width('100%')
        .textAlign(TextAlign.Center)
        .fontColor(index == this.currentIndex ? Color.Red : Color.Black)
        .fontSize(16)
        .fontWeight(index == this.currentIndex ? FontWeight.Bold : FontWeight.Normal)
        .id(index.toString())
        .onAreaChange((oldValue: Area, newValue: Area) => {
          if (this.currentIndex === index && (this.indicatorLeftMargin === 0 || this.indicatorWidth === 0)) {
            if (newValue.position.x != undefined) {
              let positionX = Number.parseFloat(newValue.position.x.toString())
              this.indicatorLeftMargin = Number.isNaN(positionX) ? 0 : positionX
            }
            let width = Number.parseFloat(newValue.width.toString())
            this.indicatorWidth = Number.isNaN(width) ? 0 : width
          }
        })
    }
    .alignItems(HorizontalAlign.Center)
    .justifyContent(FlexAlign.Center)
    .height(46)
  }
  @Builder CustomheaderRefreshlayout(){
    Column(){
      Text("这是下拉刷新头部")
        .width("100%")
        .height(80)
        .fontColor(Color.White)
        .textAlign(TextAlign.Center)
    }
    .backgroundColor(Color.Green)
    .width("100%")
    .height(80)
  }
  @Builder
  itemLayout() {
    Column(){
      Text(this.currentIndex==0?'安顿快讯':this.currentIndex==1?"疾病提醒":'互动消息')
        .fontColor(Color.Red)
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
    }
    .alignItems(HorizontalAlign.Center)
    .justifyContent(FlexAlign.Center)
    .width("100%")
    .height("100%")
  }
  build() {
    Column() {
      Stack({ alignContent: Alignment.TopEnd }) {
        Row(){
          Image($r('app.media.startIcon'))
            .objectFit(ImageFit.None).width(36).height(36)
            .onClick(()=> {
              router.back();
            })
          Text(this.mMessageType==1?'系统消息':this.mMessageType==2?'健康消息':this.mMessageType==3?'SOS求助':'咨询问诊')
            .fontColor(Color.White)
            .fontSize(16)
          Image($r('app.media.startIcon'))
            .objectFit(ImageFit.None).visibility(Visibility.Hidden).width(36).height(36)
        }
        .height('50vp')
        .width('100%')
        .backgroundColor(Color.Green)
        .justifyContent(FlexAlign.SpaceBetween)
        Row(){
          Text('一键已读')
            .fontSize(14)
            .fontColor(Color.White)
        }
        .alignItems(VerticalAlign.Center)
        .height('50vp')
        .margin({right:14})
      }
      Stack({ alignContent: Alignment.TopStart }) {
        Tabs({ barPosition: BarPosition.Start , controller:this.subController}) {
          ForEach(this.mTitleList, (item: string, index: number)=> {
            TabContent() {
              Refresh({ refreshing: $$this.isRefreshing,builder:this.CustomheaderRefreshlayout()}){
                this.itemLayout()
              }
              .onStateChange((refreshStatus: RefreshStatus) => {
                switch (refreshStatus){
                  case RefreshStatus.Inactive:
                    break
                  case RefreshStatus.Drag:
                    break
                  case RefreshStatus.OverDrag:
                    break
                  case RefreshStatus.Refresh:
                    break
                  case RefreshStatus.Done:
                    break
                }
              })
              .onRefreshing(() => {
                setTimeout(() => {
                  this.isRefreshing = false
                }, 2000)
                console.log('onRefreshing test')
              })
            }
            .tabBar(this.TabBuilder(item,index))
            .width('100%').height('100%')
            // .backgroundColor($r('app.color.project_main_bg_color'))
          })
        }
        .onAreaChange((oldValue: Area, newValue: Area) => {
          let width = Number.parseFloat(newValue.width.toString())
          this.tabsWidth = Number.isNaN(width) ? 0 : width
        })
        .barWidth('100%')
        // .barHeight(56)
        .width('100%')
        .backgroundColor('#F1F3F5')
        .animationDuration(this.animationDuration)
        .onChange((index: number) => {
          this.currentIndex = index // 监听索引index的变化,实现页签内容的切换。
        })
        .onAnimationStart((index: number, targetIndex: number, event: TabsAnimationEvent) => {
          // 切换动画开始时触发该回调。下划线跟着页面一起滑动,同时宽度渐变。
          this.currentIndex = targetIndex
          let targetIndexInfo = this.getTextInfo(targetIndex)
          this.startAnimateTo(this.animationDuration, targetIndexInfo.left, targetIndexInfo.width)
        })
        .onAnimationEnd((index: number, event: TabsAnimationEvent) => {
          // 切换动画结束时触发该回调。下划线动画停止。
          let currentIndicatorInfo = this.getCurrentIndicatorInfo(index, event)
          this.startAnimateTo(0, currentIndicatorInfo.left, currentIndicatorInfo.width)
        })
        .onGestureSwipe((index: number, event: TabsAnimationEvent) => {
          // 在页面跟手滑动过程中,逐帧触发该回调。
          let currentIndicatorInfo = this.getCurrentIndicatorInfo(index, event)
          this.currentIndex = currentIndicatorInfo.index
          this.indicatorLeftMargin = currentIndicatorInfo.left
          this.indicatorWidth = currentIndicatorInfo.width
        })
        Column()
          .height(2)
          .width(this.indicatorWidth)
          .margin({ left: this.indicatorLeftMargin, top: 48 })
        // .backgroundColor('#007DFF')
      }.width('100%')
    }.width('100%').height("100%")
  }
  private getTextInfo(index: number): Record<string, number> {
    let rectangle = this.componentUtils.getRectangleById(index.toString())
    return { 'left': px2vp(rectangle.windowOffset.x), 'width': px2vp(rectangle.size.width) }
  }
  private getCurrentIndicatorInfo(index: number, event: TabsAnimationEvent): Record<string, number> {
    let nextIndex = index
    if (index > 0 && event.currentOffset > 0) {
      nextIndex--
    } else if (index < 3 && event.currentOffset < 0) {
      nextIndex++
    }
    let indexInfo = this.getTextInfo(index)
    let nextIndexInfo = this.getTextInfo(nextIndex)
    let swipeRatio = Math.abs(event.currentOffset / this.tabsWidth)
    let currentIndex = swipeRatio > 0.5 ? nextIndex : index  // 页面滑动超过一半,tabBar切换到下一页。
    let currentLeft = indexInfo.left + (nextIndexInfo.left - indexInfo.left) * swipeRatio
    let currentWidth = indexInfo.width + (nextIndexInfo.width - indexInfo.width) * swipeRatio
    return { 'index': currentIndex, 'left': currentLeft, 'width': currentWidth }
  }
  private startAnimateTo(duration: number, leftMargin: number, width: number) {
    animateTo({
      duration: duration, // 动画时长
      curve: Curve.Linear, // 动画曲线
      iterations: 1, // 播放次数
      playMode: PlayMode.Normal, // 动画模式
      onFinish: () => {
        console.info('play end')
      }
    }, () => {
      this.indicatorLeftMargin = leftMargin
      this.indicatorWidth = width
    })
  }
}

参考链接:示例9自定义tabbar切换动画

在HarmonyOS鸿蒙Next中,使用Tabs+ForEach+TabContent实现左右滑动时,可能会遇到数据延迟的问题。这个问题通常是由于数据加载和UI渲染的异步性导致的。

要解决这个问题,可以采取以下方法:

  1. 优化数据加载:确保数据在滑动前已经加载完成,避免在滑动过程中进行数据加载操作。可以使用@State@Link等装饰器来管理数据状态,确保数据在UI渲染前已经准备好。

  2. 使用LazyForEach:如果数据量较大,可以使用LazyForEach代替ForEachLazyForEach会根据需要动态加载数据,减少初始化时的性能开销,从而改善滑动时的延迟问题。

  3. 预加载数据:在滑动到下一个Tab之前,提前加载下一个Tab的数据。可以通过监听滑动事件,在滑动到某个位置时触发数据预加载。

  4. 减少UI复杂度:简化每个TabContent的UI结构,减少不必要的组件嵌套和复杂的布局计算,以提高渲染性能。

  5. 使用@BuilderParam:通过@BuilderParam将TabContent的内容动态构建,减少不必要的UI更新操作。

  6. 合理使用@Watch:利用@Watch装饰器监听数据变化,确保数据更新时UI能够及时响应。

通过以上方法,可以有效减少HarmonyOS鸿蒙Next中左右滑动时的数据延迟问题,提升用户体验。

在HarmonyOS鸿蒙Next中,遇到左右滑动数据延迟的问题,可以通过以下步骤优化Tabs+ForEach+TabContent的组合:

  1. 减少渲染负载:确保ForEach内部组件尽可能轻量化,避免复杂计算或频繁更新。
  2. 预加载数据:在滑动前预加载相邻Tab的数据,减少切换时的延迟。
  3. 使用LazyForEach:如果数据量较大,考虑使用LazyForEach替代ForEach,按需加载组件。
  4. 优化布局:避免嵌套过多布局,减少布局计算时间。
  5. 异步加载:将数据加载和UI更新分离,使用异步任务处理数据加载。

通过以上优化,可以有效减少左右滑动时的数据延迟,提升用户体验。

回到顶部