2

我正在尝试实现一个底部带有按钮的列表,如下所示:

在此处输入图像描述

由于我不知道列表中有多少项目,我使用的是LazyColumn. 底部的按钮,只有在列表没有填满整个屏幕时才应该放在那里,如果是,按钮应该向下移动并成为列表中的最后一项。

像这样移动里面的按钮LazyColumn

LazyColumn(...) {
    items(items) { item -> ...}
    item { Button() } 
}

给出以下内容:

在此处输入图像描述

我试图在两者之间添加一个SpacerwithfillMaxHeight()修饰符作为一个项目,但它没有改变。

我还尝试在列中添加 theLazyColumnButtoninside:

Column {
   LazyColumn(
        modifier = Modifier.weight(1f)
   ) {
        items(items) { item -> ...}
   }
   Button()
}

但这只会将按钮锚定在底部,就好像它Column是一个LinearLayout.

考虑到这一点,是否可以对齐LazyColumn's 项目之一,使其始终位于底部?或者,添加某种填充可用区域的空间?

4

2 回答 2

2

@Arthur指出了一个很好的解决方案,使用verticalArrangement.

这是一个更通用的解决方案:

同时,您可以获得LazyColumn使用的大小onSizeChanged,最后显示元素的底部位置layoutInfo,并根据此信息添加到或Button作为附近的子元素。itemLazyColumnColumn

@Composable
fun CustomLazyColumn(
    modifier: Modifier,
    items: List<String>,
    button: @Composable () -> Unit
) {
    val state = rememberLazyListState()
    var listHeight by remember { mutableStateOf(0) }
    val lastItemBottom by remember(items) {
        derivedStateOf {
            state.layoutInfo
                .visibleItemsInfo
                .lastOrNull()
                ?.let { it.offset + it.size }
                ?: 0
        }
    }
    val shouldMoveButton = lastItemBottom < listHeight
    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
        modifier = modifier.fillMaxHeight()
    ) {
        LazyColumn(
            state = state,
            modifier = Modifier
                .weight(1f)
                .fillMaxWidth()
                .onSizeChanged { listHeight = it.height }
        ) {
            items(items) { Text(it) }
            if (!shouldMoveButton) {
                item {
                    Box(
                        contentAlignment = Alignment.Center,
                        modifier = Modifier.fillMaxWidth()
                    ) {
                        button()
                    }
                }
            }
        }
        if (shouldMoveButton) {
            button()
        }
    }
}

用法:


@Composable
fun TestView() {
    val items = remember {
        List(3) { it.toString() }.toMutableStateList()
    }
    Column {
        Button(onClick = {
            if (items.count() == 3) {
                items.addAll(List(100) { it.toString() })
            } else {
                items.removeRange(3, items.indices.count())
            }
        }) {
            Text("Add items")
        }
        CustomLazyColumn(
            items = items,
            button = {
                Button(
                    onClick = {},
                ) {
                    Text("Button")
                }
            },
            modifier = Modifier.fillMaxHeight(),
        )
    }
}

结果:

此方法的缺点是,当您删除许多项目时,例如将计数从 100 减少到 3,Button将在单个帧的错误位置上可见,直到它可以向下移动。当然,这不是最常见的情况。

完美地应该使用 来完成SubcomposeLayout,它位于 之下LazyColumn,但是实施这个需要很多时间。

于 2021-08-28T06:09:52.763 回答
1

正如这个已关闭的问题中所述,可以使用LazyColumn'sverticalArrangement参数通过实现来对齐底部的最后一项Arrangement.Vertical

LazyColumn(verticalArrangement = remember {
    object : Arrangement.Vertical {
        override fun Density.arrange(
            totalSize: Int,
            sizes: IntArray,
            outPositions: IntArray
        ) {
            var currentOffset = 0
            sizes.forEachIndexed { index, size -> 
                if (index == sizes.lastIndex) {
                    outPositions[index] = totalSize - size
                } else {
                    outPositions[index] = currentOffset
                    currentOffset += size
                }
            }
        }
    }
})

如果你想spacedByverticalArrangement参数上使用它,就像我一样,你可以使用以下内容:

fun spacedByWithFooter(space: Dp) = object : Arrangement.Vertical {

    override val spacing = space

    override fun Density.arrange(
        totalSize: Int,
        sizes: IntArray,
        outPositions: IntArray,
    ) {
        if (sizes.isEmpty()) return
        val spacePx = space.roundToPx()

        var occupied = 0
        var lastSpace = 0

        sizes.forEachIndexed { index, size ->

            if (index == sizes.lastIndex) {
                outPositions[index] = totalSize - size
            } else {
                outPositions[index] = min(occupied, totalSize - size)
            }
            lastSpace = min(spacePx, totalSize - outPositions[index] - size)
            occupied = outPositions[index] + size + lastSpace
        }
        occupied -= lastSpace
    }
}
于 2021-09-15T16:29:11.057 回答