Sticky headers 与 Jetpack Compose 中的分页库
Sticky headers with paging library in Jetpack Compose
我目前正在使用新的 Jetpack compose UI 工具包,我非常喜欢它。我无法弄清楚的一件事是如何在分页库填充的 LazyColumn
中使用 stickyHeaders
。 documentation 中的 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
...
}
}
}
}
我目前正在使用新的 Jetpack compose UI 工具包,我非常喜欢它。我无法弄清楚的一件事是如何在分页库填充的 LazyColumn
中使用 stickyHeaders
。 documentation 中的 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
...
}
}
}
}