Skip to content

Commit

Permalink
Handle scroll cancellation exception (#696)
Browse files Browse the repository at this point in the history
* Catch scroll cancellation exception

* Fix demo
  • Loading branch information
MatkovIvan authored Jul 20, 2023
1 parent 0fdc2a1 commit 41556ad
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import androidx.compose.ui.util.fastAny
import androidx.compose.ui.util.fastForEach
import kotlin.math.abs
import kotlin.math.roundToInt
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
Expand Down Expand Up @@ -146,11 +147,14 @@ private class AnimatedMouseWheelScrollNode(
while (isActive) {
val event = channel.receive()
isAnimationRunning = true
scrollingLogic.animatedDispatchScroll(event, speed = 1f * density) {
// Sum delta from all pending events to avoid multiple animation restarts.
channel.sumOrNull()
try {
scrollingLogic.animatedDispatchScroll(event, speed = 1f * density) {
// Sum delta from all pending events to avoid multiple animation restarts.
channel.sumOrNull()
}
} finally {
isAnimationRunning = false
}
isAnimationRunning = false
}
}
}
Expand Down Expand Up @@ -229,38 +233,42 @@ private class AnimatedMouseWheelScrollNode(
if (target.isLowScrollingDelta()) {
return
}
scrollableState.scroll {
var requiredAnimation = true
var lastValue = 0f
val anim = AnimationState(0f)
while (requiredAnimation) {
requiredAnimation = false
val durationMillis = (abs(target - anim.value) / speed)
.roundToInt()
.coerceAtMost(maxDurationMillis)
anim.animateTo(
target,
animationSpec = tween(
durationMillis = durationMillis,
easing = LinearEasing
),
sequentialAnimation = true
) {
val delta = value - lastValue
if (!delta.isLowScrollingDelta()) {
val consumedDelta = scrollBy(delta)
if (!(delta - consumedDelta).isLowScrollingDelta()) {
var requiredAnimation = true
var lastValue = 0f
val anim = AnimationState(0f)
while (requiredAnimation) {
requiredAnimation = false
val durationMillis = (abs(target - anim.value) / speed)
.roundToInt()
.coerceAtMost(maxDurationMillis)
try {
scrollableState.scroll {
anim.animateTo(
target,
animationSpec = tween(
durationMillis = durationMillis,
easing = LinearEasing
),
sequentialAnimation = true
) {
val delta = value - lastValue
if (!delta.isLowScrollingDelta()) {
val consumedDelta = scrollBy(delta)
if (!(delta - consumedDelta).isLowScrollingDelta()) {
cancelAnimation()
return@animateTo
}
lastValue += delta
}
tryReceiveNext()?.let {
target += it
requiredAnimation = !(target - lastValue).isLowScrollingDelta()
cancelAnimation()
return@animateTo
}
lastValue += delta
}
tryReceiveNext()?.let {
target += it
requiredAnimation = !(target - lastValue).isLowScrollingDelta()
cancelAnimation()
}
}
} catch (ignore: CancellationException) {
requiredAnimation = true
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import kotlin.random.Random
import kotlinx.coroutines.CancellationException

val LazyLayouts = Screen.Selection(
"LazyLayouts",
Expand All @@ -52,7 +53,11 @@ private fun ExampleLazyColumn() {
LaunchedEffect(Unit) {
while (true) {
withFrameMillis { }
state.scrollBy(2f)
try {
state.scrollBy(2f)
} catch (ignore: CancellationException) {
// Ignore cancelling by manual input
}
}
}
LazyColumn(Modifier.fillMaxSize(), state = state) {
Expand Down

0 comments on commit 41556ad

Please sign in to comment.