HarmonyOS PhotoPicker用Tabs后,默认定位到的视频 tab,切换到图片tab,然后点击预览按钮,观察tabs内容会发生跳变?
HarmonyOS PhotoPicker用Tabs后,默认定位到的视频 tab,切换到图片tab,然后点击预览按钮,观察tabs内容会发生跳变?
请把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%')
}
}
}
请把Tabs参数中的index直接用selectedIndex赋值,因为每次修改scrollable属性会导致Tabs走create流程刷新index值。
参考代码如下: