https://kotlinlang.org logo
#compose
Title
# compose
t

Tony Kazanjian

03/02/2021, 5:22 PM
I am trying to add an auto-scroll effect to the example of a Pager seen in the Jetcaster sample (https://github.com/android/compose-samples/blob/main/Jetcaster/app/src/main/java/com/example/jetcaster/util/Pager.kt), but it seems like adding a
ScrollableState
to a modifier that also includes a
DraggableState
has no effect (also, seems like whichever modifier comes next pretty much overwrites the previous one). Code in thread
1
Let's say I declare my scrollableState and want the whole composable to scroll by it's screen length as a launch effect:
Copy code
val scrollable = rememberScrollableState {dy ->
        Log.d(TAG, "Scrolled to : $dy")
        dy
    }

    LaunchedEffect(state) {
        delay(2000)
        scrollable.animateScrollBy(1080f)
    }
Then, I'm adding the scrollable to the whole layout modifier
Copy code
Layout(
        content = {
            val minPage = (state.currentPage - offscreenLimit).coerceAtLeast(state.minPage)
            val maxPage = (state.currentPage + offscreenLimit).coerceAtMost(state.maxPage)

            for (page in minPage..maxPage) {
                val pageData = PageData(page)
                val scope = PagerScope(state, page)
                key(pageData) {
                    Box(contentAlignment = Alignment.Center, modifier = pageData) {
                        scope.pageContent()
                    }
                }
            }
        },
        modifier = modifier.draggable(
            orientation = Orientation.Horizontal,
            onDragStarted = {
                state.selectionState = PagerState.SelectionState.Undecided
            },
            onDragStopped = { velocity ->
                coroutineScope.launch {
                    // Velocity is in pixels per second, but we deal in percentage offsets, so we
                    // need to scale the velocity to match
                    state.fling(velocity / pageSize)
                }
            },
            state = rememberDraggableState { dy ->
                coroutineScope.launch {
                    with(state) {
                        val pos = pageSize * currentPageOffset
                        val max = if (currentPage == minPage) 0 else pageSize * offscreenLimit
                        val min = if (currentPage == maxPage) 0 else -pageSize * offscreenLimit
                        val newPos = (pos + dy).coerceIn(min.toFloat(), max.toFloat())
                        snapToOffset(newPos / pageSize)
                    }
                }
            },
        ).scrollable(scrollable, Orientation.Horizontal)
    ) { measurables, constraints ->
        layout(constraints.maxWidth, constraints.maxHeight) {
            val currentPage = state.currentPage
            val offset = state.currentPageOffset
            val childConstraints = constraints.copy(minWidth = 0, minHeight = 0)

            measurables
                .map {
                    it.measure(childConstraints) to it.page
                }
                .forEach { (placeable, page) ->
                    // TODO: current this centers each page. We should investigate reading
                    //  gravity modifiers on the child, or maybe as a param to Pager.
                    val xCenterOffset = (constraints.maxWidth - placeable.width) / 2
                    val yCenterOffset = (constraints.maxHeight - placeable.height) / 2

                    if (currentPage == page) {
                        pageSize = placeable.width
                    }

                    val xItemOffset = ((page + offset - currentPage) * placeable.width).roundToInt()

                    placeable.place(
                        x = xCenterOffset + xItemOffset,
                        y = yCenterOffset
                    )
                }
        }
    }
I'm not sure if I have to do anything in the
rememberScrollableState
lambda to ensure the composable gets scrolled (it gets fired when I drag, but nothing happens onscreen). It does not take a suspend function so I can't do the same thing as the draggableState does with the animatable offset, etc. Is there any way I can scroll this thing without the draggable modifier?
And will the layout actually scroll with measurements like this?
4 Views