3

Currently I'm trying to develop a special kind of image carousel which basically has two rows (one at the top with the images, and another one (shorter in width) at the bottom that use names for reference)

The thing is that I need to scroll the top row faster than the bottom one to achieve this with Jetpack Compose (can be done in regular Android with just some scroll listeners)

I was able to achieve scroll the rows simultaneously but they are scrolling at the same speed. I need to scroll the first one twice as fast to achieve the effect I want. Here's the code I tried.

val scrollState = rememberScrollState()
Row(modifier = Modifier.horizontalScroll(scrollState)) {
   Images()
}
Row(modifier = Modifier.horizontalScroll(scrollState)) {
   Legend()
}
axebau24
  • 93
  • 8

1 Answers1

2

If you only need one Row to be scrollable, you can create new ScrollState each time.

val scrollState = rememberScrollState()
Row(modifier = Modifier.horizontalScroll(scrollState)) {
    repeat(100) {
        Text(it.toString())
    }
}
Row(
    modifier = Modifier.horizontalScroll(
        ScrollState(scrollState.value * 2),
        enabled = false
    )
) {
    repeat(200) {
        Text(it.toString())
    }
}

Note that this solution may not be the best in terms of performance, as it creates a class on each pixel scrolled.


An other solution is to sync them with LaunchedEffect. It'll also allow you both way scrolling synchronization.

@Composable
fun TestScreen(
) {
    val scrollState1 = rememberScrollState()
    Row(
        horizontalArrangement = Arrangement.spacedBy(10.dp),
        modifier = Modifier.horizontalScroll(scrollState1)
    ) {
        repeat(100) {
            Text(it.toString())
        }
    }
    val scrollState2 = rememberScrollState()
    Row(
        horizontalArrangement = Arrangement.spacedBy(10.dp),
        modifier = Modifier.horizontalScroll(scrollState2)
    ) {
        repeat(200) {
            Text(it.toString())
        }
    }
    SyncScrollTwoWay(scrollState1, scrollState2, 2f)
}

@Composable
fun SyncScrollTwoWay(scrollState1: ScrollState, scrollState2: ScrollState, multiplier: Float) {
    SyncScroll(scrollState1, scrollState2, multiplier)
    SyncScroll(scrollState2, scrollState1, 1 / multiplier)
}

@Composable
fun SyncScroll(scrollState1: ScrollState, scrollState2: ScrollState, multiplier: Float) {
    if (scrollState1.isScrollInProgress) {
        LaunchedEffect(scrollState1.value) {
            scrollState2.scrollTo((scrollState1.value * multiplier).roundToInt())
        }
    }
}

But it also have one downside: there'll be little delay in scrolling, so the second scroll view will always be one frame behind the scrolling one.

Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
  • 1
    solutions provided in this https://stackoverflow.com/questions/69608517/scroll-two-lazy-scrollers-together/73632577?noredirect=1#comment132728332_73632577 are better – anshul Jan 28 '23 at 18:36