Join the Compose Chinese technology community to get more excellent technical tutorials!
There is currently a Chinese manual project for Jetpack Compose, which aims to help developers better understand and master the Compose framework. This article was written by myself. At present, it has been published in this manual. Welcome to refer to it.
Slide refresh effect display
Gestures that involve nested swipes, such as swipe refresh, need to use the nestedScroll modifier. So, let’s talk about what the nestedScroll modifier is and how to use it.
NestedScroll modifier
The nestedScroll modifier is mainly used to handle nestedScroll scenarios, making it possible for the parent layout to hijack the consumption of child layout swipe gestures.
The nestedScroll parameter list has a mandatory parameter connection and an optional parameter dispatcher
Connection: Nested core logic for sliding gesture processing. Internal callbacks can pre-consume some or all of the gesture offsets before the child layout gets the sliding event, or they can get the gesture offsets left after the child layout consumes them.
Dispatcher: a dispatcher that contains a NestedScrollConnection for the parent layout and can call the Dispatch * method to notify the parent layout of a slide
fun Modifier.nestedScroll(
connection: NestedScrollConnection,
dispatcher: NestedScrollDispatcher? = null
)
Copy the code
NestedScrollConnection
NestedScrollConnection provides four callback methods.
interface NestedScrollConnection {
fun onPreScroll(available: Offset, source: NestedScrollSource): Offset = Offset.Zero
fun onPostScroll(
consumed: Offset,
available: Offset,
source: NestedScrollSource
): Offset = Offset.Zero
suspend fun onPreFling(available: Velocity): Velocity = Velocity.Zero
suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
return Velocity.Zero
}
}
Copy the code
onPreScroll
Method description: hijack the slide event in advance, and then deliver it to the sub-layout after consumption.
Parameter list:
- Available: Indicates the offset of the current available slide event
- Source: Type of slide event
Return value: the sliding event Offset of the current component consumption, Offset.Zero if you do not want to consume
onPostScroll
Method description: Gets the sliding event after the child layout is processed
Parameter list:
- Consumed: All sliding event offsets previously consumed
- Available: Indicates the offset of the sliding event that is currently available
- Source: Type of slide event
Return value: the sliding event Offset currently consumed by the component. Offset.Zero can be returned if you do not want to consume, and the remaining Offset will continue to be processed by the parent of the current layout
onPreFling
Get the speed at which the Fling starts.
Parameter list:
- The available:
Fling
The initial velocity
Return value: the speed at which the current component consumes, or velocity.zero if you do not want to consume
onPostFling
Method Description: Obtain the speed information at the end of the Fling.
Parameter list:
-
Consumed: All speeds previously consumed
-
Available: indicates the current available speed
Return value: the speed at which the current component consumes. If you don’t want to consume, return velocity.zero, and the remaining speed will continue to be handled by the parent of the current layout.
It is not a Fling. OnPreFling is not a Fling where your finger is not in the air but in the air. It is not a Fling where your finger is not in the air but in the air.
Slide refresh
Gestures that involve nested swipes, such as swipe refresh, can be done using the nestedScroll modifier.
The sample is introduced
In this example, there is loading animation and list data. As we slide our finger down, the loading animation gradually appears if there is no data at the top of the list. In contrast, when we swipe up, if the loaded animation is still there, the loaded animation gradually disappears up, and the list is not swiped down until the loaded animation is completely gone.
Design and Implementation scheme
In order to realize this sliding refresh requirement, we can design the following scheme. We first need to manage the loading animation and the list data in a single parent layout.
-
When we slide down, we want the sliding gesture to be processed first by the list in the child layout. If the list has slid to the top, the sliding gesture event has not been consumed at this point, and then by the parent layout for consumption. The parent layout can consume the remaining sliding gesture events from the consumption list (adding offsets for loading animations).
-
When we swipe up, we want the swipe gesture to be consumed by the parent layout first (to reduce the offset for the loading animation) and not if the loading animation itself is still not present. The remaining swiping gestures are then handed over to the sub-layout list for consumption.
NestedScrollConnection implementation
The most important thing to use the nestedScroll modifier is to customize the implementation of NestedScrollConnection according to your own business scenario. Next, we will analyze how to implement the NestedScrollConnection one by one.
Implement onPostScroll
Like the implementation we designed earlier, when we slide down, we want the sliding gesture to be processed first by the list in the child layout. If the list has slid to the top, the sliding gesture event is not consumed at this point, and then by the parent layout for consumption. The onPostScroll callback timing is in line with our needs.
Y > 0 to determine whether the slide event is a drag event, and if it is a slide gesture, if all is ok, inform the loading animation to increase the offset. The return value Offset(x = 0f, y = available.y) means that all remaining offsets are consumed and no longer propagated to the outer parent layout.
override fun onPostScroll(
consumed: Offset,
available: Offset,
source: NestedScrollSource
): Offset {
if (source == NestedScrollSource.Drag && available.y > 0) {
state.updateOffsetDelta(available.y)
return Offset(x = 0f, y = available.y)
} else {
return Offset.Zero
}
}
Copy the code
Implement onPreScroll
In contrast, we want to slide and retract the loaded animation, and when we swipe up, we want the sliding gesture to be consumed by the parent layout first (to reduce the offset for the loaded animation), and not if the loaded animation itself is still not present. The remaining swiping gestures are then handed over to the sub-layout list for consumption. The onPreScroll callback timing fits this requirement.
We first need to determine whether the slide event is a drag event, and check whether it is a slide up gesture through available.y < 0. The loading animation itself may not appear at this point, so additional judgment is required. Zero is not consumed if it is not. If it is, Offset(x = 0f, y = available.y) is returned for consumption.
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
if (source == NestedScrollSource.Drag && available.y < 0) {
state.updateOffsetDelta(available.y)
return if (state.isSwipeInProgress) Offset(x = 0f, y = available.y) else Offset.Zero
} else {
return Offset.Zero
}
}
Copy the code
Implement onPreFling
Next, we need a grip when we let go. If you pull over the general height of the loading animation, load, otherwise shrink back to the initial state. Earlier I mentioned that the onPreFling falls back when it lets go, which is exactly what we’re doing here.
Both onPreFling and onPostFling are called back, even if they are slow or still.
Here we only need the attraction effect, not the consumption speed, so return velocity.zero
override suspend fun onPreFling(available: Velocity): Velocity {
if (state.indicatorOffset > height / 2) {
state.animateToOffset(height)
state.isRefreshing = true
} else {
state.animateToOffset(0.dp)
}
return Velocity.Zero
}
Copy the code
Implement onPreFling
Since our slide refresh gesture processing does not involve onPreFling callback timing, no additional implementation is required.
The sample source code
The full source code for this example is open source on my Github Repo, so feel free to read it and submit any feedback.