HarmonyOS PhotoPicker用Tabs后,默认定位到的视频 tab,切换到图片tab,然后点击预览按钮,观察tabs内容会发生跳变

HarmonyOS
2025-01-09 15:02:39
浏览
收藏 0
回答 1
待解决
回答 1
按赞同
/
按时间
Excelsior_abit

请把Tabs参数中的index直接用selectedIndex赋值,因为每次修改scrollable属性会导致Tabs走create流程刷新index值。

参考代码如下:

import {
  ClickType,
  ItemInfo,
  PhotoBrowserInfo,
  PhotoPickerComponent, PickerController, PickerOptions } from '@ohos.file.PhotoPickerComponent'
import { display } from '@kit.ArkUI'
import ComponentUtils from '@ohos.arkui.UIContext';
import { photoAccessHelper } from '@kit.MediaLibraryKit'

@Component
export struct TabsAlbum {

  @State selectedIndex: number = 1
  @State animationDuration: number = 250
  @State indicatorLeftMargin: number = 0
  @State indicatorWidth: number = 0
  @State scrollable: boolean = true
  private tabsWidth: number = 0
  pickerOptionsList: PickerOptions[] = []

  private componentUtils: ComponentUtils.ComponentUtils = this.getUIContext().getComponentUtils()

  private _pickerControllerList: PickerController[] = []
  tabsController: TabsController = new TabsController()
  private tabArray: number[] = [0,1]
  private tabTitles: string[] = ['视频', '图片']
  private displayInfo: display.Display | undefined = undefined
  private isFirstToAppear: boolean = false;

  //Lazy load controller:
  pickerController(index: number) {
    if (this._pickerControllerList.length > index) {
      return this._pickerControllerList[index]
    }
    const controller = new PickerController()
    this._pickerControllerList.push(controller)
    return controller
  }

  aboutToAppear(): void {
    this.displayInfo = display.getDefaultDisplaySync()
    this.isFirstToAppear = true
    this.createPickerOptions()
  }

  createPickerOptions(): void {
    this.createVideoPickerOptions()
    this.createImagePickerOptions()
  }

  createVideoPickerOptions(): void {
    let pickerOptions: PickerOptions = new PickerOptions()
    pickerOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.VIDEO_TYPE
    pickerOptions.maxSelectNumber = 35
    pickerOptions.isPhotoTakingSupported = false
    pickerOptions.isSearchSupported = false
    this.pickerOptionsList.push(pickerOptions)
  }

  createImagePickerOptions(): void {
    let pickerOptions: PickerOptions = new PickerOptions()
    pickerOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_VIDEO_TYPE
    pickerOptions.maxSelectNumber = 1
    pickerOptions.isPhotoTakingSupported = false
    pickerOptions.isSearchSupported = false
    this.pickerOptionsList.push(pickerOptions)
  }

  build() {
    Row() {
      this.createTabs() //如果需要展示 tab
    }
    .width('100%')
    .height('100%')
  }

  @Builder
  createTabs() {
    Stack({ alignContent: Alignment.TopStart }) {
      Tabs({barPosition: BarPosition.Start, index: this.selectedIndex, controller: this.tabsController}) {
        TabContent() {
          this.tabContentBuilder(0)
        }.tabBar(this.tabItemBuilder(0))
        TabContent() {
          this.tabContentBuilder(1)
        }.tabBar(this.tabItemBuilder(1))
      }
      .vertical(false)
      .width('100%')
      .height('100%')
      .barWidth('100%')
      .barHeight(40)
      .backgroundColor(Color.White)
      .animationDuration(this.animationDuration)
      .scrollable(this.scrollable)
      .onChange((index: number) => {
        this.selectedIndex = index
      })
      .onAreaChange((oldValue: Area, newValue: Area) => {
        let width = Number.parseFloat(newValue.width.toString())
        this.tabsWidth = Number.isNaN(width) ? 0 : width
      })
      .onAnimationStart((index: number, targetIndex: number, event: TabsAnimationEvent) => {
        // 切换动画开始时触发该回调。下划线跟着页面一起滑动,同时宽度渐变。
        this.selectedIndex = 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)
        console.log(' selectedIndex = '+ this.selectedIndex.toString())
        // this.selectedIndex = currentIndicatorInfo.index
        this.indicatorLeftMargin = currentIndicatorInfo.left
        this.indicatorWidth = currentIndicatorInfo.width
      })

      Column()
        .height(2)
        .width(this.indicatorWidth)
        .margin({ left: this.indicatorLeftMargin, top: 37 })
        .backgroundColor('#007DFF')
    }
  }

  private getCurrentIndicatorInfo(index: number, event: TabsAnimationEvent): Record<string, number> {
    let nextIndex = index
    if (index > 0 && event.currentOffset > 0) {
      nextIndex--
    } else if (index < this.tabArray.length - 1 && 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切换到下一页。
    if (swipeRatio > 0.5) {
      let x = swipeRatio
      x ++
    }
    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 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 startAnimateTo(duration: number, leftMargin: number, width: number) {
    animateTo({
      duration: duration, // 动画时长
      curve: Curve.Linear, // 动画曲线
      iterations: 1, // 播放次数
      playMode: PlayMode.Normal, // 动画模式
      onFinish: () => {
      }
    }, () => {
      this.indicatorLeftMargin = leftMargin
      this.indicatorWidth = width
    })
  }

  @Builder
  tabItemBuilder(index: number) {
    Text(this.tabTitles[index])
      .fontColor(this.selectedIndex === index ? Color.Blue : Color.Black)
      .fontSize(15)
      .textAlign(TextAlign.Center)
      .fontWeight(this.selectedIndex === index ? 500 : 400)
      .width('100%')
      .id(index.toString())
      .onAreaChange((oldValue: Area, newValue: Area) => {
        if (this.selectedIndex === index && (this.indicatorLeftMargin === 0 || this.indicatorWidth === 0)) {
          if (newValue.position.x != undefined) {
            let positionX = Number.parseFloat(newValue.position.x.toString())
            if (!this.isFirstToAppear) {
              animateTo({ duration: 500 }, () => {
                this.indicatorLeftMargin = Number.isNaN(positionX) ? 0 : positionX;
              })
            } else {
              this.indicatorLeftMargin = Number.isNaN(positionX) ? 0 : positionX;
            }

          }
          let width = Number.parseFloat(newValue.width.toString())
          if (!this.isFirstToAppear) {
            animateTo({ duration: 500 }, () => {
              this.indicatorWidth = Number.isNaN(width) ? 0 : width;
            })
          } else {
            this.indicatorWidth = Number.isNaN(width) ? 0 : width;
          }
        }
        this.isFirstToAppear = false;
      })
  }

  @Builder
  tabContentBuilder(index: number) {
    Stack() {
      PhotoPickerComponent({
        pickerOptions: this.pickerOptionsList[index],
        onItemClicked: (itemInfo: ItemInfo, clickType: ClickType): boolean => {
          return true
        },
        onSelect: (uri: string): void => {
        },
        onEnterPhotoBrowser: (photoBrowserInfo: PhotoBrowserInfo): boolean => {
          this.scrollable = false
          return true
        },
        onExitPhotoBrowser: (photoBrowserInfo: PhotoBrowserInfo): boolean => {
          this.scrollable = true
          return true
        },
        onDeselect: (uri: string): void => {
        },
        pickerController: this.pickerController(index),
      })
        .width('100%')
        .height('100%')
    }
  }
}
分享
微博
QQ
微信
回复
2025-01-09 18:41:21
相关问题
HarmonyOS Tabs组件Tab栏滚动问题
875浏览 • 1回复 待解决
请问一下tab按钮切换效果
1258浏览 • 1回复 待解决
HarmonyOS tab切换demo
317浏览 • 1回复 待解决
HarmonyOS Tab指定默认Index
222浏览 • 1回复 待解决
HarmonyOS Tabs默认高度问题
512浏览 • 1回复 待解决
HarmonyOS Tabs组件切换
666浏览 • 1回复 待解决
HarmonyOS 怎么配置tab默认选择tabBar
545浏览 • 1回复 待解决
HarmonyOS tabs切换问题
144浏览 • 1回复 待解决