Skip to content

Instantly share code, notes, and snippets.

@fourlastor
Created November 25, 2022 14:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fourlastor/cfc7d7319696e37ea3073ed88a4d38a5 to your computer and use it in GitHub Desktop.
Save fourlastor/cfc7d7319696e37ea3073ed88a4d38a5 to your computer and use it in GitHub Desktop.
Sin based lazy layout
package io.github.fourlastor.keys.lazylayout
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.checkScrollableContainerConstraints
import androidx.compose.foundation.clipScrollableContainer
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.scrollable
import androidx.compose.foundation.lazy.layout.LazyLayout
import androidx.compose.foundation.lazy.layout.LazyLayoutItemProvider
import androidx.compose.foundation.lazy.layout.LazyLayoutMeasureScope
import androidx.compose.foundation.rememberScrollState
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.MeasureResult
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.sp
import kotlin.math.sin
@Preview
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun SineLayout() {
val scrollState = rememberScrollState()
val itemProvider = CustomItemProvider()
LazyLayout(
modifier = Modifier
.clipScrollableContainer(Orientation.Vertical) // used to enforce finite constraints and still have scrollable content
.scrollable(state = scrollState, orientation = Orientation.Vertical),
itemProvider = itemProvider,
measurePolicy = rememberMeasurePolicy(scrollState, itemProvider)
)
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun rememberMeasurePolicy(scrollState: ScrollState, itemProvider: LazyLayoutItemProvider): LazyLayoutMeasureScope.(Constraints) -> MeasureResult =
remember {
{ constraints ->
// ensure that the max height isn't infinite
checkScrollableContainerConstraints(constraints, Orientation.Vertical)
// measure all children
val placeables = sequence {
for (i in 0 until itemProvider.itemCount) {
yieldAll(measure(i, constraints).iterator())
}
}.toList()
// layout all children until we run out of space
layout(constraints.maxWidth, constraints.maxHeight) {
var y = 0
var i = 0
while (y < constraints.maxHeight && i < placeables.size) {
val placeable = placeables[i]
val factor = (sin(y.toDouble()) + 1) / 2
val x = ((factor * constraints.maxWidth * 0.6) + 0.2 * constraints.maxWidth).toInt()
placeable.placeRelative(x, y)
y += placeable.height
i++
}
}
}
}
@OptIn(ExperimentalFoundationApi::class)
class CustomItemProvider : LazyLayoutItemProvider {
override val itemCount: Int
get() = 500
@Composable
override fun Item(index: Int) {
Text(text = "$index", fontSize = 44.sp)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment