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

OG

02/07/2022, 8:59 PM
Hello everyone, 👋 I was wondering if anyone has any suggestions on the best way to approach building a curved/arced horizontally scrolling list of items with "Snap to Center" behavior + having the beginning of the list positioned in the center of the screen. I've found an article that does something like what I am trying to do, but with RecyclerView + custom layout manager. Posted gif from link below for quick reference as well.
I don't think this can be easily done with just using a LazyRow + custom modifiers. I think compose viewpager will give me snap to center behavior but not the curved/arced scrolling/path.
I am thinking the way to go is to create a custom Layout composable where I handle all the measuring + layout + offset + scrolling manually.
Possibly leveraging how LazyRow works under the hood but the difference being how each child measurable is laid out (using some x offset from center to determine its y offset)
and then the last part would be figuring out how to handle the snap to center and list starting from the center of the screen
would appreciate any feedback/thoughts/sanity check on my approach or any other approach here. thanks!
z

Zun

02/07/2022, 9:14 PM
I actually have no idea, but what about horizontal pager with custom transition animation?
👍 1
o

OG

02/07/2022, 9:27 PM
I think with that you can apply probably fade in/out and expand/contract of each item but I dont believe you would be able to achieve what I am looking for here.. I could be wrong though. I will have to take a closer look at the source code
d

dewildte

02/07/2022, 10:53 PM
Math probably would help here
lol
🧌
o

OG

02/07/2022, 11:20 PM
Lol yeah that's what I'm thinking.. especially if I go with custom Layout. Should've paid more attention in calc in the college days I guess.... 🙃
r

raghunandan

06/08/2022, 2:16 AM
Did you figure it out. Looking for solution on similar lines.
s

steelahhh

06/21/2022, 4:36 AM
I feel like ScalingLazyColumn could be a source of inspiration for achieving this
r

raghunandan

07/24/2022, 4:02 PM
https://fvilarino.medium.com/creating-a-circular-list-in-jetpack-compose-29924c70e3e0. Can use this for inspiration. Seems custom layout and bit of maths should work. In case you do figure out can you post the gist somewhere for others to see?
https://gist.github.com/raghunandankavi2010/133b9a821b483cea584d5c7de658ba08. Horizontal scrolling with scaling and alpha. However offset of y nees to be calculated for your case i guess.
o

OG

07/31/2022, 8:44 PM
Nice solution! This use case I was looking for is actually pretty easy to do with horizontal swiping pager, with a custom transition of offsetting y and scaling. The outer ring that always is around the centered item is actually a composable that sits above the pager and is centered.
I'll see if I can find some time to pull it out into a demo app or gist and share here, for anyone else that comes this use case.
r

raghunandan

08/01/2022, 2:51 AM
Yeah. Interesting. I tried with pager. But since we need to place more items in one page i had to hardcode some padding values to center the initial. I may be wrong. can you post a gist of pager solution as well.
Copy code
@OptIn(ExperimentalPagerApi::class)
@Composable
fun PagerDemo(modifier: Modifier = Modifier) {

    HorizontalPager(
        count = 30,
        // Not sure if i can remove this hardcoded value.
        contentPadding = PaddingValues(horizontal = 160.dp),
        itemSpacing = 8.dp,
        modifier = modifier
    ) { page ->
        Card(
            modifier = Modifier
                .graphicsLayer {
                    val pageOffset = calculateCurrentOffsetForPage(page).absoluteValue
                    // Set the item alpha and scale based on the distance from the center
                    val percentFromCenter = 1.0f - (pageOffset / (5f / 2f))
                    val itemScale = 0.5f + (percentFromCenter * 0.5f).coerceIn(0f, 1f)
                    val opacity = 0.25f + (percentFromCenter * 0.75f).coerceIn(0f, 1f)

                    alpha = opacity
                    scaleY = itemScale
                    scaleX = itemScale
                }
                .clip(CircleShape)
                .size(50.dp)
        ) {
            Image(
                painter = painterResource(R.drawable.ic_launcher_background),
                contentDescription = null,
                modifier = Modifier
                    .size(50.dp)

            )
        }
    }
}
Copy code
contentPadding = PaddingValues(horizontal = 160.dp)
This is something that is hardcoded would like to replace this.
o

OG

08/01/2022, 2:42 PM
Use a parent
BoxWithConstraints
to get the available width, you know your item width (e.g. 50dp), and then you can do some easy math to figure out the content padding without hardcoding, so the first item is centered
r

raghunandan

08/01/2022, 2:49 PM
Got it. Thanks!