RecyclerView - 连续列布局

3

我正在尝试创建一个布局,其中项目将在列中依次跟随(见下图),但我还没有达到目标。我已经尝试使用GridLayoutManagerStaggeredGridLayoutManager - 但两者都无法提供项目流动到另一列并以这种方式跟随的功能。在我的当前尝试中,我正在尝试使用FlexboxLayoutManager,但我得到的结果总是单个项目的列,而不是项目之间相互跟随。

期望的行为是项目一个接一个地排列,当滚动视图的高度不足以显示整个项目视图时,它应该被拆分到下一列。

以下是我现在正在尝试的:

mBinding?.activeRecycler?.layoutManager = FlexboxLayoutManager(context).apply {
            flexDirection = FlexDirection.COLUMN
            flexWrap = FlexWrap.WRAP
            alignItems = AlignItems.STRETCH
        }

我每列只需要一个项目。

尝试实现这个:

输入图像描述


2
交错网格布局管理器。 - Nicolas
编辑了问题,StaggeredGridLayoutManager提供不了我想要的东西 - 具体而言,如果高度更大,则项目会流动到下一列。 - Rainmaker
一个LayoutManager应该如何将一个项目分成两个部分,就像您的第三个项目的图形一样?它只能从布局的角度操作现有的项目。您能否更具体地说明您正在显示什么类型的内容以及当其在中间分裂时应该如何呈现,例如第三个项目? - ymindstorm
@Rainmaker,我来澄清一下我的观点:当LayoutManager完成后,最终发生的事情是,您的视图接收到一个矩形区域在屏幕上绘制自己,具有特定的宽度和高度。此外,您可以对生成的视图执行特定的转换。但是从LayoutManager无法向项目视图通信“请将自己分成两半”。 - ymindstorm
我建议尝试将单个视图项制作得非常小,以便一个逻辑项由多个构建块RecyclerView项组成。这样,您可以使用例如StaggeredGridViewManager来在项目中使用自然的“断点”。为了更好地帮助您编写代码,需要更具体的视图内容信息。 - ymindstorm
显示剩余3条评论
3个回答

1
我非常怀疑这是可能的。RecyclerView、它的适配器和布局管理器都没有设计来改变视图的基本形式。
这意味着“分割”一个视图是不可能的。RecyclerView 的设计是理解同时有多少个视图在视野中,只创建那么多的视图,然后将底层对象分别绑定到这些视图上。
这意味着 RecyclerView 不会“将一个视图切成两半,并在不同的位置显示它们”。
唯一可能实现您所描述的情况的方式是,如果布局管理器专门设计为在多个视图和位置中显示一个项目。这将允许它按您所描述的方式显示。但是,正如我所说的,这意味着中间的视图 3 和最后一列的视图 3 是绑定到同一对象或其副本的两个视图。(或者有人完全疯了并分裂了视图,我怀疑)。
我不相信任何标准布局管理器能够做到这一点,而且我怀疑即使没有相应地修改适配器,您也无法实现这一点,至少需要修改适配器。因为适配器基本上完成绑定,所以没有它的帮助,标准布局管理器将无法执行上述双重绑定。

话虽如此,这只是通过视图和其组件的原则所做出的一个很好的猜测。我没有阅读每个布局管理器的源代码或完整描述。


是的,我正在考虑将视图分成两个部分,并使用相同的ID进行绑定,以定义它是相同的对象,因为这不仅涉及显示问题,还涉及处理点击事件。 - Rainmaker
没错。因此,使用通常的布局管理器基本上是不可能进行分割的,即使你设法将这种行为破解到它们中,我怀疑这并不意味着你可以像通常一样处理视图。 - Benjamin Basmaci
但是我甚至找不到可以提供从左到右垂直列分割视图的布局管理器。就好像在Android中从未有人尝试过这样做一样。 - Rainmaker
正确,所以我猜你得自己构建它。要么布局管理器和适配器,甚至是一个全新的视图。也许是多个RecyclerView的复合体。无论如何,这可能非常困难,并超出任何合理的SO答案。 - Benjamin Basmaci
有些答案确实包含了相当多的代码,并回答了一些非常困难的问题,特别是在悬赏部分。所以我还是抱有希望的 :) - Rainmaker

1
我理解你的问题是这样的:你有一个包含文本字段的当前数据列表,想要以正常方式显示它们,在recycler view中每个列表项对应一个视图项。
但基于你的设计需求,这似乎不可能。
我的想法是这样的: 你需要创建一个新列表,将前一个列表的一个项目分成2、3或更多项目,以适应你的列数。
private fun demo() {

    val originalList = listOf<String>()
    val newScreenSpecificList = mutableListOf<String>()

    val columnHeight = 3//example number of lines
    val columnWidth = 10//example number of chars
    var columnsIndex = 0//index of column
    var currentColumnHeight = 0 // current column filled height

    originalList.forEach {
        if (currentColumnHeight + getTextHeight(it, columnWidth) <= columnHeight) {
            newScreenSpecificList.add(it)
            currentColumnHeight = currentColumnHeight + getTextHeight(it, columnWidth)
        } else {
            //here is the part where your text is bigger then your column height so you need to divide it
            val textForSpaceLeft = getTextForSpaceLeft(it, columnHeight - currentColumnHeight)
            newScreenSpecificList.add(textForSpaceLeft)
            currentColumnHeight = currentColumnHeight + getTextHeight(textForSpaceLeft, columnWidth)

            if (currentColumnHeight >= columnHeight) {
                columnsIndex++
            }

            if (getTextForNewSpaceLeft(it, columnHeight - currentColumnHeight)){
                //continue to repeat logic for new column
                //...
            }

        }

        if (currentColumnHeight >= columnHeight) {
            columnsIndex++
        }
    }

}

private fun getTextForSpaceLeft(it: String, spaceLeft: Int): String {
    return "it"// return text for the available space
}

private fun getTextForNewSpaceLeft(it: String, spaceLeft: Int): String {
    return "new column also"// return text left for the new available space
}

private fun getTextHeight(text: String, columnWidth: Int): Int {
    return 2//todo your logic to convert text length to number of lines needed for a specific width of the column
}

现在你需要继续这个逻辑,它并不完整,希望这能帮到你。

谢谢你的回答,看起来一切都要将物品分成几个部分,并在高度不允许时将块移动到新列中,我明白这个逻辑需要进一步完善。 - Rainmaker

-1

我猜你的问题可能与在适配器中创建的项目的LayoutParams有关。也许项目中的高度设置为match_parent。你可以尝试在适配器的onCreateViewHolder/onBindViewHolder中更改itemViews的LayoutParams。或者,如果项目的高度有点棘手难以计算,你可以创建一个customView并在onMeasure中尝试计算高度,并将高度设置为wrap_content

尝试将项目的高度设置为wrap_content,或者如果你想在代码中实现,可以像这样:

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FlexItemViewHolder {
    val infatedView = ...
    
    infatedView.layoutParams = FlexboxLayoutManager.LayoutParams(FlexboxLayoutManager.LayoutParams.WRAP_CONTENT,     FlexboxLayoutManager.LayoutParams.WRAP_CONTENT)
    infatedView.addView(textView)
    return FlexItemViewHolder(f)
}

1
当项目过长时,这如何帮助它们流入下一列? - ymindstorm
高度为wrap_content,问题是使用wrap_content时它可能会比整个屏幕高。 - Rainmaker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接