最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

android - How to compose items that are offscreen with LazyColumn or LazyRow? - Stack Overflow

programmeradmin1浏览0评论

This is a share your knowledge, Q&A-style question to create a follow up answer to create a Lazy list(Column, Row, Grid) that can compose more items than default behavior allows which is the next item in scroll direction when you start scrolling.

In cases, where some items take more time to load, like a video or downloading an image, so you might want to have more items that are loaded or entering composition offscreen.

LazyRow(
    modifier = Modifier.fillMaxWidth(),
    horizontalArrangement = Arrangement.spacedBy(8.dp),
    contentPadding = PaddingValues(16.dp),
) {
    items(30) {
        var loading by remember {
            mutableStateOf(true)
        }

        LaunchedEffect(Unit) {
            println("Composing First LazyRow item: $it")
            delay(1000)
            loading = false
        }
        MyRow(itemWidth, loading, it)
    }
}

@Composable
private fun MyRow(itemWidth: Dp, loading: Boolean, it: Int) {
    Box(
        modifier = Modifier
            .size(itemWidth, 100.dp)
            .background(if (loading) Color.Red else Color.Green, RoundedCornerShape(16.dp))
            .padding(16.dp)
    ) {
        Text("Row $it", fontSize = 26.sp, color = Color.White)
    }
}

It takes 1 second for this item to full be ready but if you scroll so how to make this or more items composed?

Actually i'm also looking how to store states of Composables like moveableContentOf does in a Row/Column but it doesn't work with LazyColumn for some reason? It would also another approach to storing Composable state that left composition and show with last state before showing on screen.

This is a share your knowledge, Q&A-style question to create a follow up answer to create a Lazy list(Column, Row, Grid) that can compose more items than default behavior allows which is the next item in scroll direction when you start scrolling.

In cases, where some items take more time to load, like a video or downloading an image, so you might want to have more items that are loaded or entering composition offscreen.

LazyRow(
    modifier = Modifier.fillMaxWidth(),
    horizontalArrangement = Arrangement.spacedBy(8.dp),
    contentPadding = PaddingValues(16.dp),
) {
    items(30) {
        var loading by remember {
            mutableStateOf(true)
        }

        LaunchedEffect(Unit) {
            println("Composing First LazyRow item: $it")
            delay(1000)
            loading = false
        }
        MyRow(itemWidth, loading, it)
    }
}

@Composable
private fun MyRow(itemWidth: Dp, loading: Boolean, it: Int) {
    Box(
        modifier = Modifier
            .size(itemWidth, 100.dp)
            .background(if (loading) Color.Red else Color.Green, RoundedCornerShape(16.dp))
            .padding(16.dp)
    ) {
        Text("Row $it", fontSize = 26.sp, color = Color.White)
    }
}

It takes 1 second for this item to full be ready but if you scroll so how to make this or more items composed?

Actually i'm also looking how to store states of Composables like moveableContentOf does in a Row/Column but it doesn't work with LazyColumn for some reason? It would also another approach to storing Composable state that left composition and show with last state before showing on screen.

Share Improve this question edited Nov 18, 2024 at 20:35 Thracian asked Nov 18, 2024 at 17:40 ThracianThracian 68.2k21 gold badges213 silver badges397 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 4

One of the ways to compose more offscreen items than one that is composed when scroll starts is increasing LazyColumn/Row viewport size bigger than parent Composable or device dimensions.

Since, creating dimensions bigger than parent Composable is not permitted by default one option is to use Modifier.layout

Text("Compose 4 items offscreen in right direction")

LazyRow(
    modifier = Modifier.fillMaxWidth().layout { measurable, constraints ->
        val width = constraints.maxWidth + 4 * itemWidth.roundToPx()
        val wrappedConstraints = constraints.copy(minWidth = width, maxWidth = width)

        val placeable = measurable.measure(wrappedConstraints)

        layout(
            placeable.width, placeable.height
        ) {
            val xPos = (placeable.width - constraints.maxWidth) / 2
            placeable.placeRelative(xPos, 0)
        }
    },
    horizontalArrangement = Arrangement.spacedBy(8.dp),
    contentPadding = PaddingValues(16.dp)
) {
    items(30) {
        var loading by remember {
            mutableStateOf(true)
        }

        LaunchedEffect(Unit) {
            println("Composing Second LazyRow item: $it")
            delay(1000)
            loading = false
        }
        MyRow(itemWidth, loading, it)
    }
}

Results 4 more items are composed in right scroll while one while scrolled left.

If you wish to make fling gesture, gesture that scrolls after user lifts off finger, to scroll after is slower so items are more like to be loaded. NestedScrollConnection to limit fling speed does the trick. Limiting initial velocity between

- threshold < available < threshold

So fling gesture is limited in both directions.

@Composable
fun rememberFlingNestedScrollConnection() = remember {
    object : NestedScrollConnection {

        override suspend fun onPreFling(available: Velocity): Velocity {
            val threshold = 3000f
            val availableX = available.x
            val consumed = if (availableX > threshold) {
                availableX - threshold
            } else if (availableX < -threshold) {
                availableX + threshold
            } else {
                0f
            }
            return Velocity(consumed, 0f)
        }

    }
}

  LazyRow(
            modifier = Modifier
                .nestedScroll(rememberFlingNestedScrollConnection())
 )

In both cases LazyColumn is set to start position and viewport overflows right side of the screen. But what about making it overflow both sides so it can compose more items in both scroll direction.

For that positioning 0 to does the trick but you won't be able to see first 4 and last 4 items and to make them visible need to add contentPadding as

Text("Compose 4 items offscreen in both scroll directions")

LazyRow(
    modifier = Modifier
        .nestedScroll(rememberFlingNestedScrollConnection())
        .fillMaxWidth()
        .layout { measurable, constraints ->
            val width = constraints.maxWidth + 8 * itemWidth.roundToPx()
            val wrappedConstraints = constraints.copy(minWidth = width, maxWidth = width)

            val placeable = measurable.measure(wrappedConstraints)

            layout(
                placeable.width, placeable.height
            ) {
                placeable.placeRelative(0, 0)
            }
        },
    horizontalArrangement = Arrangement.spacedBy(8.dp),
    contentPadding = PaddingValues(
        vertical = 16.dp,
        horizontal = 16.dp + itemWidth * 4
    )
) {
    items(30) {
        var loading by remember {
            mutableStateOf(true)
        }

        LaunchedEffect(Unit) {
            println("Composing Forth LazyRow item: $it")
            delay(1000)
            loading = false
        }
        MyRow(itemWidth, loading, it)
    }
}

发布评论

评论列表(0)

  1. 暂无评论