HarmonyOS 鸿蒙Next swiper结合LazyForEach,数据刷新后,播放器被销毁

发布于 1周前 作者 gougou168 来自 鸿蒙OS

HarmonyOS 鸿蒙Next swiper结合LazyForEach,数据刷新后,播放器被销毁

用swiper实现沉浸式小视频,网络请求数据回来后,页面会闪烁。播放器被销毁重建了

咨询场景描述:从页面A带过来一条视频,B页面先启播视频,网络请求数据回来后 根据视频位置 前插数据、后插数据后调用notifyAddData。结果B页面刷新,播放器销毁重建了。导致页面闪烁~~~

例如:A页面带过来的视频在接口返回的数据位置是4,原来swiper中已经启播了视频, 数据来后,在视频数据前后插入返回的数据。然后swiperController.changeIndex(4,false)


更多关于HarmonyOS 鸿蒙Next swiper结合LazyForEach,数据刷新后,播放器被销毁的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html

2 回复

目前没有什么好方法,阻止这个组件的更新。一开始的时候,播放位置是在0,更新后,播放位置变为x,这时候正在展示的组件,不为我们需要的组件(之前的播放组件会删除),之后跳转的时候,组件会重新渲染,除非这个组件位置不变,不需要跳转到对应位置。

可以参考一下下面的demo:

Index.ets


import { ArticleDataSource } from './ArticleDataSource';

import { ListController } from './ListController';

import { TestDataBinding } from './TestDataBinding';

import { hilog } from '[@kit](/user/kit).PerformanceAnalysisKit';

[@Observed](/user/Observed)

export class DemoBean {

  title: string = ''

  bgColor: Color = Color.Red

  imgUrl: string = ''

  videoController: VideoController = new VideoController()

  isStart: boolean = false

  time: number = 0

  constructor(title: string, bgColor: Color, imgUrl: string) {

    this.title = title;

    this.bgColor = bgColor;

    this.imgUrl = imgUrl;

  }

}

[@Entry](/user/Entry)

[@Component](/user/Component)

struct Index {

  [@State](/user/State) message: string = 'Hello World';

  private swiperController: SwiperController = new SwiperController()

  channelItemsDataSource?: ArticleDataSource;

  controller: ListController = new ListController()

  [@State](/user/State) [@Watch](/user/Watch)('haha') test: TestDataBinding = new TestDataBinding()

  defaultItem: DemoBean | null = null

  private oldIndex: number = 2;

  aboutToAppear(): void {

    this.defaultItem = new DemoBean('3', Color.Red,

      'https://vd3.bdstatic.com/mda-kksevzw0s5ap0x6k/hd/cae_h264_nowatermark/1606445103/mda-kksevzw0s5ap0x6k.mp4')

    let one = new DemoBean('1', Color.Black, '')

    let strs: DemoBean[] = [this.defaultItem]

    this.channelItemsDataSource = new ArticleDataSource(strs);

    this.test = this.controller.test

    setTimeout(() => {

      this.controller.getData()

    }, 5000)

  }

  build() {

    Column() {

      Stack() {

        Swiper(this.swiperController) {

          LazyForEach(this.channelItemsDataSource, (item: DemoBean, index: number) => {

            Column() {

              Text(item?.title)

                .width('100%')

                .height(240)

                .backgroundColor(item.bgColor)

                .fontColor(Color.White)

              Video({ src: item.imgUrl, controller: item.videoController })

                .width('100%')

                .layoutWeight(1)

                .autoPlay(false)

                .onDetach(() => {

                  console.info('===onDetach', item?.title, JSON.stringify(item.imgUrl));

                })

                .onAttach(() => {

                  console.info('===onAttach', item?.title, JSON.stringify(item.imgUrl));

                })

                .onAppear(() => {

                  // console.info('===onAppear', item?.title)

                })

                .onDisAppear(() => {

                  // console.info('===onDisAppear', item?.title)

                })

                .onUpdate(e => {

                  item.isStart = true

                  item.time = e.time

                })

            }

          }, (item: DemoBean) => JSON.stringify(item.imgUrl))

        }

        .width('100%')

        .height('100%')

        .indicator(false)

        .loop(false)

        .vertical(true)

        .duration(250)

        .cachedCount(1)

        .curve(Curve.EaseInOut)

        .backgroundColor(Color.Yellow)

        .onChange((index: number) => {

          setTimeout(() => {

            let item = this.channelItemsDataSource?.getCurrentInfo(index);

            this.channelItemsDataSource?.getCurrentInfo(this.oldIndex)?.videoController.pause();

            if (item) {

              item.videoController.start();

              item.videoController.setCurrentTime(item.time);

            }

            this.oldIndex = index;

          }, 100)

        })

      }

      .width('100%')

      .height('80%')

      Button('刷新数据', { type: ButtonType.Capsule, stateEffect: true })

        .onClick(() => {

          // let targetIndex = -1

          // let strs: DemoBean[] =

          //   [

          //     new DemoBean('1', Color.Black,

          //     'https://vd3.bdstatic.com/mda-kfisbw0hbai441ay/v1-cae/hd/mda-kfisbw0hbai441ay.mp4'),

          //     new DemoBean('2', Color.Blue,

          //       'https://vd3.bdstatic.com/mda-kmnpgkbqabz9quvs/hd/mda-kmnpgkbqabz9quvs.mp4'),

          //     new DemoBean('3', Color.Red,

          //       'https://news-daily-cap-vms-img-pub.5club.cctv.cn/image/1005/process/bfd194d4bb8a4ea8a755c64e41317f43.jpg'),

          //     new DemoBean('4', Color.Orange,

          //       'https://vd3.bdstatic.com/mda-mmj4wbq0071a7fqr/hd/cae_h264/1639970939463612377/mda-mmj4wbq0071a7fqr.mp4'),

          //     new DemoBean('5', Color.Pink,

          //       'https://vd3.bdstatic.com/mda-mhg9x84sypsk91rb/hd/cae_h264_nowatermark/1629184590919483342/mda-mhg9x84sypsk91rb.mp4'),

          //     new DemoBean('6', Color.Brown,

          //       'https://vd3.bdstatic.com/mda-kikcz1rekji5ante/v2-hknm/hd/mda-kikcz1rekji5ante.mp4')

          //   ]

          // for (let index = 0; index < strs.length; index++) {

          //   if (this.defaultItem?.title === strs[index].title) {

          //     targetIndex = index

          //   }

          // }

          // this.channelItemsDataSource?.organizeCollectionDataSource(strs, targetIndex)

          // setTimeout(() => {

          //   console.log("===targetIndex: " + targetIndex);

          //   this.swiperController.changeIndex(targetIndex, false)

          //   let item = this.channelItemsDataSource?.getCurrentInfo(targetIndex)

          //   if (item) {

          //     item.videoController.setCurrentTime(item.time)

          //   }

          // }, 100)

          this.channelItemsDataSource?.addData(0, new DemoBean('1', Color.Black, 'https://vd3.bdstatic.com/mda-kfisbw0hbai441ay/v1-cae/hd/mda-kfisbw0hbai441ay.mp4'));

          this.swiperController.changeIndex(1, false)

          let item = this.channelItemsDataSource?.getCurrentInfo(1)

          if (item) {

            item.videoController.setCurrentTime(item.time)

          }

        }).margin(20)

    }

    .height('100%')

    .width('100%')

    .backgroundColor(Color.Grey)

  }

  haha() {

    hilog.info(1313, '', 'haha')

  }

}

ArticleDataSource.ets


import BasicDataSource from './BasicDataSource';

import { DemoBean } from './Index';

export class ArticleDataSource extends BasicDataSource<DemoBean> {

  constructor(channelItems: DemoBean[]) {

    super(channelItems)

  }

  public addData(index: number, data: DemoBean): void {

    this.mDataSource.splice(index, 0, data);

    this.notifyDataAdd(index);

  }

  public pushData(data: DemoBean): void {

    this.mDataSource.push(data);

    this.notifyDataAdd(this.mDataSource.length - 1);

  }

  public replaceData(index: number, data: DemoBean) {

    if (index >= 0 && index < this.totalCount()) {

      this.mDataSource[index] = data

      this.notifyDataChange(index)

    }

  }

  public insertDataToArrayHeader(data: DemoBean[] | undefined) {

    if (data === undefined) {

      return

    }

    console.log('wcg data: ' + data.length)

    this.mDataSource.unshift(...data)

    this.notifyDataReload()

  }

  public pushArrayData(data: DemoBean[]): void {

    if (data === undefined) {

      return

    }

    this.mDataSource.push(...data);

    this.notifyDataAdd(this.mDataSource.length - 1);

  }

  /**

   * 重组合集数据

   *

   * [@param](/user/param) data 合集数据

   * [@param](/user/param) index 从常规模式带入的article数据下标

   */

  public organizeCollectionDataSource(data: DemoBean[], index: number): void {

    console.info('===index', index)

    this.mDataSource.unshift(...data.slice(0, index))

    this.mDataSource.push(...data.slice(index + 1))

    // this.notifyDataReload();

    // this.notifyDataAdd(0);

    // this.notifyDataAdd(1);

    this.notifyDataAdd(3);

    // this.notifyDataAdd(4);

    // this.notifyDataAdd(5);

  }

  /** 获取当前的元素信息 */

  public getCurrentInfo(index: number): DemoBean{

    return this.mDataSource[index]

  }

}

BasicDataSource.ets


export default class BasicDataSource<T> implements IDataSource {

  private listeners: DataChangeListener[] = [];

  protected mDataSource: T[] = [];

  constructor(dataList: T[]) {

    this.mDataSource = dataList;

  }

  public totalCount(): number {

    return this.mDataSource.length

  }

  public find(predicate: (value: T, index: number, obj: T[]) => boolean): T | undefined {

    return this.mDataSource.find(predicate);

  }

  public findIndex(predicate: (value: T, index: number, obj: T[]) => boolean): number {

    return this.mDataSource.findIndex(predicate);

  }

  public getData(index: number): T | undefined {

    return index >= 0 && index < this.totalCount() ? this.mDataSource[index] : undefined;

  }

  public getHead(): T | undefined {

    return this.getData(0);

  }

  public getTail(): T | undefined {

    return this.getData(this.totalCount() - 1);

  }

  public remove(index: number): T | undefined {

    let ret = this.getData(index);

    if (ret !== undefined) {

      this.mDataSource.splice(index, 1);

      this.notifyDataDelete(index);

    }

    return ret;

  }

  public clean() {

    this.mDataSource = [];

    this.notifyDataReload();

  }

  public setData(...data: T[]): void {

    this.mDataSource = [];

    this.mDataSource.push(...data);

    this.notifyDataReload();

  }

  registerDataChangeListener(listener: DataChangeListener): void {

    if (this.listeners.indexOf(listener) < 0) {

      this.listeners.push(listener)

    }

  }

  unregisterDataChangeListener(listener: DataChangeListener): void {

    const pos = this.listeners.indexOf(listener);

    if (pos >= 0) {

      this.listeners.splice(pos, 1);

    }

  }

  notifyDataReload(): void {

    this.listeners.forEach(listener => {

      listener.onDataReloaded();

    })

  }

  notifyDataAdd(index: number): void {

    this.listeners.forEach(listener => {

      listener.onDataAdd(index)

    })

  }

  notifyDataChange(index: number): void {

    this.listeners.forEach(listener => {

      listener.onDataChange(index)

    })

  }

  notifyDataDelete(index: number): void {

    this.listeners.forEach(listener => {

      listener.onDataDelete(index)

    })

  }

  notifyDataMove(from: number, to: number): void {

    this.listeners.forEach(listener => {

      listener.onDataMove(from, to)

    })

  }

}

ListController.ets


import { TestDataBinding } from './TestDataBinding'

export class ListController {

  test: TestDataBinding = new TestDataBinding()

  async getData(): Promise<string> {

    this.test.title = '121213131'

    return Promise.resolve('1')

  }

}

TestDataBinding.ets


[@Observed](/user/Observed)

export class TestDataBinding {

  title: string = ''

}

更多关于HarmonyOS 鸿蒙Next swiper结合LazyForEach,数据刷新后,播放器被销毁的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS鸿蒙系统中,当使用Next swiper结合LazyForEach进行数据刷新时,如果播放器被销毁,通常是因为组件的重建导致了播放器的生命周期结束。解决这一问题的关键在于保持播放器的状态在数据刷新过程中不被重置。

一种可能的解决方法是利用全局或页面级别的状态管理,将播放器的实例保存在一个不会被数据刷新影响的区域。例如,可以使用页面的ViewModel(如果框架支持)或者定义一个全局的变量来存储播放器实例。

另外,可以通过监听swiper的刷新事件,在数据更新前后对播放器进行暂停和恢复操作,以避免数据刷新导致的播放器销毁问题。具体实现时,可以在数据刷新前暂停播放,刷新完成后再恢复播放,同时确保播放器实例不被重新创建。

此外,检查LazyForEach的使用方式是否正确,确保在数据更新时只是更新了数据源,而没有导致整个swiper组件的重新渲染。如果swiper组件被重新渲染,那么其中的播放器组件也会被重新创建。

如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html

回到顶部