Sticky headers 与 Jetpack Compose 中的分页库

Sticky headers with paging library in Jetpack Compose

我目前正在使用新的 Jetpack compose UI 工具包,我非常喜欢它。我无法弄清楚的一件事是如何在分页库填充的 LazyColumn 中使用 stickyHeadersdocumentation 中的 non-paging 示例是:

val grouped = contacts.groupBy { it.firstName[0] }

fun ContactsList(grouped: Map<Char, List<Contact>>) {
    LazyColumn {
        grouped.forEach { (initial, contactsForInitial) ->
            stickyHeader {
                CharacterHeader(initial)
            }

            items(contactsForInitial) { contact ->
                ContactListItem(contact)
            }
        }
    }
}

由于我使用的是分页库,所以我无法使用 groupedBy 所以我尝试在 PagingData 和 insert/create 上使用 insertSeparators 函数 header我自己是这样的(请忽略遗留的Date代码,它只是为了测试):

// On my flow
.insertSeparators { before, after ->
        when {
            before == null -> ListItem.HeaderItem(after?.workout?.time ?: 0)
            after == null -> ListItem.HeaderItem(before.workout.time)
            (Date(before.workout.time).day != Date(after.workout.time).day) ->
                ListItem.HeaderItem(before.workout.time)
            // Return null to avoid adding a separator between two items.
            else -> null
        }
    }

// In my composeable
LazyColumn {
    items(workoutItems) {
        when(it) {
            is ListItem.HeaderItem -> this@LazyColumn.stickyHeader { Header(it) }
            is ListItem.SongItem -> WorkoutItem(it)
        }
    }
}

但这会生成我所有项目的列表,并且 header 项目会附加在末尾。任何想法在使用分页库时使用 stickyHeader 函数的正确方法是什么?

我通过查看 items 函数的源代码使其工作:您不得在 items 函数中调用 stickyHeader。根本不需要修改 PagingData 流程。只需使用 peek 获取下一项而不触发重新加载,然后对其进行布局:

LazyColumn {
    var lastWorkout: Workout? = null

    for(index in workoutItems.indices) {
        val workout = workoutItems.peek(index)

        if(lastWorkout?.time != workout?.time) stickyHeader { Header(workout) }
        item { WorkoutItem(workoutItems.getAsState(index).value) } // triggers reload

        lastWorkout = workout 
    }
}

我认为您代码中的问题是您从 LazyItemScope.

内部调用 this@LazyColumn

我也用 insertSeparators 进行了试验并得到了这个有效的 LazyColumn 代码:

LazyColumn {
    for (index in 0 until photos.itemCount) {
        when (val peekData = photos.peek(index)) {
            is String? -> stickyHeader {
                Text(
                    text = (photos.getAsState(index).value as? String).orEmpty(),
                )
            }
            is Photo? -> item(key = { peekData?.id }) {
                val photo = photos.getAsState(index).value as? Photo
                ...
            }
        }
    }
}