返回

Jetpack Compose LazyColumn 中如何仅对首个 StickyHeader 应用动画?

Android

Jetpack Compose LazyColumn 中首个 StickyHeader 的动画

问题

在 Jetpack Compose 中的 LazyColumn 中,如何仅对首个 StickyHeader 应用动画?目前,动画应用于所有 StickyHeader,这会导致不必要的性能开销和用户体验不佳。

解决方法

可以使用组合 ScrollState.firstVisibleItemIndex、ScrollState.firstVisibleItemScrollOffset 和 ListScrollListener 来确定首个可见 StickyHeader 的索引,然后仅对该 StickyHeader 应用动画。

改进的代码

val listState = rememberLazyListState()
val firstVisibleItemIndex = remember { derivedStateOf { listState.firstVisibleItemIndex } }
val firstVisibleItemScrollOffset = remember { derivedStateOf { listState.firstVisibleItemScrollOffset } }
val topStickyHeaderIndex = remember { mutableStateOf(-1) } // 初始值为无效索引

val scrollChangeListener = remember {
    object : ListScrollListener {
        override fun onScrollStateChanged(state: ScrollState, previousState: ScrollState) {
            super.onScrollStateChanged(state, previousState)

            if (state.isScrollInProgress) {
                // 跟踪正在滚动的方向
                val direction = if (state.firstVisibleItemIndex < previousState.firstVisibleItemIndex) ScrollDirection.UP else ScrollDirection.DOWN

                when (direction) {
                    ScrollDirection.UP -> {
                        // 向上滚动
                        if (topStickyHeaderIndex.value == -1) {
                            // 尚未确定首个 StickyHeader,查找它
                            var index = 0
                            while (index < listState.layoutInfo.totalItemsCount) {
                                if (listState.isItemSticky(index)) {
                                    topStickyHeaderIndex.value = index
                                    break
                                }
                                index++
                            }
                        }
                    }
                    ScrollDirection.DOWN -> {
                        // 向下滚动
                        if (topStickyHeaderIndex.value >= 0) {
                            topStickyHeaderIndex.value = -1
                        }
                    }
                }
            }
        }
    }
}

LazyColumn(
    state = listState,
    modifier = modifier.nestedScroll(nestedScrollConnection).onScroll(scrollChangeListener)
) {
    categories.forEachIndexed { index, category ->
        stickyHeader {
            SessionCard(
                firstVisibleItemIndex = firstVisibleItemIndex.value,
                firstVisibleItemScrollOffset = firstVisibleItemScrollOffset.value,
                scale = if (index == topStickyHeaderIndex.value) scale.value else 1f,
                index = index
            )
        }
        items(category.items) { text ->
            CategoryItem(text)
        }
    }
}

结论

通过使用 LazyColumn 中提供的功能,我们可以有效地仅对首个 StickyHeader 应用动画。这可以显着提高性能并改善用户体验。

常见问题解答

  1. 是否可以仅对特定的 StickyHeader 应用动画,而不是首个?

    • 是,可以通过修改代码来实现。您需要确定您要应用动画的 StickyHeader 的索引,并相应地调整 if 语句。
  2. 如果 StickyHeader 可以出现在其组的任何位置,该怎么办?

    • 您需要修改代码以检查每个项是否是 StickyHeader。您可以通过调用 listState.isItemSticky() 来做到这一点。
  3. 是否有任何替代方法?

    • 有,您可以使用一个单独的 NestedScrollConnection 来跟踪首个可见的 StickyHeader。但是,这种方法可能不太高效。
  4. 这种方法是否与不同类型的 StickyHeader 兼容?

    • 是,这种方法与任何类型的 StickyHeader 兼容,只要它们是由 LazyColumn 管理的。
  5. 如何进一步优化动画性能?

    • 您可以通过使用 AnimatedContent 来仅在 StickyHeader 更改时更新动画来进一步优化性能。