Fix scroll animation when system animation is disabled (#7509)
This commit is contained in:
parent
788583e66f
commit
ba93060e59
5 changed files with 97 additions and 7 deletions
|
@ -3,7 +3,6 @@ package eu.kanade.presentation.browse
|
||||||
import androidx.compose.foundation.layout.WindowInsets
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
import androidx.compose.foundation.layout.asPaddingValues
|
import androidx.compose.foundation.layout.asPaddingValues
|
||||||
import androidx.compose.foundation.layout.navigationBars
|
import androidx.compose.foundation.layout.navigationBars
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material3.Switch
|
import androidx.compose.material3.Switch
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
@ -15,6 +14,7 @@ import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import eu.kanade.presentation.components.EmptyScreen
|
import eu.kanade.presentation.components.EmptyScreen
|
||||||
|
import eu.kanade.presentation.components.LazyColumn
|
||||||
import eu.kanade.presentation.components.LoadingScreen
|
import eu.kanade.presentation.components.LoadingScreen
|
||||||
import eu.kanade.presentation.components.PreferenceRow
|
import eu.kanade.presentation.components.PreferenceRow
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
|
|
|
@ -2,12 +2,12 @@ package eu.kanade.presentation.category.components
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.foundation.lazy.LazyListState
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import eu.kanade.domain.category.model.Category
|
import eu.kanade.domain.category.model.Category
|
||||||
|
import eu.kanade.presentation.components.LazyColumn
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CategoryContent(
|
fun CategoryContent(
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
package eu.kanade.presentation.components
|
package eu.kanade.presentation.components
|
||||||
|
|
||||||
import androidx.compose.foundation.gestures.FlingBehavior
|
import androidx.compose.foundation.gestures.FlingBehavior
|
||||||
import androidx.compose.foundation.gestures.ScrollableDefaults
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.calculateEndPadding
|
import androidx.compose.foundation.layout.calculateEndPadding
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.foundation.lazy.LazyListScope
|
import androidx.compose.foundation.lazy.LazyListScope
|
||||||
import androidx.compose.foundation.lazy.LazyListState
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
|
@ -17,6 +15,38 @@ import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import eu.kanade.presentation.util.drawVerticalScrollbar
|
import eu.kanade.presentation.util.drawVerticalScrollbar
|
||||||
|
import eu.kanade.presentation.util.flingBehaviorIgnoringMotionScale
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LazyColumn with fling animation fix
|
||||||
|
*
|
||||||
|
* @see flingBehaviorIgnoringMotionScale
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun LazyColumn(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
state: LazyListState = rememberLazyListState(),
|
||||||
|
contentPadding: PaddingValues = PaddingValues(0.dp),
|
||||||
|
reverseLayout: Boolean = false,
|
||||||
|
verticalArrangement: Arrangement.Vertical =
|
||||||
|
if (!reverseLayout) Arrangement.Top else Arrangement.Bottom,
|
||||||
|
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
|
||||||
|
flingBehavior: FlingBehavior = flingBehaviorIgnoringMotionScale(),
|
||||||
|
userScrollEnabled: Boolean = true,
|
||||||
|
content: LazyListScope.() -> Unit,
|
||||||
|
) {
|
||||||
|
androidx.compose.foundation.lazy.LazyColumn(
|
||||||
|
modifier = modifier,
|
||||||
|
state = state,
|
||||||
|
contentPadding = contentPadding,
|
||||||
|
reverseLayout = reverseLayout,
|
||||||
|
verticalArrangement = verticalArrangement,
|
||||||
|
horizontalAlignment = horizontalAlignment,
|
||||||
|
flingBehavior = flingBehavior,
|
||||||
|
userScrollEnabled = userScrollEnabled,
|
||||||
|
content = content,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LazyColumn with scrollbar.
|
* LazyColumn with scrollbar.
|
||||||
|
@ -30,7 +60,7 @@ fun ScrollbarLazyColumn(
|
||||||
verticalArrangement: Arrangement.Vertical =
|
verticalArrangement: Arrangement.Vertical =
|
||||||
if (!reverseLayout) Arrangement.Top else Arrangement.Bottom,
|
if (!reverseLayout) Arrangement.Top else Arrangement.Bottom,
|
||||||
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
|
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
|
||||||
flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
|
flingBehavior: FlingBehavior = flingBehaviorIgnoringMotionScale(),
|
||||||
userScrollEnabled: Boolean = true,
|
userScrollEnabled: Boolean = true,
|
||||||
content: LazyListScope.() -> Unit,
|
content: LazyListScope.() -> Unit,
|
||||||
) {
|
) {
|
||||||
|
@ -69,7 +99,7 @@ fun FastScrollLazyColumn(
|
||||||
verticalArrangement: Arrangement.Vertical =
|
verticalArrangement: Arrangement.Vertical =
|
||||||
if (!reverseLayout) Arrangement.Top else Arrangement.Bottom,
|
if (!reverseLayout) Arrangement.Top else Arrangement.Bottom,
|
||||||
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
|
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
|
||||||
flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
|
flingBehavior: FlingBehavior = flingBehaviorIgnoringMotionScale(),
|
||||||
userScrollEnabled: Boolean = true,
|
userScrollEnabled: Boolean = true,
|
||||||
content: LazyListScope.() -> Unit,
|
content: LazyListScope.() -> Unit,
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -20,7 +20,6 @@ import androidx.compose.foundation.layout.navigationBars
|
||||||
import androidx.compose.foundation.layout.only
|
import androidx.compose.foundation.layout.only
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.systemBars
|
import androidx.compose.foundation.layout.systemBars
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.foundation.lazy.LazyListScope
|
import androidx.compose.foundation.lazy.LazyListScope
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
|
@ -52,6 +51,7 @@ import com.google.accompanist.swiperefresh.SwipeRefresh
|
||||||
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
|
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
|
||||||
import eu.kanade.domain.chapter.model.Chapter
|
import eu.kanade.domain.chapter.model.Chapter
|
||||||
import eu.kanade.presentation.components.ExtendedFloatingActionButton
|
import eu.kanade.presentation.components.ExtendedFloatingActionButton
|
||||||
|
import eu.kanade.presentation.components.LazyColumn
|
||||||
import eu.kanade.presentation.components.Scaffold
|
import eu.kanade.presentation.components.Scaffold
|
||||||
import eu.kanade.presentation.components.SwipeRefreshIndicator
|
import eu.kanade.presentation.components.SwipeRefreshIndicator
|
||||||
import eu.kanade.presentation.components.VerticalFastScroller
|
import eu.kanade.presentation.components.VerticalFastScroller
|
||||||
|
|
60
app/src/main/java/eu/kanade/presentation/util/Scrollable.kt
Normal file
60
app/src/main/java/eu/kanade/presentation/util/Scrollable.kt
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
package eu.kanade.presentation.util
|
||||||
|
|
||||||
|
import androidx.compose.animation.core.AnimationState
|
||||||
|
import androidx.compose.animation.core.DecayAnimationSpec
|
||||||
|
import androidx.compose.animation.core.animateDecay
|
||||||
|
import androidx.compose.animation.rememberSplineBasedDecay
|
||||||
|
import androidx.compose.foundation.gestures.FlingBehavior
|
||||||
|
import androidx.compose.foundation.gestures.ScrollScope
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.MotionDurationScale
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import kotlin.math.abs
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FlingBehavior that always uses the default motion scale.
|
||||||
|
*
|
||||||
|
* This makes the scrolling animation works like View's lists
|
||||||
|
* when "Remove animation" settings is on.
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun flingBehaviorIgnoringMotionScale(): FlingBehavior {
|
||||||
|
val flingSpec = rememberSplineBasedDecay<Float>()
|
||||||
|
return remember(flingSpec) {
|
||||||
|
DefaultFlingBehavior(flingSpec)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val DefaultMotionDurationScale = object : MotionDurationScale {
|
||||||
|
// Use default motion scale factor
|
||||||
|
override val scaleFactor: Float = 1f
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DefaultFlingBehavior(
|
||||||
|
private val flingDecay: DecayAnimationSpec<Float>,
|
||||||
|
) : FlingBehavior {
|
||||||
|
override suspend fun ScrollScope.performFling(initialVelocity: Float): Float {
|
||||||
|
// come up with the better threshold, but we need it since spline curve gives us NaNs
|
||||||
|
return if (abs(initialVelocity) > 1f) {
|
||||||
|
var velocityLeft = initialVelocity
|
||||||
|
var lastValue = 0f
|
||||||
|
withContext(DefaultMotionDurationScale) {
|
||||||
|
AnimationState(
|
||||||
|
initialValue = 0f,
|
||||||
|
initialVelocity = initialVelocity,
|
||||||
|
).animateDecay(flingDecay) {
|
||||||
|
val delta = value - lastValue
|
||||||
|
val consumed = scrollBy(delta)
|
||||||
|
lastValue = value
|
||||||
|
velocityLeft = this.velocity
|
||||||
|
// avoid rounding errors and stop if anything is unconsumed
|
||||||
|
if (abs(delta - consumed) > 0.5f) this.cancelAnimation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
velocityLeft
|
||||||
|
} else {
|
||||||
|
initialVelocity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in a new issue