Requirement Description
When using the Scroll component, a certain element cannot be fixed at the top during scrolling. How can the sticky effect of the Scroll component be achieved during scrolling?
Background Knowledge
The Scroll component is a container component that supports vertical and horizontal scrolling, enabling the scrolling of elements within the component to display more and richer content on the page. Since the Scroll component only supports one child component, it is generally used in conjunction with components such as Column, Row, List, and Grid.
To achieve the sticky effect of a certain element within the Scroll component, the nestedScroll property can be used to set a nested scrolling mode for the forward and backward scrolling of parent and child components, thereby achieving scrolling linkage between the component and its parent component.
Implementation Steps
Based on the above background knowledge, the sticky effect can be achieved by setting the nestedScroll property for the component. By changing the value of the parameter, the parent component can trigger an edge effect (fixed at the edge) when scrolling forward to the edge. Generally, the parameter value of the nestedScroll property is set as follows:
Scroll() {...}
//......
.nestedScroll({
scrollForward: NestedScrollMode.PARENT_FIRST, // When scrolling forward, the parent component scrolls first
scrollBackward: NestedScrollMode.SELF_FIRST // When scrolling backward, the component scrolls first
})
It is important to note that nestedScroll involves nesting components within their parent components. Therefore, in actual development, it is crucial to clearly define the scope and actual effects of the parent component.
Below, we will demonstrate the implementation of a sticky effect using the Tabs component and the List component as examples, and provide a faulty example for comparison.
Code Snippet / Configuration
@Entry
@Component
struct ScrollCeiling1 {
scroller: Scroller = new Scroller()
itemData: Array<number> = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
tabTitles: Array<string> = ['Tab1', 'Tab2', 'Tab3']
// Create a content component under a single tab of the Tabs component and set the nestedScroll property of the List component within the TabContent component. The parent component of the List component is the TabContent component.
@Builder
tabContentData(tabTitle: string) {
TabContent() {
List() {
ForEach(this.itemData, (item: number) => {
ListItem() {
Text(`${ item }`).height(80).width('100%').textAlign(TextAlign.Center).backgroundColor(0xDDDDDD).margin({bottom: 5})
}
})
}
.nestedScroll({
scrollForward: NestedScrollMode.PARENT_FIRST,
scrollBackward: NestedScrollMode.SELF_FIRST
})
}.tabBar(tabTitle)
.padding({top:5, bottom:5})
.borderWidth(1)
.borderColor(Color.Red)
}
/*
Set the scrolling mode of scrollForward to NestedScrollMode.PARENT_FIRST:
When controlling the forward scrolling of elements within the List, the parent component TabContent scrolls first, covering the Image component within the Column component nested by the Scroll component. Subsequently, the Tabs component touches the top edge, triggering the edge effect, thereby fixing it at the top.
Set the scrolling mode of scrollBackward to NestedScrollMode.SELF_FIRST:
When controlling the backward scrolling of elements within the List, the content of the List scrolls first until it reaches the very top of the List, after which the parent component TabContent begins to scroll. */
build() {
Scroll(this.scroller){
Column() {
Image($r('app.media.app_icon')).height(70)
Tabs() {
ForEach(this.tabTitles, (title: string) => {
this.tabContentData(title)
})
}
.borderWidth(2)
}.width('90%')
.alignItems(HorizontalAlign.Center)
}.width('100%')
.align(Alignment.Center)
.scrollBar(BarState.Off)
}
}
- Implementing the ceiling-mounted List component.
@Entry
@Component
struct ScrollCeiling2 {
scroller: Scroller = new Scroller()
itemData: Array<number> = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
classList: Array<string> = ['class1', 'class2']
build() {
Scroll(this.scroller) {
Column({space : 10}) {
// search box
Stack({alignContent: Alignment.End}) {
Row() {
Image($r('sys.media.ohos_ic_public_search_filled')).height(20)
.margin({left:5})
TextInput({placeholder: 'Please enter'}).type(InputType.Normal).fontSize('10fp').backgroundColor(Color.Transparent)
}.height(50).width('100%')
.borderWidth('1')
.borderRadius(24)
.padding({left:5, right:5, top:5, bottom:5})
Button('search').type(ButtonType.Capsule).width(80).margin({right:5})
}
Column() {
// Class_List
List() {
ForEach(this.classList, (cls: string) => {
ListItem() {
Text(cls).fontSize('20fp').width(100).textAlign(TextAlign.Center)
.borderWidth(1).borderRadius(12).margin({left:5, right:5})
}
})
}.listDirection(Axis.Horizontal).height(30)
/*
Set the scrolling mode of scrollForward to NestedScrollMode.PARENT_FIRST:
When controlling the forward scrolling of elements within Data_List, the parent component Column scrolls first, covering the Stack component (search box) within the nested Column component of the Scroll component. Subsequently, when the Column component touches the top edge, it triggers the edge effect, thereby fixing Class_List at the top.
Set the scrolling mode of scrollBackward to NestedScrollMode.SELF_FIRST:
When controlling the backward scrolling of elements within Data_List, the content of Data_List scrolls first until it reaches the very top of Data_List, after which the parent component Column begins to scroll.
*/
// Data_List
List() {
ForEach(this.itemData, (item: number) => {
ListItem() {
Text(`${ item }`).height(80).width('100%').textAlign(TextAlign.Center).backgroundColor(0xDDDDDD).margin({bottom: 5})
}
})
}.height('90%')
.nestedScroll({
scrollForward: NestedScrollMode.PARENT_FIRST,
scrollBackward: NestedScrollMode.SELF_FIRST
})
}.height('100%')
}
}.scrollBar(BarState.Off)
}
}
- Error example The effect is as shown below:
@Component
struct ScrollCeiling3 {
scroller: Scroller = new Scroller()
itemData: Array<number> = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
classList: Array<string> = ['class1', 'class2']
build() {
Scroll(this.scroller) {
Column({space : 10}) {
//search box
Stack({alignContent: Alignment.End}) {
Row() {
Image($r('sys.media.ohos_ic_public_search_filled')).height(20)
.margin({left:5})
TextInput({placeholder: 'Please enter'}).type(InputType.Normal).fontSize('10fp').backgroundColor(Color.Transparent)
}.height(50).width('100%')
.borderWidth('1')
.borderRadius(24)
.padding({left:5, right:5, top:5, bottom:5})
Button('search').type(ButtonType.Capsule).width(80).margin({right:5})
}
// Class_List
List() {
ForEach(this.classList, (cls: string) => {
ListItem() {
Text(cls).fontSize('20fp').width(100).textAlign(TextAlign.Center)
.borderWidth(1).borderRadius(12).margin({left:5, right:5})
}
})
}.listDirection(Axis.Horizontal).height(30)
/*
The outer layer of Class_List and Data_List does not have an additional nested Column component. At this point, the parent component of Class_List and Data_List is the Column component nested within Scroll. When controlling the elements within Data_List to scroll forward, based on the nestedScroll settings, the parent Column component scrolls first. Since the top of the Column component is already at the edge of the display, the Column component completes its scrolling and begins to scroll its child components. As all components are within the Column component and the height of Data_List is set to 100%, Class_List will start to exit the display edge as it scrolls forward, making it impossible to achieve the sticky header effect.
*/
// Data_List
List() {
ForEach(this.itemData, (item: number) => {
ListItem() {
Text(`${ item }`).height(80).width('100%').textAlign(TextAlign.Center).backgroundColor(0xDDDDDD).margin({bottom: 5})
}
})
}.height('100%')
.nestedScroll({
scrollForward: NestedScrollMode.PARENT_FIRST,
scrollBackward: NestedScrollMode.SELF_FIRST
})
}
}.scrollBar(BarState.Off)
}
}
Interestingly, if the height of the Data_List in the example is set to less than 100% (for instance, 93%), since the Data_List cannot fully occupy the screen, part or even the entire Class_List may be visible, potentially resulting in an effect similar to that of a ceiling effect.
Test Results
Output on wearables:
To fix an element at the top of the Scroll component, you can set the nestedScroll property of the component, bind it to the correct parent component for nested scrolling mode, and set the scrollForward parameter to NestedScrollMode.PARENT_FIRST and scrollBackward to NestedScrollMode.SELF_FIRST. This determines the scrolling mode for forward and backward scrolling, thereby achieving the effect of an element sticking to the top within the Scroll component.


Top comments (0)