在 Jetpack Compose 上创建 UI 时,需要为 LazyRow 添加粘性标题。但当前的实现是作为行元素嵌入到列表中的。
我希望粘性标题位于元素上方,如下所示:
UPD:我开始尝试自己解决问题,但遇到了以下问题:
当我的 OffsetX 处于离开状态时,为 LazyRow 添加 itemSpacing 时它会中断
此外,对于较长的 StickyHeader,我不知道如何正确设置偏移量以将其长度考虑在内。
@Composable
fun <K, V> LazyRowWithStickyHeader(
items: Map<K, List<V>>,
modifier: Modifier = Modifier,
state: LazyListState = rememberLazyListState(),
contentPadding: PaddingValues = PaddingValues(0.dp),
reverseLayout: Boolean = false,
horizontalArrangement: Arrangement.Horizontal = if (!reverseLayout) Arrangement.Start else Arrangement.End,
verticalAlignment: Alignment.Vertical = Alignment.Top,
flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
userScrollEnabled: Boolean = true,
stickyHeader: StickyHeaderScope.(K) -> Unit,
itemContent: @Composable LazyItemScope.(V) -> Unit
) {
val itemsWithKeys = remember(items) {
items.flatMap { entry -> entry.value.map { entry.key to it } }
}
val textMeasurer = rememberTextMeasurer()
var itemWidth by remember { mutableIntStateOf(0) }
var stickyHeaderHeight by remember { mutableStateOf(0.dp) }
Box(
modifier = Modifier.drawWithCache {
onDrawBehind {
var previousKey: K? = null
val startPadding = state.layoutInfo.beforeContentPadding
if (itemWidth == 0) {
itemWidth = state.layoutInfo.visibleItemsInfo.firstOrNull()?.size ?: 0
}
state.layoutInfo.visibleItemsInfo.forEachIndexed { index, itemInfo ->
val currentKey = itemsWithKeys.getOrNull(itemInfo.index)?.first
val nextItemKey = itemsWithKeys.getOrNull(itemInfo.index + 1)?.first
if (currentKey == null || currentKey == previousKey) {
return@forEachIndexed
}
StickyHeaderScopeImpl(
drawScope = this,
textMeasurer = textMeasurer,
offsetProvider = { size ->
stickyHeaderHeight = size.height.toDp()
val offsetX = when {
//Stickying
currentKey == nextItemKey && index == 0 -> {
startPadding
}
//Coming
currentKey == nextItemKey -> {
(itemInfo.offset + startPadding).coerceAtLeast(startPadding)
}
//Leaving
else -> {
itemInfo.offset + startPadding
}
}
Offset(x = offsetX.toFloat(), y = 0f)
}
).stickyHeader(currentKey)
previousKey = currentKey
}
}
}
) {
LazyRow(
modifier = modifier.padding(top = stickyHeaderHeight),
state = state,
contentPadding = contentPadding,
reverseLayout = reverseLayout,
horizontalArrangement = horizontalArrangement,
verticalAlignment = verticalAlignment,
flingBehavior = flingBehavior,
userScrollEnabled = userScrollEnabled
) {
items(
items = itemsWithKeys
) { (_, value) ->
itemContent(value)
}
}
}
}
fun StickyHeaderScope.drawStickyHeader(
text: String,
style: TextStyle,
color: Color = style.color,
overflow: TextOverflow = TextOverflow.Clip,
softWrap: Boolean = true,
maxLines: Int = Int.MAX_VALUE,
) {
val textLayout = textMeasurer.measure(
text = AnnotatedString(text),
style = style,
overflow = overflow,
softWrap = softWrap,
maxLines = maxLines
)
with(drawScope) {
drawText(
textLayoutResult = textLayout,
color = color,
topLeft = offsetProvider(textLayout.size)
)
}
}
interface StickyHeaderScope {
val drawScope: DrawScope
val textMeasurer: TextMeasurer
val offsetProvider: (IntSize) -> Offset
}
private class StickyHeaderScopeImpl(
override val drawScope: DrawScope,
override val textMeasurer: TextMeasurer,
override val offsetProvider: (IntSize) -> Offset
) : StickyHeaderScope
我的实现演示:
3
最佳答案
1
我曾研究过一个 Compose Multiplatform 库,它为此提供了解决方案:
预览显示水平和垂直粘性标题:
附言:仅提供解决方案的链接通常不太好,但在这种情况下,将源代码复制到这里会很困难。
|
–
–
–
|