鸿蒙目前tabs组件tabContent+tabBar 能够使用的api有点差,太难用了,示例如下:
Tabs({ barPosition: BarPosition.Start ,index: this.currentTabIndex}) {
LazyForEach(this.homeTabData, (tabData: TabData, index: number) => {
TabContent() {
if (tabData.type == 'edu_plan') {
EduPlanFrag({ tabData: tabData, fragIndex: index })
} else if (tabData.type == 'edu_history') {
PersonCenterFrag({ tabData: tabData, fragIndex: index })
} else {
ComWaterfallFrag({ tabData: tabData, fragIndex: index })
}
}
.tabBar(this.tabBuilder(tabData, index))//tabBar只能接受builder或者直接写组件
}, (tabData: TabData) => tabData.type)
}
//这里可以触发,但是tabContent和tabBar里面的组件都无法触发焦点事件
.onFocus(()=>{
this.isFocused = true
})
.onBlur(()=>{
this.isFocused = false
})
.barWidth('auto')
.barHeight('28vp')
.width('auto')
.barMode(BarMode.Scrollable)
.scrollable(false)
.animationDuration(50)
.onChange((index: number) => {
this.currentTabIndex = index
this.onTabChange(index)
})
@Builder tabBuilder(tabData: TabData,index:number) {
TabItem({tabData: tabData, setTabIndex: index})
}
@Component
export struct TabItem {
setTabIndex: number = 0
tabData: TabData
@Consume currentTabIndex: number
@State isFocused: boolean = false
onFocusChange? : (index)=>void
build() {
Row() {
Text(this.tabData.name).fontSize(14)
.fontColor(this.isFocused && this.setTabIndex == this.currentTabIndex ? $r('app.color.white_100') :
(this.setTabIndex == this.currentTabIndex ? $r('app.color.btn_select_color') : $r('app.color.white_70')))
.padding({ left: 12, right: 12 })
.focusable(true)
}
.onFocus(()=>{
this.isFocused = true
this.onFocusChange(this.setTabIndex)
})
.onBlur(()=>{
this.isFocused = false
})
.defaultFocus(this.setTabIndex == this.currentTabIndex)
.borderRadius(14)
.borderWidth(0)
.borderColor($r('app.color.transparent'))
.width('auto')
.height(28)
.backgroundColor(this.isFocused && this.setTabIndex == this.currentTabIndex ? $r('app.color.white_100') : $r('app.color.transparent'))
}
}
上面的写法看似没有问题,其实是不行的,TabContent() {}.tabBar() 这个结构是一体的,当中的所有元素都没法监听到onFocus和onBlur, 导致tabItem 没法实现选中效果,只能设置获焦效果,而且而且,tabBar是水平居中的,怎么设都没用
可以通过swiper替代实现类似的效果:
@Component
export struct HomeTabsLayout {
homeTabData: HomeTabData
onTabChange? : (index: number) => void
@Provide isFocused: boolean = false
@Consume currentTabIndex: number
swiperControl: SwiperController = new SwiperController()
build() {
Column() {
Row() {
LazyForEach(this.homeTabData, (tabData: TabData, index: number) => {
TabItem({tabData: tabData, setTabIndex: index, onFocusChange:(changeIndex)=>{
if (this.currentTabIndex != changeIndex) {
//这里需要先触发一下swiper的onChange
this.onFocusScroll(this.currentTabIndex, changeIndex)
}
}})
}, (tabData: TabData) => tabData.type)
}
Swiper(this.swiperControl) {
ForEach(this.homeTabData.items, (tabData: TabData, index: number) => {
ComWaterfallFrag({ tabData: tabData, fragIndex: index })
}, (tabData: TabData) => tabData.type)
}.index(this.currentTabIndex)
.autoPlay(false)
.indicator(false)
.loop(false)
.width('100%')
.height('100%')
.displayMode(SwiperDisplayMode.Stretch)
.cachedCount(3)
.disableSwipe(true)
.duration(50)
.onChange((swiperIndex)=>{
if (this.currentTabIndex != swiperIndex) {
this.scrollOneFrag(swiperIndex,this.currentTabIndex)
}
})
}.width('100%')
.height('100%')
.alignItems(HorizontalAlign.Start)
}
onFocusScroll(from: number, to: number) {
this.currentTabIndex = to
this.scrollOneFrag(from,to)
}
scrollOneFrag(from:number,to:number) {
Logger.i(TAG,"==scrollOneFrag==from:"+from + " ,to:" + to)
if (from > to) {
this.swiperControl.showPrevious()
} else if (from < to){
this.swiperControl.showNext()
}
}
@Builder tabBuilder(tabData: TabData,index:number) {
TabItem({tabData: tabData, setTabIndex: index})
}
}
这个也可以实现类似于tab+tabcontent的效果,而且更加灵活好设计