Jetpack Compose:嵌套 LazyColumn / LazyRow

ade*_*111 47 android kotlin android-recyclerview android-jetpack-compose android-jetpack-compose-list

我读过类似的主题,但找不到满意的结果:

我的用例是:创建一个评论列表(数百个项目),并可以显示对每个评论的回复(每个项目数百个项目)。

目前不可能LazyColumn在另一个内部进行嵌套LazyColumn,因为 Compose 会抛出异常:

java.lang.IllegalStateException:垂直可滚动组件是用无限最大高度约束测量的,这是不允许的。常见原因之一是嵌套布局,例如 LazyColumn 和 Column(Modifier.verticalScroll())。如果您想在项目列表之前添加标题,请在 LazyColumn 范围内的主 items() 之前添加一个标题作为单独的 item() 。发生这种情况可能还有其他原因:您的 ComposeView 被添加到具有一定权重的 LinearLayout 中、您应用了 Modifier.wrapContentSize(unbounded = true) 或编写了自定义布局。请尝试消除滚动容器上方层次结构中无限约束的来源。

上面的链接(以及我想到的其他链接)提供的解决方案是:

  • 对内部使用固定高度LazyColumn- 我无法使用它,因为每个项目可以有不同的高度(例如:单行注释与多行注释)。
  • 在内部使用普通Columns (不是惰性)LazyColumn- 性能方面它不如惰性,当使用 Android Studio 的 Profiler 和 500 个元素的列表时,正常Column会在我的应用程序中使用 350MB 的 RAM,而使用惰性 则需要 220-240MB Composables。因此它无法正常回收。
  • 使用FlowColumn伴奏者 - 我没有看到这个和正常之间有任何性能差异,Column所以请参见上面。
  • 展平列表的数据源(将评论和回复显示为“主要”评论,并且仅进行 UI 更改以区分它们) - 这是我当前正在使用的内容,但是当我为此功能添加更多复杂性时,它会阻止一些新功能要求予以落实。
  • 使用Compose 1.2.0 中新添加的参数禁用内部LazyColumn滚动- 不幸的是,它会引发相同的错误,并且这是预期的行为(请参阅此处)。 userScrollEnabled
  • 使用其他方式阻止滚动(也以编程方式阻止滚动)-同样的错误。
  • 使用其他LazyColumn参数.height(),如wrapContentHeight()或 using IntrinsicSize.Min- 相同的错误。

还有其他想法如何解决这个问题吗?特别是考虑到可以在 Apple 的 SwiftUI 中嵌套惰性组件而无需限制高度。

Dav*_*d A 5

我有一个类似的用例,并且我有一个解决方案,该解决方案LazyColumn对我来说效果很好且性能良好,其想法是将您的数据视为具有LazyColumn不同类型元素的大型数据。由于评论回复现在是单独的列表项,因此您必须首先展平数据,使其成为一个大列表或多个列表。现在,对于子注释,您只需在前面添加一些填充,否则它们将显示为单独的惰性项目。我还使用了 aLazyVerticalGrid而不是 LazyColumn,因为我必须在最后显示图库图片网格,您可能不需要它,但如果需要,则必须在其他地方使用 span 选项,如下所示。

你会得到这样的东西:

LazyVerticalGrid(
        modifier = Modifier
            .padding(6.dp),
        columns = GridCells.Fixed(3)
    ) {
        item(span = { GridItemSpan(3) }) {
            ComposableTitle()
        }
        items(
            items = flattenedCommentList,
            key = { it.commentId },
            span = { GridItemSpan(3) }) { comment ->
                ShowCommentComposable(comment) 
                //here subcomments will have extra padding in front
        }
        item(span = { GridItemSpan(3) }) {
            ComposableGalleryTitle()
        }
        items(items = imageGalleryList,
            key = { it.imageId }) { image ->
                ShowImageInsideGrid(image) //images in 3 column grid
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 -1

我在这个函数中解决了这个问题

@Composable
fun NestedLazyList(
    modifier: Modifier = Modifier,
    outerState: LazyListState = rememberLazyListState(),
    innerState: LazyListState = rememberLazyListState(),
    outerContent: LazyListScope.() -> Unit,
    innerContent: LazyListScope.() -> Unit,
) {
    val scope = rememberCoroutineScope()
    val innerFirstVisibleItemIndex by remember {
        derivedStateOf {
            innerState.firstVisibleItemIndex
        }
    }
    SideEffect {
        if (outerState.layoutInfo.visibleItemsInfo.size == 2 && innerState.layoutInfo.totalItemsCount == 0)
            scope.launch { outerState.scrollToItem(outerState.layoutInfo.totalItemsCount) }
        println("outer ${outerState.layoutInfo.visibleItemsInfo.map { it.index }}")
        println("inner ${innerState.layoutInfo.visibleItemsInfo.map { it.index }}")
    }

    BoxWithConstraints(
        modifier = modifier
            .scrollable(
                state = rememberScrollableState {
                    scope.launch {
                        val toDown = it <= 0
                        if (toDown) {
                            if (outerState.run { firstVisibleItemIndex == layoutInfo.totalItemsCount - 1 }) {
                                Log.i(TAG, "NestedLazyList: down inner")
                                innerState.scrollBy(-it)
                            } else {
                                Log.i(TAG, "NestedLazyList: down outer")
                                outerState.scrollBy(-it)
                            }
                        } else {
                            if (innerFirstVisibleItemIndex == 0 && innerState.firstVisibleItemScrollOffset == 0) {
                                Log.i(TAG, "NestedLazyList: up outer")
                                outerState.scrollBy(-it)
                            } else {
                                Log.i(TAG, "NestedLazyList: up inner")
                                innerState.scrollBy(-it)
                            }
                        }
                    }
                    it
                },
                Orientation.Vertical,
            )
    ) {
        LazyColumn(
            userScrollEnabled = false,
            state = outerState,
            modifier = Modifier
                .heightIn(maxHeight)
        ) {
            outerContent()
            item {
                LazyColumn(
                    state = innerState,
                    userScrollEnabled = false,
                    modifier = Modifier
                        .height(maxHeight)

                ) {
                    innerContent()
                }
            }
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

我所做的就是:
首先,我使用 ,将内部lazyList的高度设置为父视图的高度BoxWithConstraints,这让内部列表填充屏幕,而不会破坏lazy概念。
然后,我通过禁用延迟滚动来控制滚动,并使父级可滚动,以确定滚动何时影响父级列表以及子级何时应滚动。
芽,当父尺寸改变时,这仍然有一些错误,在我的例子中,我逃脱了这个SideEffect


归档时间:

查看次数:

23408 次

最近记录:

1 年,10 月 前