mirror of
https://github.com/mihonapp/mihon.git
synced 2024-12-25 18:38:23 +01:00
Replace our custom Pager (#9494)
Turns out that changing the pagerSnapDistance is enough to achieve the same result.
This commit is contained in:
parent
8df9bce1b4
commit
96defd6b05
@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.ColumnScope
|
|||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.wrapContentSize
|
import androidx.compose.foundation.layout.wrapContentSize
|
||||||
|
import androidx.compose.foundation.pager.rememberPagerState
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.MoreVert
|
import androidx.compose.material.icons.filled.MoreVert
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
@ -31,7 +32,6 @@ import kotlinx.coroutines.launch
|
|||||||
import tachiyomi.presentation.core.components.HorizontalPager
|
import tachiyomi.presentation.core.components.HorizontalPager
|
||||||
import tachiyomi.presentation.core.components.material.Divider
|
import tachiyomi.presentation.core.components.material.Divider
|
||||||
import tachiyomi.presentation.core.components.material.TabIndicator
|
import tachiyomi.presentation.core.components.material.TabIndicator
|
||||||
import tachiyomi.presentation.core.components.rememberPagerState
|
|
||||||
|
|
||||||
object TabbedDialogPaddings {
|
object TabbedDialogPaddings {
|
||||||
val Horizontal = 24.dp
|
val Horizontal = 24.dp
|
||||||
@ -84,7 +84,7 @@ fun TabbedDialog(
|
|||||||
|
|
||||||
HorizontalPager(
|
HorizontalPager(
|
||||||
modifier = Modifier.animateContentSize(),
|
modifier = Modifier.animateContentSize(),
|
||||||
count = tabTitles.size,
|
pageCount = tabTitles.size,
|
||||||
state = pagerState,
|
state = pagerState,
|
||||||
verticalAlignment = Alignment.Top,
|
verticalAlignment = Alignment.Top,
|
||||||
) { page ->
|
) { page ->
|
||||||
|
@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.calculateEndPadding
|
|||||||
import androidx.compose.foundation.layout.calculateStartPadding
|
import androidx.compose.foundation.layout.calculateStartPadding
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.pager.rememberPagerState
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.SnackbarHost
|
import androidx.compose.material3.SnackbarHost
|
||||||
import androidx.compose.material3.SnackbarHostState
|
import androidx.compose.material3.SnackbarHostState
|
||||||
@ -25,7 +26,6 @@ import tachiyomi.presentation.core.components.HorizontalPager
|
|||||||
import tachiyomi.presentation.core.components.material.Scaffold
|
import tachiyomi.presentation.core.components.material.Scaffold
|
||||||
import tachiyomi.presentation.core.components.material.TabIndicator
|
import tachiyomi.presentation.core.components.material.TabIndicator
|
||||||
import tachiyomi.presentation.core.components.material.TabText
|
import tachiyomi.presentation.core.components.material.TabText
|
||||||
import tachiyomi.presentation.core.components.rememberPagerState
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TabbedScreen(
|
fun TabbedScreen(
|
||||||
@ -82,7 +82,7 @@ fun TabbedScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
HorizontalPager(
|
HorizontalPager(
|
||||||
count = tabs.size,
|
pageCount = tabs.size,
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
state = state,
|
state = state,
|
||||||
verticalAlignment = Alignment.Top,
|
verticalAlignment = Alignment.Top,
|
||||||
|
@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.PaddingValues
|
|||||||
import androidx.compose.foundation.layout.calculateEndPadding
|
import androidx.compose.foundation.layout.calculateEndPadding
|
||||||
import androidx.compose.foundation.layout.calculateStartPadding
|
import androidx.compose.foundation.layout.calculateStartPadding
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.pager.rememberPagerState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@ -22,7 +23,6 @@ import tachiyomi.domain.category.model.Category
|
|||||||
import tachiyomi.domain.library.model.LibraryDisplayMode
|
import tachiyomi.domain.library.model.LibraryDisplayMode
|
||||||
import tachiyomi.domain.library.model.LibraryManga
|
import tachiyomi.domain.library.model.LibraryManga
|
||||||
import tachiyomi.presentation.core.components.material.PullRefresh
|
import tachiyomi.presentation.core.components.material.PullRefresh
|
||||||
import tachiyomi.presentation.core.components.rememberPagerState
|
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@ -60,8 +60,10 @@ fun LibraryContent(
|
|||||||
var isRefreshing by remember(pagerState.currentPage) { mutableStateOf(false) }
|
var isRefreshing by remember(pagerState.currentPage) { mutableStateOf(false) }
|
||||||
|
|
||||||
if (showPageTabs && categories.size > 1) {
|
if (showPageTabs && categories.size > 1) {
|
||||||
if (categories.size <= pagerState.currentPage) {
|
LaunchedEffect(categories) {
|
||||||
pagerState.currentPage = categories.size - 1
|
if (categories.size <= pagerState.currentPage) {
|
||||||
|
pagerState.scrollToPage(categories.size - 1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
LibraryTabs(
|
LibraryTabs(
|
||||||
categories = categories,
|
categories = categories,
|
||||||
|
@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.PaddingValues
|
|||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.pager.PagerState
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@ -22,7 +23,6 @@ import eu.kanade.tachiyomi.ui.library.LibraryItem
|
|||||||
import tachiyomi.domain.library.model.LibraryDisplayMode
|
import tachiyomi.domain.library.model.LibraryDisplayMode
|
||||||
import tachiyomi.domain.library.model.LibraryManga
|
import tachiyomi.domain.library.model.LibraryManga
|
||||||
import tachiyomi.presentation.core.components.HorizontalPager
|
import tachiyomi.presentation.core.components.HorizontalPager
|
||||||
import tachiyomi.presentation.core.components.PagerState
|
|
||||||
import tachiyomi.presentation.core.screens.EmptyScreen
|
import tachiyomi.presentation.core.screens.EmptyScreen
|
||||||
import tachiyomi.presentation.core.util.plus
|
import tachiyomi.presentation.core.util.plus
|
||||||
|
|
||||||
@ -43,7 +43,7 @@ fun LibraryPager(
|
|||||||
onClickContinueReading: ((LibraryManga) -> Unit)?,
|
onClickContinueReading: ((LibraryManga) -> Unit)?,
|
||||||
) {
|
) {
|
||||||
HorizontalPager(
|
HorizontalPager(
|
||||||
count = pageCount,
|
pageCount = pageCount,
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
state = state,
|
state = state,
|
||||||
verticalAlignment = Alignment.Top,
|
verticalAlignment = Alignment.Top,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package eu.kanade.presentation.library.components
|
package eu.kanade.presentation.library.components
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.pager.PagerState
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.ScrollableTabRow
|
import androidx.compose.material3.ScrollableTabRow
|
||||||
import androidx.compose.material3.Tab
|
import androidx.compose.material3.Tab
|
||||||
@ -8,7 +9,6 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import eu.kanade.presentation.category.visualName
|
import eu.kanade.presentation.category.visualName
|
||||||
import tachiyomi.domain.category.model.Category
|
import tachiyomi.domain.category.model.Category
|
||||||
import tachiyomi.presentation.core.components.PagerState
|
|
||||||
import tachiyomi.presentation.core.components.material.Divider
|
import tachiyomi.presentation.core.components.material.Divider
|
||||||
import tachiyomi.presentation.core.components.material.TabIndicator
|
import tachiyomi.presentation.core.components.material.TabIndicator
|
||||||
import tachiyomi.presentation.core.components.material.TabText
|
import tachiyomi.presentation.core.components.material.TabText
|
||||||
|
@ -1,300 +1,57 @@
|
|||||||
package tachiyomi.presentation.core.components
|
package tachiyomi.presentation.core.components
|
||||||
|
|
||||||
import androidx.compose.foundation.gestures.FlingBehavior
|
|
||||||
import androidx.compose.foundation.gestures.Orientation
|
import androidx.compose.foundation.gestures.Orientation
|
||||||
import androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider
|
|
||||||
import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.BoxScope
|
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.wrapContentSize
|
import androidx.compose.foundation.pager.PageSize
|
||||||
import androidx.compose.foundation.lazy.LazyListItemInfo
|
import androidx.compose.foundation.pager.PagerDefaults
|
||||||
import androidx.compose.foundation.lazy.LazyListLayoutInfo
|
import androidx.compose.foundation.pager.PagerSnapDistance
|
||||||
import androidx.compose.foundation.lazy.LazyListState
|
import androidx.compose.foundation.pager.PagerState
|
||||||
import androidx.compose.foundation.lazy.LazyRow
|
import androidx.compose.foundation.pager.rememberPagerState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.Stable
|
|
||||||
import androidx.compose.runtime.derivedStateOf
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.saveable.Saver
|
|
||||||
import androidx.compose.runtime.saveable.listSaver
|
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.runtime.snapshotFlow
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.Density
|
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
||||||
import androidx.compose.ui.util.fastForEach
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.util.fastMaxBy
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.util.fastSumBy
|
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
|
||||||
import kotlin.math.abs
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Horizontal Pager with custom SnapFlingBehavior for a more natural swipe feeling
|
||||||
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun HorizontalPager(
|
fun HorizontalPager(
|
||||||
count: Int,
|
pageCount: Int,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
state: PagerState = rememberPagerState(),
|
state: PagerState = rememberPagerState(),
|
||||||
key: ((page: Int) -> Any)? = null,
|
contentPadding: PaddingValues = PaddingValues(0.dp),
|
||||||
contentPadding: PaddingValues = PaddingValues(),
|
pageSize: PageSize = PageSize.Fill,
|
||||||
|
beyondBoundsPageCount: Int = 0,
|
||||||
|
pageSpacing: Dp = 0.dp,
|
||||||
verticalAlignment: Alignment.Vertical = Alignment.CenterVertically,
|
verticalAlignment: Alignment.Vertical = Alignment.CenterVertically,
|
||||||
userScrollEnabled: Boolean = true,
|
userScrollEnabled: Boolean = true,
|
||||||
content: @Composable BoxScope.(page: Int) -> Unit,
|
reverseLayout: Boolean = false,
|
||||||
|
key: ((index: Int) -> Any)? = null,
|
||||||
|
pageNestedScrollConnection: NestedScrollConnection = PagerDefaults.pageNestedScrollConnection(
|
||||||
|
Orientation.Horizontal,
|
||||||
|
),
|
||||||
|
pageContent: @Composable (page: Int) -> Unit,
|
||||||
) {
|
) {
|
||||||
Pager(
|
androidx.compose.foundation.pager.HorizontalPager(
|
||||||
count = count,
|
pageCount = pageCount,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
state = state,
|
state = state,
|
||||||
isVertical = false,
|
|
||||||
key = key,
|
|
||||||
contentPadding = contentPadding,
|
contentPadding = contentPadding,
|
||||||
|
pageSize = pageSize,
|
||||||
|
beyondBoundsPageCount = beyondBoundsPageCount,
|
||||||
|
pageSpacing = pageSpacing,
|
||||||
verticalAlignment = verticalAlignment,
|
verticalAlignment = verticalAlignment,
|
||||||
|
flingBehavior = PagerDefaults.flingBehavior(
|
||||||
|
state = state,
|
||||||
|
pagerSnapDistance = PagerSnapDistance.atMost(0),
|
||||||
|
),
|
||||||
userScrollEnabled = userScrollEnabled,
|
userScrollEnabled = userScrollEnabled,
|
||||||
content = content,
|
reverseLayout = reverseLayout,
|
||||||
|
key = key,
|
||||||
|
pageNestedScrollConnection = pageNestedScrollConnection,
|
||||||
|
pageContent = pageContent,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun Pager(
|
|
||||||
count: Int,
|
|
||||||
modifier: Modifier,
|
|
||||||
state: PagerState,
|
|
||||||
isVertical: Boolean,
|
|
||||||
key: ((page: Int) -> Any)?,
|
|
||||||
contentPadding: PaddingValues,
|
|
||||||
userScrollEnabled: Boolean,
|
|
||||||
verticalAlignment: Alignment.Vertical = Alignment.CenterVertically,
|
|
||||||
horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally,
|
|
||||||
content: @Composable BoxScope.(page: Int) -> Unit,
|
|
||||||
) {
|
|
||||||
LaunchedEffect(count) {
|
|
||||||
state.currentPage = minOf(count - 1, state.currentPage).coerceAtLeast(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
LaunchedEffect(state) {
|
|
||||||
snapshotFlow { state.mostVisiblePageLayoutInfo?.index }
|
|
||||||
.distinctUntilChanged()
|
|
||||||
.collect { state.updateCurrentPageBasedOnLazyListState() }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isVertical) {
|
|
||||||
LazyColumn(
|
|
||||||
modifier = modifier,
|
|
||||||
state = state.lazyListState,
|
|
||||||
contentPadding = contentPadding,
|
|
||||||
horizontalAlignment = horizontalAlignment,
|
|
||||||
verticalArrangement = Arrangement.aligned(verticalAlignment),
|
|
||||||
userScrollEnabled = userScrollEnabled,
|
|
||||||
flingBehavior = rememberLazyListSnapFlingBehavior(lazyListState = state.lazyListState),
|
|
||||||
) {
|
|
||||||
items(
|
|
||||||
count = count,
|
|
||||||
key = key,
|
|
||||||
) { page ->
|
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillParentMaxHeight()
|
|
||||||
.wrapContentSize(),
|
|
||||||
) {
|
|
||||||
content(this, page)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LazyRow(
|
|
||||||
modifier = modifier,
|
|
||||||
state = state.lazyListState,
|
|
||||||
contentPadding = contentPadding,
|
|
||||||
verticalAlignment = verticalAlignment,
|
|
||||||
horizontalArrangement = Arrangement.aligned(horizontalAlignment),
|
|
||||||
userScrollEnabled = userScrollEnabled,
|
|
||||||
flingBehavior = rememberLazyListSnapFlingBehavior(lazyListState = state.lazyListState),
|
|
||||||
) {
|
|
||||||
items(
|
|
||||||
count = count,
|
|
||||||
key = key,
|
|
||||||
) { page ->
|
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillParentMaxWidth()
|
|
||||||
.wrapContentSize(),
|
|
||||||
) {
|
|
||||||
content(this, page)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun rememberPagerState(
|
|
||||||
initialPage: Int = 0,
|
|
||||||
) = rememberSaveable(saver = PagerState.Saver) {
|
|
||||||
PagerState(currentPage = initialPage)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Stable
|
|
||||||
class PagerState(
|
|
||||||
currentPage: Int = 0,
|
|
||||||
) {
|
|
||||||
init { check(currentPage >= 0) { "currentPage cannot be less than zero" } }
|
|
||||||
|
|
||||||
val lazyListState = LazyListState(firstVisibleItemIndex = currentPage)
|
|
||||||
|
|
||||||
private val pageSize: Int
|
|
||||||
get() = visiblePages.firstOrNull()?.size ?: 0
|
|
||||||
|
|
||||||
private var _currentPage by mutableStateOf(currentPage)
|
|
||||||
|
|
||||||
private val layoutInfo: LazyListLayoutInfo
|
|
||||||
get() = lazyListState.layoutInfo
|
|
||||||
|
|
||||||
private val visiblePages: List<LazyListItemInfo>
|
|
||||||
get() = layoutInfo.visibleItemsInfo
|
|
||||||
|
|
||||||
var currentPage: Int
|
|
||||||
get() = _currentPage
|
|
||||||
set(value) {
|
|
||||||
if (value != _currentPage) {
|
|
||||||
_currentPage = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val mostVisiblePageLayoutInfo: LazyListItemInfo?
|
|
||||||
get() {
|
|
||||||
val layoutInfo = lazyListState.layoutInfo
|
|
||||||
return layoutInfo.visibleItemsInfo.fastMaxBy {
|
|
||||||
val start = maxOf(it.offset, 0)
|
|
||||||
val end = minOf(
|
|
||||||
it.offset + it.size,
|
|
||||||
layoutInfo.viewportEndOffset - layoutInfo.afterContentPadding,
|
|
||||||
)
|
|
||||||
end - start
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val closestPageToSnappedPosition: LazyListItemInfo?
|
|
||||||
get() = visiblePages.fastMaxBy {
|
|
||||||
-abs(
|
|
||||||
calculateDistanceToDesiredSnapPosition(
|
|
||||||
layoutInfo,
|
|
||||||
it,
|
|
||||||
SnapAlignmentStartToStart,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val currentPageOffsetFraction: Float by derivedStateOf {
|
|
||||||
val currentPagePositionOffset = closestPageToSnappedPosition?.offset ?: 0
|
|
||||||
val pageUsedSpace = pageSize.toFloat()
|
|
||||||
if (pageUsedSpace == 0f) {
|
|
||||||
// Default to 0 when there's no info about the page size yet.
|
|
||||||
0f
|
|
||||||
} else {
|
|
||||||
((-currentPagePositionOffset) / (pageUsedSpace)).coerceIn(
|
|
||||||
MinPageOffset,
|
|
||||||
MaxPageOffset,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun updateCurrentPageBasedOnLazyListState() {
|
|
||||||
mostVisiblePageLayoutInfo?.let {
|
|
||||||
currentPage = it.index
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun animateScrollToPage(page: Int) {
|
|
||||||
lazyListState.animateScrollToItem(index = page)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun scrollToPage(page: Int) {
|
|
||||||
lazyListState.scrollToItem(index = page)
|
|
||||||
updateCurrentPageBasedOnLazyListState()
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val Saver: Saver<PagerState, *> = listSaver(
|
|
||||||
save = { listOf(it.currentPage) },
|
|
||||||
restore = { PagerState(it[0]) },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private const val MinPageOffset = -0.5f
|
|
||||||
private const val MaxPageOffset = 0.5f
|
|
||||||
internal val SnapAlignmentStartToStart: (layoutSize: Float, itemSize: Float) -> Float =
|
|
||||||
{ _, _ -> 0f }
|
|
||||||
|
|
||||||
// https://android.googlesource.com/platform/frameworks/support/+/refs/changes/78/2160778/35/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapLayoutInfoProvider.kt
|
|
||||||
private fun lazyListSnapLayoutInfoProvider(
|
|
||||||
lazyListState: LazyListState,
|
|
||||||
positionInLayout: (layoutSize: Float, itemSize: Float) -> Float = { layoutSize, itemSize ->
|
|
||||||
layoutSize / 2f - itemSize / 2f
|
|
||||||
},
|
|
||||||
) = object : SnapLayoutInfoProvider {
|
|
||||||
|
|
||||||
private val layoutInfo: LazyListLayoutInfo
|
|
||||||
get() = lazyListState.layoutInfo
|
|
||||||
|
|
||||||
// Single page snapping is the default
|
|
||||||
override fun Density.calculateApproachOffset(initialVelocity: Float): Float = 0f
|
|
||||||
|
|
||||||
override fun Density.calculateSnappingOffsetBounds(): ClosedFloatingPointRange<Float> {
|
|
||||||
var lowerBoundOffset = Float.NEGATIVE_INFINITY
|
|
||||||
var upperBoundOffset = Float.POSITIVE_INFINITY
|
|
||||||
|
|
||||||
layoutInfo.visibleItemsInfo.fastForEach { item ->
|
|
||||||
val offset =
|
|
||||||
calculateDistanceToDesiredSnapPosition(layoutInfo, item, positionInLayout)
|
|
||||||
|
|
||||||
// Find item that is closest to the center
|
|
||||||
if (offset <= 0 && offset > lowerBoundOffset) {
|
|
||||||
lowerBoundOffset = offset
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find item that is closest to center, but after it
|
|
||||||
if (offset >= 0 && offset < upperBoundOffset) {
|
|
||||||
upperBoundOffset = offset
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return lowerBoundOffset.rangeTo(upperBoundOffset)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun Density.calculateSnapStepSize(): Float = with(layoutInfo) {
|
|
||||||
if (visibleItemsInfo.isNotEmpty()) {
|
|
||||||
visibleItemsInfo.fastSumBy { it.size } / visibleItemsInfo.size.toFloat()
|
|
||||||
} else {
|
|
||||||
0f
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun rememberLazyListSnapFlingBehavior(lazyListState: LazyListState): FlingBehavior {
|
|
||||||
val snappingLayout = remember(lazyListState) { lazyListSnapLayoutInfoProvider(lazyListState) }
|
|
||||||
return rememberSnapFlingBehavior(snappingLayout)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun calculateDistanceToDesiredSnapPosition(
|
|
||||||
layoutInfo: LazyListLayoutInfo,
|
|
||||||
item: LazyListItemInfo,
|
|
||||||
positionInLayout: (layoutSize: Float, itemSize: Float) -> Float,
|
|
||||||
): Float {
|
|
||||||
val containerSize =
|
|
||||||
with(layoutInfo) { singleAxisViewportSize - beforeContentPadding - afterContentPadding }
|
|
||||||
|
|
||||||
val desiredDistance =
|
|
||||||
positionInLayout(containerSize.toFloat(), item.size.toFloat())
|
|
||||||
|
|
||||||
val itemCurrentPosition = item.offset
|
|
||||||
return itemCurrentPosition - desiredDistance
|
|
||||||
}
|
|
||||||
|
|
||||||
private val LazyListLayoutInfo.singleAxisViewportSize: Int
|
|
||||||
get() = if (orientation == Orientation.Vertical) viewportSize.height else viewportSize.width
|
|
||||||
|
Loading…
Reference in New Issue
Block a user