Skip to content

Commit

Permalink
Merge pull request #810 from chrisbanes/cb/snapper-migration
Browse files Browse the repository at this point in the history
Migrate Pager to use Snapper
  • Loading branch information
andkulikov committed Oct 21, 2021
2 parents d6b0629 + 8dd152e commit 7e1bc6c
Show file tree
Hide file tree
Showing 13 changed files with 121 additions and 365 deletions.
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Expand Up @@ -27,6 +27,8 @@ compose-material-material = { module = "androidx.compose.material:material", ver
compose-material-iconsext = { module = "androidx.compose.material:material-icons-extended", version.ref = "compose" }
compose-animation-animation = { module = "androidx.compose.animation:animation", version.ref = "compose" }

snapper = "dev.chrisbanes.snapper:snapper:0.1.0"

android-gradlePlugin = { module = "com.android.tools.build:gradle", version.ref = "gradlePlugin" }
gradleMavenPublishPlugin = "com.vanniktech:gradle-maven-publish-plugin:0.17.0"
metalavaGradle = "me.tylerbwong.gradle:metalava-gradle:0.1.9"
Expand Down
13 changes: 5 additions & 8 deletions pager/api/current.api
Expand Up @@ -5,14 +5,15 @@ package com.google.accompanist.pager {
}

public final class Pager {
method @androidx.compose.runtime.Composable @com.google.accompanist.pager.ExperimentalPagerApi public static void HorizontalPager(int count, optional androidx.compose.ui.Modifier modifier, optional com.google.accompanist.pager.PagerState state, optional boolean reverseLayout, optional float itemSpacing, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional androidx.compose.foundation.layout.PaddingValues contentPadding, kotlin.jvm.functions.Function2<? super com.google.accompanist.pager.PagerScope,? super java.lang.Integer,kotlin.Unit> content);
method @androidx.compose.runtime.Composable @com.google.accompanist.pager.ExperimentalPagerApi public static void VerticalPager(int count, optional androidx.compose.ui.Modifier modifier, optional com.google.accompanist.pager.PagerState state, optional boolean reverseLayout, optional float itemSpacing, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional androidx.compose.foundation.layout.PaddingValues contentPadding, kotlin.jvm.functions.Function2<? super com.google.accompanist.pager.PagerScope,? super java.lang.Integer,kotlin.Unit> content);
method @androidx.compose.runtime.Composable @com.google.accompanist.pager.ExperimentalPagerApi public static void HorizontalPager(int count, optional androidx.compose.ui.Modifier modifier, optional com.google.accompanist.pager.PagerState state, optional boolean reverseLayout, optional float itemSpacing, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, kotlin.jvm.functions.Function2<? super com.google.accompanist.pager.PagerScope,? super java.lang.Integer,kotlin.Unit> content);
method @androidx.compose.runtime.Composable @com.google.accompanist.pager.ExperimentalPagerApi public static void VerticalPager(int count, optional androidx.compose.ui.Modifier modifier, optional com.google.accompanist.pager.PagerState state, optional boolean reverseLayout, optional float itemSpacing, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, kotlin.jvm.functions.Function2<? super com.google.accompanist.pager.PagerScope,? super java.lang.Integer,kotlin.Unit> content);
method @com.google.accompanist.pager.ExperimentalPagerApi public static float calculateCurrentOffsetForPage(com.google.accompanist.pager.PagerScope, int page);
}

@com.google.accompanist.pager.ExperimentalPagerApi public final class PagerDefaults {
method @androidx.compose.runtime.Composable public androidx.compose.foundation.gestures.FlingBehavior flingBehavior(com.google.accompanist.pager.PagerState state, optional androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> decayAnimationSpec, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec);
method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.foundation.gestures.FlingBehavior rememberPagerFlingConfig(com.google.accompanist.pager.PagerState state, optional androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> decayAnimationSpec, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec);
method @androidx.compose.runtime.Composable @dev.chrisbanes.snapper.ExperimentalSnapperApi public androidx.compose.foundation.gestures.FlingBehavior flingBehavior(com.google.accompanist.pager.PagerState state, optional androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> decayAnimationSpec, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec, optional kotlin.jvm.functions.Function1<? super dev.chrisbanes.snapper.SnapperLayoutInfo,java.lang.Float> maximumFlingDistance, optional float endContentPadding);
method public kotlin.jvm.functions.Function1<dev.chrisbanes.snapper.SnapperLayoutInfo,java.lang.Float> getSinglePageFlingDistance();
property public final kotlin.jvm.functions.Function1<dev.chrisbanes.snapper.SnapperLayoutInfo,java.lang.Float> singlePageFlingDistance;
field public static final com.google.accompanist.pager.PagerDefaults INSTANCE;
}

Expand Down Expand Up @@ -51,12 +52,8 @@ package com.google.accompanist.pager {
}

public final class PagerStateKt {
method @Deprecated @androidx.compose.runtime.Composable @com.google.accompanist.pager.ExperimentalPagerApi public static inline com.google.accompanist.pager.PagerState rememberPagerState(@IntRange(from=0) int pageCount, optional @IntRange(from=0) int initialPage, optional @FloatRange(from=0.0, to=1.0) float initialPageOffset, optional @IntRange(from=1) int initialOffscreenLimit, optional boolean infiniteLoop);
method @androidx.compose.runtime.Composable @com.google.accompanist.pager.ExperimentalPagerApi public static com.google.accompanist.pager.PagerState rememberPagerState(optional @IntRange(from=0) int initialPage);
}

public final class SnappingFlingBehaviorKt {
}

}

4 changes: 3 additions & 1 deletion pager/build.gradle
Expand Up @@ -82,7 +82,9 @@ android {
}

dependencies {
implementation libs.compose.foundation.foundation
api libs.compose.foundation.foundation
api libs.snapper

implementation libs.napier

// ======================
Expand Down
Expand Up @@ -55,6 +55,7 @@ class InstrumentedHorizontalPagerTest(
.combineWithParameters(1f)
// contentPadding
.combineWithParameters(
PaddingValues(horizontal = 0.dp), // Fill
PaddingValues(end = 32.dp), // Alignment.Start
PaddingValues(horizontal = 32.dp), // Alignment.Center
PaddingValues(start = 32.dp), // Alignment.End
Expand Down
Expand Up @@ -49,6 +49,7 @@ class InstrumentedVerticalPagerTest(
fun data() = parameterizedParams()
// contentPadding
.combineWithParameters(
PaddingValues(vertical = 0.dp), // Fill
PaddingValues(bottom = 32.dp), // Alignment.Top
PaddingValues(vertical = 32.dp), // Alignment.Center
PaddingValues(top = 32.dp), // Alignment.Bottom
Expand Down
68 changes: 49 additions & 19 deletions pager/src/main/java/com/google/accompanist/pager/Pager.kt
Expand Up @@ -25,6 +25,7 @@ import androidx.compose.foundation.gestures.FlingBehavior
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyRow
Expand All @@ -40,8 +41,15 @@ import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.unit.dp
import dev.chrisbanes.snapper.ExperimentalSnapperApi
import dev.chrisbanes.snapper.SnapOffsets
import dev.chrisbanes.snapper.SnapperFlingBehavior
import dev.chrisbanes.snapper.SnapperFlingBehaviorDefaults
import dev.chrisbanes.snapper.SnapperLayoutInfo
import dev.chrisbanes.snapper.rememberSnapperFlingBehavior
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.filter

Expand All @@ -59,34 +67,47 @@ annotation class ExperimentalPagerApi
*/
@ExperimentalPagerApi
object PagerDefaults {
/**
* The default implementation for the `maximumFlingDistance` parameter of
* [flingBehavior] which limits the fling distance to a single page.
*/
@ExperimentalSnapperApi
@Suppress("MemberVisibilityCanBePrivate")
val singlePageFlingDistance: (SnapperLayoutInfo) -> Float = { layoutInfo ->
// We can scroll up to the scrollable size of the lazy layout
layoutInfo.endScrollOffset - layoutInfo.startScrollOffset.toFloat()
}

/**
* Remember the default [FlingBehavior] that represents the scroll curve.
*
* Please remember to provide the correct [endContentPadding] if supplying your own
* [FlingBehavior] to [VerticalPager] or [HorizontalPager]. See those functions for how they
* calculate the value.
*
* @param state The [PagerState] to update.
* @param decayAnimationSpec The decay animation spec to use for decayed flings.
* @param snapAnimationSpec The animation spec to use when snapping.
* @param maximumFlingDistance Block which returns the maximum fling distance in pixels.
* @param endContentPadding The amount of content padding on the end edge of the lazy list
* in pixels (end/bottom depending on the scrolling direction).
*/
@Composable
@ExperimentalSnapperApi
fun flingBehavior(
state: PagerState,
decayAnimationSpec: DecayAnimationSpec<Float> = rememberSplineBasedDecay(),
snapAnimationSpec: AnimationSpec<Float> = SnappingFlingBehaviorDefaults.snapAnimationSpec,
): FlingBehavior = rememberSnappingFlingBehavior(
snapAnimationSpec: AnimationSpec<Float> = SnapperFlingBehaviorDefaults.SpringAnimationSpec,
maximumFlingDistance: (SnapperLayoutInfo) -> Float = singlePageFlingDistance,
endContentPadding: Dp = 0.dp,
): FlingBehavior = rememberSnapperFlingBehavior(
lazyListState = state.lazyListState,
snapOffsetForItem = SnapOffsets.Start, // pages are full width, so we use the simplest
decayAnimationSpec = decayAnimationSpec,
snapAnimationSpec = snapAnimationSpec,
)

@Deprecated(
"Replaced with PagerDefaults.flingBehavior()",
ReplaceWith("PagerDefaults.flingBehavior(state, decayAnimationSpec, snapAnimationSpec)")
springAnimationSpec = snapAnimationSpec,
maximumFlingDistance = maximumFlingDistance,
endContentPadding = endContentPadding,
)
@Composable
fun rememberPagerFlingConfig(
state: PagerState,
decayAnimationSpec: DecayAnimationSpec<Float> = rememberSplineBasedDecay(),
snapAnimationSpec: AnimationSpec<Float> = SnappingFlingBehaviorDefaults.snapAnimationSpec,
): FlingBehavior = flingBehavior(state, decayAnimationSpec, snapAnimationSpec)
}

/**
Expand All @@ -108,6 +129,7 @@ object PagerDefaults {
* @param content a block which describes the content. Inside this block you can reference
* [PagerScope.currentPage] and other properties in [PagerScope].
*/
@OptIn(ExperimentalSnapperApi::class)
@ExperimentalPagerApi
@Composable
fun HorizontalPager(
Expand All @@ -116,10 +138,13 @@ fun HorizontalPager(
state: PagerState = rememberPagerState(),
reverseLayout: Boolean = false,
itemSpacing: Dp = 0.dp,
flingBehavior: FlingBehavior = PagerDefaults.flingBehavior(state),
contentPadding: PaddingValues = PaddingValues(0.dp),
verticalAlignment: Alignment.Vertical = Alignment.CenterVertically,
flingBehavior: FlingBehavior = PagerDefaults.flingBehavior(
state = state,
endContentPadding = contentPadding.calculateEndPadding(LayoutDirection.Ltr),
),
key: ((page: Int) -> Any)? = null,
contentPadding: PaddingValues = PaddingValues(0.dp),
content: @Composable PagerScope.(page: Int) -> Unit,
) {
Pager(
Expand Down Expand Up @@ -156,6 +181,7 @@ fun HorizontalPager(
* @param content a block which describes the content. Inside this block you can reference
* [PagerScope.currentPage] and other properties in [PagerScope].
*/
@OptIn(ExperimentalSnapperApi::class)
@ExperimentalPagerApi
@Composable
fun VerticalPager(
Expand All @@ -164,10 +190,13 @@ fun VerticalPager(
state: PagerState = rememberPagerState(),
reverseLayout: Boolean = false,
itemSpacing: Dp = 0.dp,
flingBehavior: FlingBehavior = PagerDefaults.flingBehavior(state),
contentPadding: PaddingValues = PaddingValues(0.dp),
horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally,
flingBehavior: FlingBehavior = PagerDefaults.flingBehavior(
state = state,
endContentPadding = contentPadding.calculateBottomPadding(),
),
key: ((page: Int) -> Any)? = null,
contentPadding: PaddingValues = PaddingValues(0.dp),
content: @Composable PagerScope.(page: Int) -> Unit,
) {
Pager(
Expand Down Expand Up @@ -206,7 +235,8 @@ internal fun Pager(
// Provide our PagerState with access to the SnappingFlingBehavior animation target
// TODO: can this be done in a better way?
state.flingAnimationTarget = {
(flingBehavior as? SnappingFlingBehavior)?.animationTarget
@OptIn(ExperimentalSnapperApi::class)
(flingBehavior as? SnapperFlingBehavior)?.animationTarget
}

LaunchedEffect(count) {
Expand Down
22 changes: 1 addition & 21 deletions pager/src/main/java/com/google/accompanist/pager/PagerState.kt
Expand Up @@ -42,24 +42,6 @@ import io.github.aakira.napier.Napier
import kotlin.math.absoluteValue
import kotlin.math.roundToInt

@Deprecated(
"Replaced with rememberPagerState(initialPage) and count parameter on Pager composables",
ReplaceWith("rememberPagerState(initialPage)"),
level = DeprecationLevel.ERROR,
)
@Suppress("UNUSED_PARAMETER", "NOTHING_TO_INLINE")
@ExperimentalPagerApi
@Composable
inline fun rememberPagerState(
@IntRange(from = 0) pageCount: Int,
@IntRange(from = 0) initialPage: Int = 0,
@FloatRange(from = 0.0, to = 1.0) initialPageOffset: Float = 0f,
@IntRange(from = 1) initialOffscreenLimit: Int = 1,
infiniteLoop: Boolean = false
): PagerState {
return rememberPagerState(initialPage = initialPage)
}

/**
* Creates a [PagerState] that is remembered across compositions.
*
Expand Down Expand Up @@ -97,9 +79,7 @@ class PagerState(
private var _currentPage by mutableStateOf(currentPage)

private val currentLayoutPageInfo: LazyListItemInfo?
get() = lazyListState.layoutInfo.visibleItemsInfo.asSequence()
.filter { it.offset <= 0 && it.offset + it.size > 0 }
.lastOrNull()
get() = lazyListState.layoutInfo.visibleItemsInfo.lastOrNull { it.offset <= 0 }

private val currentLayoutPageOffset: Float
get() = currentLayoutPageInfo?.let { current ->
Expand Down

0 comments on commit 7e1bc6c

Please sign in to comment.