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

HarmonyOS
2025-01-09 15:02:39
537浏览
收藏 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%')
    }
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 139.
  • 140.
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.
  • 147.
  • 148.
  • 149.
  • 150.
  • 151.
  • 152.
  • 153.
  • 154.
  • 155.
  • 156.
  • 157.
  • 158.
  • 159.
  • 160.
  • 161.
  • 162.
  • 163.
  • 164.
  • 165.
  • 166.
  • 167.
  • 168.
  • 169.
  • 170.
  • 171.
  • 172.
  • 173.
  • 174.
  • 175.
  • 176.
  • 177.
  • 178.
  • 179.
  • 180.
  • 181.
  • 182.
  • 183.
  • 184.
  • 185.
  • 186.
  • 187.
  • 188.
  • 189.
  • 190.
  • 191.
  • 192.
  • 193.
  • 194.
  • 195.
  • 196.
  • 197.
  • 198.
  • 199.
  • 200.
  • 201.
  • 202.
  • 203.
  • 204.
  • 205.
  • 206.
  • 207.
  • 208.
  • 209.
  • 210.
  • 211.
  • 212.
  • 213.
  • 214.
  • 215.
  • 216.
  • 217.
  • 218.
  • 219.
  • 220.
  • 221.
  • 222.
  • 223.
  • 224.
  • 225.
  • 226.
  • 227.
  • 228.
  • 229.
  • 230.
  • 231.
分享
微博
QQ
微信
回复
2025-01-09 18:41:21


相关问题
HarmonyOS Tabs组件Tab栏滚动问题
1511浏览 • 1回复 待解决
HarmonyOS tab切换demo
629浏览 • 1回复 待解决
请问一下tab按钮切换效果
1641浏览 • 1回复 待解决
HarmonyOS Tab指定默认Index
594浏览 • 1回复 待解决
HarmonyOS Tabs组件切换
1079浏览 • 1回复 待解决
HarmonyOS Tabs默认高度问题
939浏览 • 1回复 待解决
Tab控件切换问题有哪些?
1164浏览 • 1回复 待解决
HarmonyOS tabs切换问题
447浏览 • 1回复 待解决
HarmonyOS 怎么配置tab默认选择tabBar
1024浏览 • 1回复 待解决