鸿蒙6.0应用开发——Tabs页签变更效果
鸿蒙6.0应用开发——Tabs页签变更效果
在使用Tabs组件进行开发时,特别是当Tabs组件作为二级导航使用时,业务需求往往需要对Tabs的标签页进行更精细的控制。下文将介绍几种定制标签页显示逻辑的场景。
显示指定页签与预加载
Tabs组件的TabContent默认在首次切换到该标签页时加载。如果TabContent中的内容或初始化逻辑较为复杂,加载速度较慢,则会影响标签页切换的流畅性,进而影响用户体验。此时,如果应用能在切换前预加载相应的标签页,将显著提升使用流畅度。

实现原理
通过TabController的preloadItem()方法可以预加载指定子节点。该方法参数为需要预加载的index数组,无参调用此方法时,会一次性加载所有指定的子节点。因此,为了性能考虑,建议分批加载子节点。代码示例这里做法是当切换到某页签时,预加载所选页签左右两侧的页签内容。
开发步骤
定义subsController属性,并在Tabs的onChange函数中调用preloadItem()预加载当前页签两侧页签。
@Component
export default struct InTabsComponent {
// ...
private subsController: TabsController = new TabsController();
// ...
build() {
// ...
Tabs({
// ...
controller: this.subsController,
// ...
}) {
// ...
}
// ...
.onChange((index: number) => {
this.focusIndex = index;
this.tabBarItemScroller.scrollToIndex(index, true, ScrollAlign.CENTER);
// preload the left and right item
let preloadItems: number[] = [];
if (index - 1 >= 0) {
preloadItems.push(index - 1);
}
if (index + 1 < this.selectTabsViewModel.selectedTabs.length) {
preloadItems.push(index + 1);
}
this.subsController.preloadItems(preloadItems);
})
// ...
}
}
切换到指定页签
Tabs组件除了自带的滑动切换和点击切换功能外,还提供了两种可编程方式来切换页签。第一种是通过调用TabsController的changeIndex()方法,切换到指定的index;第二种是定义一个由@State修饰的变量currentIndex,并将其绑定到Tabs,通过修改currentIndex的值来触发页签切换。

开发步骤
定义currentIndex变量和tabController属性,并绑定到Tabs。在按钮onClick函数中,调用tabController.changeIndex()或者直接修改currentIndex变量切换页签。
@Component
export default struct SwitchTabComponent {
// ...
@State currentIndex: number = 0;
private tabController: TabsController = new TabsController();
// ...
build() {
Column() {
Row() {
Button('Previous Tab')
.onClick(() => {
this.tabController.changeIndex((this.currentIndex + 3) % 4); // call tabController.changeIndex() to switch tab
})
// ...
Button('Next Tab')
.onClick(() => {
this.currentIndex = (this.currentIndex + 1) % 4; // change currentIndex to switch tab
})
// ...
}
Tabs({
controller: this.tabController,
index: $$this.currentIndex // use $$ for two-way data binding
}) {
// ...
}
}
}
}
此外,Tabs可注册切换前的处理函数,进一步控制切换行为。详情请参见切换至指定页签。
增删Tabs页签
在日常的应用开发中,经常需要实现用户自定义选择频道的功能。通常,这些自定义选择的频道会通过Tabs组件来展示,因此需要动态地更新Tabs的页签。本示例设计了一对父子组件来演示这一功能。父组件负责显示页签及其内容,并在页签栏的最右侧设置一个“更多”按钮。点击此按钮会弹出一个窗口,供用户选择需要显示的页签。该弹窗内容由子组件提供,关闭弹窗后,父组件的页签将被更新。

实现原理
定义selectTabsViewModel对象,其中的数组allTabs表示所有可选择页签,数组selectedTabs表示选中的需要显示的页签,并通过@Link绑定到父组件InTabComponent和子组件SelectTabsComponent中。子组件SelectTabsComponent作为一个弹窗用于选择需要显示的页签。选择完成后,关闭弹窗并更新 selectTabsViewModel对象中的选中页签数组 selectedTabs,以触发父组件InTabComponent的页签更新。

开发步骤
-
定义SelectTabsViewModel类,包含所有可选择页签数组allTabs属性,和需要显示的页签数组selectedTabs属性,及更新显示页签数组的方法updateSelectedTabs()。
@Observed class TabItemArray extends Array<TabItemViewModel> { } @Observed export default class SelectTabsViewModel { allTabs: TabItemArray = new TabItemArray(); selectedTabs: TabItemArray = new TabItemArray(); // ... async loadTabs(ctx: Context) { // ... } // apply changes to the selected tabs updateSelectedTabs() { let tempTabs: TabItemViewModel[] = []; for (let tab of this.allTabs) { if (tab.isChecked) { tempTabs.push(tab); } } this.selectedTabs = tempTabs; } } -
在InTabsComponent中定义selectTabsViewModel属性,并且在aboutToAppear()方法中初始化。
@Component export default struct InTabsComponent { @State selectTabsViewModel: SelectTabsViewModel = new SelectTabsViewModel(); // ... async aboutToAppear() { // ... await this.selectTabsViewModel.loadTabs(this.ctx); // ... } // ... } -
利用ForEach组件将selectTabsViewModel.selectedTabs属性绑定到Tabs的页签上。
Tabs({ // ... }) { // bind selected tabs to ui ForEach(this.selectTabsViewModel.selectedTabs, (tab: TabItemViewModel, index: number) => { if (index === this.selectTabsViewModel.selectedTabs.length - 1) { TabContent() { // ... } .tabBar(this.tabBuilder(index, tab)) // ... } else { // ... } }, (tab: TabItemViewModel, index: number) => index + '_' + JSON.stringify(tab)) } -
在更多按钮的弹窗中初始化SelectTabsComponent,并将selectTabsViewModel属性作为双向绑定属性传入。在关闭弹窗处理函数中调用selectTabsViewModel.updateSelectedTabs()方法,更新需要显示的组件。
@Builder sheetBuilder() { //select tabs to show SelectTabsComponent({ selectTabsViewModel: this.selectTabsViewModel }) } build() { Scroll() { Column() { BannerComponent() Stack({ alignContent: Alignment.TopEnd }) { Row() { Image($r('app.media.more')) // ... .onClick(() => { this.showSelectTabsComponent = !this.showSelectTabsComponent; }) } // ... .zIndex(1) .bindSheet($$this.showSelectTabsComponent, this.sheetBuilder(), { detents: [SheetSize.MEDIUM, SheetSize.MEDIUM, 500], preferType: SheetType.BOTTOM, title: { title: $r('app.string.bind_sheet_title') }, onWillDismiss: (dismissSheetAction: DismissSheetAction) => { // update tab when closing modal box this.selectTabsViewModel.updateSelectedTabs(); if (this.selectTabsViewModel.selectedTabs.length > 0) { this.subsController.changeIndex(0); } dismissSheetAction.dismiss(); } }) // ... } } } // ... } -
在SelectTabsComponent中将selectTabsViewModel.allTabs属性渲染成toggle组件,并且注册toggle组件的切换处理函数onChange(),在其中修改该页签的选择状态isChecked属性,供更新显示页签方法selectTabsViewModel.updateSelectedTabs()使用。
@Component export default struct SelectTabsComponent { @State checkedChange: boolean = false; @Link selectTabsViewModel: SelectTabsViewModel; build() { Grid() { ForEach(this.selectTabsViewModel.allTabs, (tab: TabItemViewModel) => { GridItem() { Row() { Toggle({ type: ToggleType.Button, isOn: tab.isChecked }) { // ... } // ... .onChange((isOn: boolean) => { tab.isChecked = isOn; this.checkedChange = !this.checkedChange; }) } } }, (tab: TabItemViewModel, index: number) => index + '_' + JSON.stringify(tab)) } .columnsTemplate(('1fr 1fr 1fr 1fr') as string) .height(Constants.FULL_HEIGHT) } }
更多推荐

所有评论(0)