New Pager implementation (#8323)
Minimal implementation using new Compose SnapFlingBehavior
This commit is contained in:
parent
5b12c144da
commit
f9c25b350e
7 changed files with 186 additions and 10 deletions
|
@ -176,8 +176,6 @@ dependencies {
|
|||
implementation(compose.accompanist.webview)
|
||||
implementation(compose.accompanist.swiperefresh)
|
||||
implementation(compose.accompanist.flowlayout)
|
||||
implementation(compose.accompanist.pager.core)
|
||||
implementation(compose.accompanist.pager.indicators)
|
||||
implementation(compose.accompanist.permissions)
|
||||
|
||||
implementation(androidx.paging.runtime)
|
||||
|
|
182
app/src/main/java/eu/kanade/presentation/components/Pager.kt
Normal file
182
app/src/main/java/eu/kanade/presentation/components/Pager.kt
Normal file
|
@ -0,0 +1,182 @@
|
|||
package eu.kanade.presentation.components
|
||||
|
||||
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.wrapContentSize
|
||||
import androidx.compose.foundation.lazy.LazyListItemInfo
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
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.Modifier
|
||||
import androidx.compose.ui.util.fastMaxBy
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
|
||||
@Composable
|
||||
fun HorizontalPager(
|
||||
count: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
state: PagerState = rememberPagerState(),
|
||||
key: ((page: Int) -> Any)? = null,
|
||||
contentPadding: PaddingValues = PaddingValues(),
|
||||
verticalAlignment: Alignment.Vertical = Alignment.CenterVertically,
|
||||
userScrollEnabled: Boolean = true,
|
||||
content: @Composable BoxScope.(page: Int) -> Unit,
|
||||
) {
|
||||
Pager(
|
||||
count = count,
|
||||
modifier = modifier,
|
||||
state = state,
|
||||
isVertical = false,
|
||||
key = key,
|
||||
contentPadding = contentPadding,
|
||||
verticalAlignment = verticalAlignment,
|
||||
userScrollEnabled = userScrollEnabled,
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
|
||||
@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 = rememberSnapFlingBehavior(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 = rememberSnapFlingBehavior(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 var _currentPage by mutableStateOf(currentPage)
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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]) },
|
||||
)
|
||||
}
|
||||
}
|
|
@ -16,8 +16,6 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.google.accompanist.pager.HorizontalPager
|
||||
import com.google.accompanist.pager.rememberPagerState
|
||||
import eu.kanade.tachiyomi.widget.TachiyomiBottomNavigationView
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
|
|
@ -15,12 +15,12 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import com.google.accompanist.pager.rememberPagerState
|
||||
import eu.kanade.core.prefs.PreferenceMutableState
|
||||
import eu.kanade.domain.category.model.Category
|
||||
import eu.kanade.domain.library.model.LibraryDisplayMode
|
||||
import eu.kanade.domain.library.model.LibraryManga
|
||||
import eu.kanade.presentation.components.SwipeRefresh
|
||||
import eu.kanade.presentation.components.rememberPagerState
|
||||
import eu.kanade.presentation.library.LibraryState
|
||||
import eu.kanade.tachiyomi.ui.library.LibraryItem
|
||||
import kotlinx.coroutines.delay
|
||||
|
|
|
@ -10,11 +10,11 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import com.google.accompanist.pager.HorizontalPager
|
||||
import com.google.accompanist.pager.PagerState
|
||||
import eu.kanade.core.prefs.PreferenceMutableState
|
||||
import eu.kanade.domain.library.model.LibraryDisplayMode
|
||||
import eu.kanade.domain.library.model.LibraryManga
|
||||
import eu.kanade.presentation.components.HorizontalPager
|
||||
import eu.kanade.presentation.components.PagerState
|
||||
import eu.kanade.tachiyomi.ui.library.LibraryItem
|
||||
|
||||
@Composable
|
||||
|
|
|
@ -10,11 +10,11 @@ import androidx.compose.runtime.mutableStateOf
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.google.accompanist.pager.PagerState
|
||||
import eu.kanade.domain.category.model.Category
|
||||
import eu.kanade.presentation.category.visualName
|
||||
import eu.kanade.presentation.components.AppStateBanners
|
||||
import eu.kanade.presentation.components.Divider
|
||||
import eu.kanade.presentation.components.PagerState
|
||||
import eu.kanade.presentation.components.TabIndicator
|
||||
import eu.kanade.presentation.components.TabText
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
|
@ -19,6 +19,4 @@ material-icons = { module = "androidx.compose.material:material-icons-extended"
|
|||
accompanist-webview = { module = "com.google.accompanist:accompanist-webview", version.ref = "accompanist" }
|
||||
accompanist-swiperefresh = { module = "com.google.accompanist:accompanist-swiperefresh", version.ref = "accompanist" }
|
||||
accompanist-flowlayout = { module = "com.google.accompanist:accompanist-flowlayout", version.ref = "accompanist" }
|
||||
accompanist-pager-core = { module = "com.google.accompanist:accompanist-pager", version.ref = "accompanist" }
|
||||
accompanist-pager-indicators = { module = "com.google.accompanist:accompanist-pager-indicators", version.ref = "accompanist" }
|
||||
accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanist" }
|
||||
|
|
Reference in a new issue