场景描述
在页面布局过程中,Tabs可以将产品包含的所有内容进行清晰分类,一目了然地呈现应用的内容范围,方便概览与跳转
场景一:tab嵌套list的吸顶效果
场景二:tabbar样式自定义:
1、tabs切换、监听
2、样式自定义
3、tabbar尾端文字渐变
场景三:tabContent切换动画
方案描述
场景一:tab嵌套list的吸顶效果
方案一:
实现思路:
1、最外层为tabs组件,首页tabContent主要用的stack组件嵌套了scroll组件+导航输入框组件,其中scroll组件嵌套了tabs组件,tabs里面嵌套list组件。
2、外层的滚动组件scroll主要通过onScroll,onScrollEdge以及onScrollFrameBegin回调判断页面是否在顶部,中间还是底部。
3、里层list组件也是通过onReachStart,onReachEnd,onScrollFrameBegin回调来判断list列表是否在顶部,中间还是底部,使用scrollBy滑动指定距离。如Scroll嵌套List滚动时,List组件的edgeEffect属性需设置为EdgeEffect.None。
核心代码
// scroll部分主要逻辑
enum ScrollPosition{
start,
center,
end
}
@Entry
@Component
struct NestedScroll {
@State listPosition: number = ScrollPosition.start; // 0代表滚动到List顶部,1代表中间值,2代表滚动到List底部。
@State scrollPosition: number = ScrollPosition.start; // 0代表滚动到页面顶部,1代表中间值,2代表滚动到页面底部。
...
build() {
Column() {
Tabs({ barPosition: BarPosition.End, index: this.currentIndex, controller: this.TabsController }) {
TabContent() {
Stack({ alignContent: Alignment.Top }) {
Scroll(this.scrollerForScroll) {
Column() {
Column(){
}
.width("100%")
.height("40%")
.backgroundColor(Color.Pink)
// tabbar
Row({ space: 7 }) {
Scroll() {
...
}
}
//tabs
Tabs({ barPosition: BarPosition.Start, controller: this.subsController }) {
TabContent() {
List({ space: 10, scroller: this.scrollerForList }) {
...
}
.onReachStart(() => {
this.listPosition = ScrollPosition.start
})
.onReachEnd(() => {
this.listPosition = ScrollPosition.end
})
.onScrollFrameBegin((offset: number, state: ScrollState) => {
console.info('chenoffset::'+offset)
// 滑动到列表中间时
if (!((this.listPosition == ScrollPosition.start && offset < 0)
|| (this.listPosition == ScrollPosition.end && offset > 0))) {
this.listPosition = ScrollPosition.center
}
// 如果页面已滚动到底部 且 列表不在顶部或列表有正向偏移量
if (this.scrollPosition == ScrollPosition.end
&& (this.listPosition != ScrollPosition.start || offset > 0)) {
console.info('chenoffsetscrollBy::'+offset)
return { offsetRemain: offset };
} else {
// scrollBy滑动指定距离
console.info('chenoffsetscrollBy滑动指定距离::'+offset)
this.scrollerForScroll.scrollBy(0, offset)
return { offsetRemain: 0 };
}
})
}.tabBar('关注')
...
}
}
.width("100%")
.height("92%")
.backgroundColor('#F1F3F5')
}
}
.scrollBar(BarState.Off)
.width("100%")
.height("100%")
// 滚动事件回调, 返回竖直方向偏移量,单位vp
.onScroll((xOffset: number, yOffset: number) => {
this.currentYOffset = this.scrollerForScroll.currentOffset().yOffset;
console.info('this.currentYOffset'+this.currentYOffset)
// 非(页面在顶部或页面在底部),则页面在中间
if (!((this.scrollPosition == ScrollPosition.start && yOffset < 0)
|| (this.scrollPosition == ScrollPosition.end && yOffset > 0))) {
this.scrollPosition = ScrollPosition.center
}
})
// 当组件滚动到边缘时触发
.onScrollEdge((side: Edge) => {
if (side == Edge.Top) {
// 页面在顶部
this.scrollPosition = ScrollPosition.start
} else if (side == Edge.Bottom) {
// 页面在底部
this.scrollPosition = ScrollPosition.end
}
})
.onScrollFrameBegin(offset => {
if (this.scrollPosition == ScrollPosition.end) {
return { offsetRemain: 0 };
} else {
return { offsetRemain: offset };
}
})
// 顶部导航输入框
Row() {
TextInput({ text: '', placeholder: 'input your word...',