如何在Jetpack Compose中将可滚动项捕捉到屏幕中心
从 Snapper 迁移到 SnapFlingBehavior
 
之前,我们探讨了Android应用程序开发中一个常见的用例:在fling手势后将可滚动项捕捉到屏幕中心。
为了在Jetpack Compose中实现这一目标,我们使用了之前被存档的"Snapper"库。当官方框架还没有支持时,Snapper提供了一种无缝的解决方案,用于在可滚动布局中实现捕捉行为。如果你错过了那篇文章,你可以在这里阅读。
自那时以来,Jetpack Compose已经发展壮大,好消息是,我们之前使用Snapper库实现的功能现在已经在Jetpack Compose中得到官方支持,引入了SnapFlingBehavior。这个新的类提供了一个更全面和高效的解决方案,用于在具有动态内容的可滚动列表中将项居中。
在本文中,我们将重新讨论我们在之前的文章中解决的问题,并演示如何从使用Snapper库过渡到官方支持的SnapFlingBehavior。我们将首先讨论两种方法之间的主要区别,然后提供一步一步的指南,用于在Jetpack Compose项目中实现SnapFlingBehavior。
转换为SnapFlingBehavior
为了快速回顾一下,让我们来看看我们使用Snapper实现的核心功能。该库允许我们创建一个动态的可滚动列表,在fling手势后将项目捕捉到屏幕中心。当Jetpack Compose没有官方实现此用例的功能时,它是一个很好的解决方案。
 通过引入SnapFlingBehavior,我们现在可以使用Jetpack Compose的官方API实现此捕捉行为。相比Snapper库,SnapFlingBehavior提供了更大的灵活性和自定义选项。
 SnapFlingBehavior类使项目捕捉到给定位置,使用shortSnapVelocityThreshold参数区分短/滚动捕捉和长/fling捕捉。此外,它还为不同情况提供了各种动画规格,例如highVelocityAnimationSpec、lowVelocityAnimationSpec和snapAnimationSpec。
 现在让我们看看如何利用SnapFlingBehavior来实现我们之前使用Snapper库实现的相同功能。
重新审视我们以前的用例
在我上一篇关于Snapper库的文章中,我们使用了一个动态列表的例子:
@Composable
fun MainContent(
    placeholderItems: List<String>
) {
    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Text(
            modifier = Modifier
                .padding(16.dp)
                .fillMaxWidth(),
            text = "Example Horizontal LazyRow"
        )
        LazyRow(
            modifier = Modifier.fillMaxWidth(),
            contentPadding = PaddingValues(8.dp),
            verticalAlignment = Alignment.CenterVertically
        ) {
            items(items = placeholderItems) { itemMessage: String ->
                PlaceholderCard(itemMessage)
            }
        }
    }
}
实际效果如下:
 
 我们可以使用 Snapper 实现 fling 行为,如下代码片段所示:
val lazyListState: LazyListState = rememberLazyListState()
  val layoutInfo: LazyListSnapperLayoutInfo = rememberLazyListSnapperLayoutInfo(lazyListState)
  LazyRow(
      modifier = Modifier.fillMaxWidth(),
      state = lazyListState,
      flingBehavior = rememberSnapperFlingBehavior(lazyListState),
      contentPadding = PaddingValues(8.dp),
      verticalAlignment = Alignment.CenterVertically,
  ) {
      items(items = placeholderItems) { itemMessage: String ->
          PlaceholderCard(itemMessage)
      }
  }
使用SnapFlingBehavior
但是,为了确保在执行 fling 手势后可见项目滚动到屏幕中心,我们不再需要依赖外部库。相反,我们可以利用前面讨论的 SnapFlingBehavior。
下面的代码片段显示了启用 SnapFlingBehavior 的 MainContent:
@Composable
fun MainContent(
    placeholderItems: List<String>
) {
    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Text(
            modifier = Modifier
                .padding(16.dp)
                .fillMaxWidth(),
            text = "Example Horizontal LazyRow"
        )
        val listState: LazyListState = rememberLazyListState()
        LazyRow(
            modifier = Modifier.fillMaxWidth(),
            contentPadding = PaddingValues(horizontal = 32.dp),
            verticalAlignment = Alignment.CenterVertically,
            state = listState,
            flingBehavior = rememberSnapFlingBehavior(listState)
        ) {
            items(placeholderItems) { item: String ->
                PlaceholderCard(itemMessage = item)
            }
        }
    }
}
首先,我们使用rememberLazyListState()函数创建一个LazyListState,这允许我们存储和管理我们的LazyRow组件的状态:
val listState: LazyListState = rememberLazyListState()
接下来,我们使用以下属性定义了 LazyRow 组件:
- modifier = Modifier.fillMaxWidth():确保- LazyRow横跨整个屏幕的宽度。
- contentPadding = PaddingValues(horizontal = 32.dp):在- LazyRow内部应用水平方向为 32.dp 的填充。
- verticalAlignment = Alignment.CenterVertically:将- LazyRow中的项在垂直方向上居中对齐。
- state = listState:将我们之前定义的- LazyListState设置为- LazyRow的状态。
- flingBehavior = rememberSnapFlingBehavior(listState):通过提供- listState,将- SnapFlingBehavior分配给- LazyRow,从而使列表中的项在 fling 手势后捕捉到中心并最终实现所需的行为。
最终效果如下:
 
结论
本文中,我们重温了在Jetpack Compose中将可滚动项捕捉到屏幕中央的问题,并从使用Snapper库转换为官方支持的SnapFlingBehavior。
 我们首先看到了我们以前是如何实现动态、可滚动列表的,它在手势滑动后自动将项目捕捉到屏幕中央。随后,我们讨论了使用官方解决方案的相同方法。最后,我们发现在代码长度方面的差异并不大,迁移过程非常简单。



















