Read the original article:Scroll Offset Retrieval Method
Scroll Offset Retrieval Method
Problem Description
During the scrolling process inside a Scroll container, when the ScrollDirection property is changed, the components inside the container return to their initial positions. How can we record the scrolling position of the internal components so that they do not return to their initial positions when the ScrollDirection property changes?
Background Knowledge
In the Scroll component of HarmonyOS, changing the scroll direction (ScrollDirection) usually causes the positions of the internal components within the container to be reset. This is because a change in scroll direction requires recalculating the layout and positions of the internal components.
Solution
If you want to prevent the internal components' positions from being reset when changing the scroll direction, you can achieve this by following these steps:
- Use a listener to record positions: You can record the position of each child component during the scroll event of the Scroll component. For example, you can execute
scroller.currentOffset()in theonWillScrolloronDidScrollevent to listen to the displacement in real-time, and manually set the scroll offset when the scroll direction changes. When the scroll direction changes, restore the scroll position based on the stored position state. This way, when the scroll direction is changed, you can use the recorded position information to move the child components back to their previous positions. - Customize scroll behavior: Override the default scroll behavior of the Scroll component to customize how the component handles changes in scroll direction.
- Use state management: Utilize HarmonyOS's state management mechanism to manage the internal state of the Scroll component, including the scroll position and other related states. When the scroll direction changes, update these states through state management instead of resetting them.
Example code is as follows:
@Entry
@Component
struct TabsExample {
@State fontColor: string = '# 182431'
@State selectedFontColor: string = '#007DFF'
@State currentIndex: number = 0
@State numbers1: String[] = ['0', '1', '2', '3', '4']
@State numbers2: String[] = ['0', '1', '2', '3', '4', '5']
curScrollOffset1: number = 0
@State clickedContent: string = ""
layoutOptions: GridLayoutOptions = {
regularSize: [1, 1],
onGetRectByIndex: (index: number) => {
if (index == 0) {
return [0, 0, 1, 1]
} else if (index == 1) {
return [0, 1, 2, 2]
} else if (index == 2) {
return [0, 3, 3, 3]
} else if (index == 3) {
return [3, 0, 3, 3]
} else if (index == 4) {
return [4, 3, 2, 2]
} else {
return [5, 5, 1, 1]
}
}
}
private controller: TabsController = new TabsController()
private scroller: Scroller = new Scroller()
private scroller1: Scroller = new Scroller()
@Builder
tabBuilder(index: number, name: string) {
Column() {
Text(name)
.fontColor(this.currentIndex === index ? this.selectedFontColor : this.fontColor)
.fontSize(8)
.fontWeight(this.currentIndex === index ? 500 : 400)
.lineHeight(11)
.margin({
top: 10,
bottom: 5
})
Divider()
.strokeWidth(2)
.color('#36D')
.opacity(this.currentIndex === index ? 1 : 0)
}.width('100%')
}
build() {
Column() {
Row(){
Button('tabs stop scrolling and store position')
.width(50).height(30)
.onClick(() => {
AppStorage.setOrCreate('this.currentIndex',
this.currentIndex); // Record the index in the tabs to be switched. Here, index === 1 is used as an example to record the scrolling offset. This.currentIndex can be set to 1.
AppStorage.setOrCreate('this.curScrollOffset1',
this.curScrollOffset1); // Use index === 1 as an example to record the scrolling offset.
})
Button('Click to scroll to previous position')
.width(50).height(30)
.onClick(() => {
this.currentIndex =
Number(AppStorage.get('this.currentIndex')) // Obtain the index in the tabs where the record is switched.
this.curScrollOffset1 =
Number(AppStorage.get('this.curScrollOffset1')) // Obtain the scrolling offset recorded using index === 1 as an example.
this.scroller.scrollTo({
xOffset: 0,
yOffset: this.curScrollOffset1,
animation: {
duration: 100,
curve: Curve.Ease
}
}) // Change yOffset because you can only move up or down.
})
}
Tabs({
barPosition: BarPosition.Start,
index: this.currentIndex,
controller: this.controller
}) {
TabContent() {
Scroll(this.scroller) {
Grid() {
ForEach(this.numbers1, (day: string) => {
ForEach(this.numbers2, (day: string) => {
GridItem() {
Text(day)
.fontSize(8)
.backgroundColor('#ffeca636')
.width('100%')
.height('100%')
.textAlign(TextAlign.Center)
}
}, (day: string) => day)
}, (day: string) => day)
}
.columnsTemplate('1fr 1fr 1fr 1fr')
.rowsTemplate('1fr 1fr 1fr 1fr')
.columnsGap(5)
.rowsGap(5)
.width('100%')
.height('150%')
}.backgroundColor('#ffffed00')
}
.tabBar(this.tabBuilder(0, 'green'))
TabContent() {
Scroll(this.scroller1) {
Grid() {
ForEach(this.numbers1, (day: string) => {
ForEach(this.numbers2, (day: string) => {
GridItem() {
Text(day)
.fontSize(8)
.backgroundColor('#ffa4b4d7')
.width('100%')
.height('100%')
.textAlign(TextAlign.Center)
}
}, (day: string) => day)
}, (day: string) => day)
}
.columnsTemplate('1fr 1fr 1fr 1fr')
.rowsTemplate('1fr 1fr 1fr 1fr')
.columnsGap(5)
.rowsGap(5)
.width('100%')
.height('150%')
}
.backgroundColor('#00ef2323')
.onWillScroll((xOffset: number, yOffset: number, scrollState: ScrollState) => {
this.curScrollOffset1 += yOffset // Slides up or down, only yOffset needs to be recorded.
console.info('this.curScrollOffset1' + '' + this.curScrollOffset1)
})
}
.tabBar(this.tabBuilder(1, 'blue'))
}
.vertical(false)
.barMode(BarMode.Fixed)
.barWidth(180)
.barHeight(28)
.animationDuration(400)
.onChange((index: number) => {
if (index === 0) {
this.scroller.scrollTo({
xOffset: 0,
yOffset: 0,
animation: {
duration: 100,
curve: Curve.Ease
}
}) // Data scrolls to the top during index switching.
} else {
this.scroller1.scrollTo({
xOffset: 0,
yOffset: 0,
animation: {
duration: 100,
curve: Curve.Ease
}
})
}
this.currentIndex = index
})
}
}
}
If you want to verify whether the offset is correct, you can remove this code segment and record the offset.
this.scroller1.scrollTo({ xOffset: 0, yOffset: 0, animation: { duration: 100, curve: Curve.Ease } });

Top comments (0)