我正在尝试检测三种情况:
1.- 用户垂直(向下)滚动并通知隐藏按钮
2.- 用户停止滚动并通知隐藏按钮
3.- 用户垂直(向上)滚动并通知显示按钮
4.- 用户位于列表底部,没有更多项目并通知显示按钮。
我尝试过的是:
第一种方法是使用
nestedScrollConnection
如下
val isVisible = remember { MutableTransitionState(false) }
.apply { targetState = true }
val nestedScrollConnection = remember {
object : NestedScrollConnection {
override fun onPreScroll(
available: Offset,
source: NestedScrollSource
): Offset {
return Offset.Zero
}
override fun onPostScroll(
consumed: Offset,
available: Offset,
source: NestedScrollSource
): Offset {
isVisible.targetState = false
return super.onPostScroll(consumed, available, source)
}
}
}
LazyColumn(
modifier = Modifier
.padding(start = 16.dp, end = 16.dp, top = 16.dp)
.nestedScroll(nestedScrollConnection),
verticalArrangement = Arrangement.spacedBy(16.dp),
)
我尝试过的是当 y > 0 时上升,否则下降,但其他我不知道如何获得它们。
我遵循的另一种方法是:
val scrollState = rememberLazyListState()
LazyColumn(
modifier = Modifier
.padding(start = 16.dp, end = 16.dp, top = 16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp),
state = scrollState,
但是我不知道如何获取它是否是最后一项,如果滚动正在进行,我可以获取。
Skizo 的回答是有效的,但是这有点奇怪,因为如果你慢慢向上滚动,Y 有时不是我想要的,然后再次隐藏它,有没有办法留下一些滚动来开始对此做出反应?例如,滚动 X 像素即可开始显示/隐藏。
我想要的是根据滚动隐藏/显示一个浮动操作按钮(场景是上面的场景)
我找到了这种方式,但它使用了偏移量,我想为
FloatActionButton
设置动画,而不是像淡入/淡出一样从底部出现,我使用的是动画可见性,我得到了这个使用淡入/淡出,但现在,我如何调整 github 上的代码以使用 Animation Visibility
?并且当用户结束滚动时添加此内容,从现在开始,代码中只是在滚动时
以下是实现这些目标的方法,
1.) 检测滚动方向(垂直向上或垂直向下)
@Composable
private fun LazyListState.isScrollingUp(): Boolean {
var previousIndex by remember(this) { mutableStateOf(firstVisibleItemIndex) }
var previousScrollOffset by remember(this) { mutableStateOf(firstVisibleItemScrollOffset) }
return remember(this) {
derivedStateOf {
if (previousIndex != firstVisibleItemIndex) {
previousIndex > firstVisibleItemIndex
} else {
previousScrollOffset >= firstVisibleItemScrollOffset
}.also {
previousIndex = firstVisibleItemIndex
previousScrollOffset = firstVisibleItemScrollOffset
}
}
}.value
}
现在,只需创建一个 listState 变量并将其与此可组合项一起使用即可检索滚动方向。
val listState = rememberLazyListState()
val scrollingUp = listState.isScrollingUp()
然后,正如您所说,您希望在用户停止滚动时收到通知,因此您可以创建一个名为
restingScrollPosition
的变量,我将其简称为 rsp
。现在您已经熟悉了所需的帮助器方法,所需要做的就是设计一种机制来根据当前滚动位置的值触发事件,如果特定数量的值相同,则触发相关代码时间(卷轴的定义是“静止”)。
所以,就在这里
var rsp by remember { mutableStateOf(listState.firstVisibleItemScrollOffset) }
var isScrolling by remember { mutableStateOf(false) } // to keep track of the scroll-state
LaunchedEffect(key = listState.firstVisibleItemScrollOffset){ //Recomposes every time the key changes
/*This block will also be executed
on the start of the program,
so you might want to handle
that with the help of another variable.*/
isScrolling = true // If control reaches here, we're scrolling
launch{
isScrolling = false
delay(100) //If there's no scroll after a hundred seconds, update rsp
if(!isScrolling){
rsp = listState.firstVisibleItemScrollOffset
/* Execute your trigger here,
this denotes the scrolling has stopped */
}
}
}
我想我不会在这里解释最后一段代码的工作原理,请自己分析一下以更好地理解,这并不难。
啊,是的,要实现最后一个目标,您只需对 API 使用一点虚张声势,特别是像
lazyListState.layoutInfo
这样的方法。它包含您需要的有关屏幕上当前可见项目的所有信息,因此您还可以使用它来实现您的微妙需求,即您希望在触发代码块之前允许滚动一定量。只需查看对象中的可用信息,您就应该能够启动它。
更新:
根据昨天添加的评论中提供的信息,这应该是实施,
您的层次结构中的某个位置有一个 FAB,并且您希望根据
isScrollingUp
为其可见性设置动画,该动画绑定到层次结构中其他位置定义的惰性滚动器,
在这种情况下,您可以查看状态提升,这是声明式编程的一般最佳实践。
只需将
isScrollingUp()
输出提升到声明 FAB 的位置,或者如果您需要根据您的用例进行特定说明,请分享完整的代码。我需要整个家族能够帮助你解决这个问题。
几天前我遇到了同样的问题,我混合了你所说的方法。
要显示或隐藏,然后使用
Y
向上或向下滚动就足够了。
val nestedScrollConnection = remember {
object : NestedScrollConnection {
override fun onPreScroll(
available: Offset,
source: NestedScrollSource
): Offset {
val delta = available.y
isVisible.targetState = delta > 0
return Offset.Zero
}
}
}
并检测没有更多可以使用的物品
fun isLastItemVisible(lazyListState: LazyListState): Boolean {
val lastItem = layoutInfo.visibleItemsInfo.lastOrNull()
return lastItem == null || lastItem.size + lastItem.offset <= layoutInfo.viewportEndOffset
}
对于 1 和 3,如果您想在显示/隐藏某些内容之前指定特定的滚动距离,您可以创建一个具有自己状态的
NestedScrollConnection
。
这是一个实现示例,可能会给您一些想法。当到达
CustomScrollConnection.isScrollingDown
时true
会返回downThreshold
,当到达false
时会变成upThreshold
。
private class CustomScrollConnection(
val downThreshold: Dp = 0.dp,
val upThreshold: Dp = 0.dp,
density: Density
) : NestedScrollConnection {
var isScrollingDown by mutableStateOf(false)
private set
private val downThresholdPx = with(density) { -downThreshold.toPx() }
private val upThresholdPx = with(density) { upThreshold.toPx() }
private var isScrollingDownDelta = 0f
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
val delta = available.y
updateState(delta)
return Offset.Zero
}
private fun updateState(delta: Float) {
val isChangingDirections = isScrollingDownDelta.sign != delta.sign
isScrollingDownDelta = if (isChangingDirections) {
delta
} else {
isScrollingDownDelta + delta
}.coerceIn(downThresholdPx, upThresholdPx)
val isUpScroll = isScrollingDownDelta == upThresholdPx && delta > 0
val isDownScroll = isScrollingDownDelta == downThresholdPx && delta < 0
val continueDownScroll = isScrollingDown && !isUpScroll
isScrollingDown = continueDownScroll || isDownScroll
}
}
您可以在将其传递给
nestedScroll
修改器之前创建一个这样的实例。
val localDensity = LocalDensity.current
val customScrollConnection = remember(localDensity) {
CustomScrollConnection(
downThreshold = DOWN_SCROLL_THRESHOLD,
upThreshold = UP_SCROLL_THRESHOLD,
density = localDensity
)
}
需要额外的代码才能将其与
rememberSaveable
一起使用。
对于2,如果您正在使用
LazyListState
,您可以看看!lazyListState.isScrollInProgress
是否有帮助。
对于 4,您可以尝试使用
!lazyListState.canScrollForward
。或者,在 NestedScrollConnection
的实现中,如果您覆盖 onPostScroll
,则可以检查是否 consumed.y == 0f && available.y > 0f
;这可能意味着您已触底。