Reputation: 937
I'am new to jetpack compose and i really liked it. But ran into a problem : I want know if my view is swiped up or down so i created a LazyColumn with some item in it to be able to scroll something. It work fine but i would like to access the Gesture property to know if the view is scrolled down or up, here is my code :
LazyColumn{
items (100){
Text(
text = "Item $it",
fontSize = 24.sp,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxSize()
.padding(vertical = 24.dp)
.pointerInput(Unit) {
detectDragGestures { change, dragAmount ->
//change.consumeAllChanges()// i don't know if this does something, i tried to remove it
println("detectDragGestures")
val (x, y) = dragAmount
if(abs(x) < abs(y)){
if (y > 0)
println("drag down")
else
println("drag Up")
}
}
})
}
}
This work, i can detect if the view is scrolled down or up, the problem is when i tap on the item and scroll, i get the right print but the view isn't scrolled, i have to click between item to be able to scroll.
I don't really know how gesture work in jetpack compose but i would like to get the direction of the swipe without preventing my view to be scrolled.
Upvotes: 3
Views: 4683
Reputation: 66869
Explanation of how gesture system in Jetpack Compose works in detail here and here
change.consumeAllChanges()
is deprecated now, partial consumes are deprecated. change.consume()
is the only consume function to be used. What consume does is it returns change.isConsumed
true and because of that any drag, scroll, transform gesture stops progressing or receiving events. detectDragGestures and detectTransformGestures consume events by default so next
Modifier.pointerInput()
doesn't get these events. What you commented doesn't mean anything since drag already consumes events.
Here it's source code.
suspend fun PointerInputScope.detectDragGestures(
onDragStart: (Offset) -> Unit = { },
onDragEnd: () -> Unit = { },
onDragCancel: () -> Unit = { },
onDrag: (change: PointerInputChange, dragAmount: Offset) -> Unit
) {
forEachGesture {
awaitPointerEventScope {
val down = awaitFirstDown(requireUnconsumed = false)
var drag: PointerInputChange?
var overSlop = Offset.Zero
do {
drag = awaitPointerSlopOrCancellation(
down.id,
down.type
) { change, over ->
change.consume()
overSlop = over
}
// ! EVERY Default movable GESTURE HAS THIS CHECK
} while (drag != null && !drag.isConsumed)
if (drag != null) {
onDragStart.invoke(drag.position)
onDrag(drag, overSlop)
if (
!drag(drag.id) {
onDrag(it, it.positionChange())
it.consume()
}
) {
onDragCancel()
} else {
onDragEnd()
}
}
}
}
}
What you can do is using nestedScroll on parent of LazyColumn as here
@Composable
private fun NestedScrollExample() {
var text by remember { mutableStateOf("") }
val nestedScrollConnection = remember {
object : NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
text = "onPreScroll()\n" +
"available: $available\n" +
"source: $source\n\n"
return super.onPreScroll(available, source)
}
override fun onPostScroll(
consumed: Offset,
available: Offset,
source: NestedScrollSource
): Offset {
text += "onPostScroll()\n" +
"consumed: $consumed\n" +
"available: $available\n" +
"source: $source\n\n"
return super.onPostScroll(consumed, available, source)
}
override suspend fun onPreFling(available: Velocity): Velocity {
text += "onPreFling()\n" +
" available: $available\n\n"
return super.onPreFling(available)
}
override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
text += "onPostFling()\n" +
"consumed: $consumed\n" +
"available: $available\n\n"
return super.onPostFling(consumed, available)
}
}
}
Column() {
Box(
Modifier
.weight(1f)
.nestedScroll(nestedScrollConnection)
) {
LazyColumn(
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
items(100) {
Text(
text = "I'm item $it",
modifier = Modifier
.shadow(1.dp, RoundedCornerShape(5.dp))
.fillMaxWidth()
.background(Color.LightGray)
.padding(12.dp),
fontSize = 16.sp,
color = Color.White
)
}
}
}
Spacer(modifier = Modifier.height(10.dp))
Text(
text = text,
modifier = Modifier
.fillMaxWidth()
.verticalScroll(rememberScrollState())
.height(250.dp)
.padding(10.dp)
.background(BlueGrey400),
fontSize = 16.sp,
color = Color.White
)
}
}
It will return if you scroll up or down on your LazyColumn, if you want to do this only when your items are being scrolled you can do it as
Column(modifier = Modifier.fillMaxSize()) {
var text by remember { mutableStateOf("Drag to see effects") }
Text(text)
LazyColumn {
items(100) {
Text(
text = "Item $it",
fontSize = 24.sp,
textAlign = TextAlign.Center,
modifier = Modifier
.border(3.dp, Color.Green)
.fillMaxSize()
.padding(vertical = 24.dp)
.pointerMotionEvents(Unit,
onDown = {
it.consume()
},
onMove = { change ->
val position = change.positionChange()
val dragText = if (position.y < 0) {
"drag up"
} else if (position.y > 0) {
"drag down"
} else {
"idle"
}
text = "position: ${change.position}\n" +
"positionChange: ${change.positionChange()}\n" +
"dragText: $dragText"
}
)
)
}
}
}
Modifier.pointerMotionEvents() is a gesture Modifier i wrote, it's available here.
Upvotes: 2
Reputation: 355
I managed to detect scroll direction with using Column instead of LazyColumn.
I hope it can lead you.
@Composable
fun ScrollDetect() {
val scrollState = rememberScrollState()
var dragPosition by remember {
mutableStateOf(0)
}
LaunchedEffect(key1 = scrollState.value, block = {
if (scrollState.value > dragPosition)
Log.e("dragging", "up")
else
Log.e("dragging", "down")
dragPosition = scrollState.value
})
Column(
modifier = Modifier
.verticalScroll(scrollState)
)
{
repeat(100) {
Text(
text = "Item $it",
fontSize = 24.sp,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxSize()
.padding(vertical = 24.dp)
)
}
}
}
Upvotes: 0