ОбнаружениеDragGestures на HorizontalPagerAndroid

Форум для тех, кто программирует под Android
Ответить
Anonymous
 ОбнаружениеDragGestures на HorizontalPager

Сообщение Anonymous »

У меня есть HorizontalPager с изображениями внутри прокручиваемой карты.
Вот как это выглядит
Изображение

И вы можете смахивать эти карточки вот так.
[img]https:// i.sstatic.net/485HW6Lj.jpg[/img]

Проблема в том, что когда я нажимаю на горизонтальный пейджер, он перехватывает все жесты перетаскивания, я хочу иметь возможность пролистывать его перепрокрутка. Если я на первом изображении - должен быть включен свайп влево, а если я на последнем -> свайп вправо.
Вот код горизонтального пейджер с модификатором смахивания:

Код: Выделить всё

Card(
modifier = modifier
.swipableCard(
state = swipeState,
onSwiped = {
when (it) {
Direction.Left -> onLeftSwipe()
Direction.Right -> onRightSwipe()
else -> {}
}
},
blockedDirections = listOf(Direction.Up, Direction.Down)
),
colors = CardDefaults.elevatedCardColors(containerColor = MaterialTheme.colors.surface),
shape = RoundedCornerShape(16.dp)
) {
Box(modifier = Modifier.fillMaxSize()) {
PostCardInfoUi(
post = post,
onDescriptionClicked = onDescriptionClicked,
onOwnProfileClicked = onOwnProfileClicked,
onOtherProfileClicked = onOtherProfileClicked,
pagerState = pagerState
)
ReactionOverlay(
modifier = Modifier.fillMaxSize(),
isLike = swipeState.offset.value.x > 0,
alpha = (absoluteOffsetDp.value / 100).coerceAtLeast(0f)
)
}
}
А вот сам модификатор смахивания со всем соответствующим кодом

Код: Выделить всё

fun Modifier.swipableCard(
state: SwipeableCardState,
onSwiped: (Direction) -> Unit,
onSwipeCancel: () -> Unit = {},
blockedDirections: List = listOf(Direction.Up, Direction.Down),
) = pointerInput(key1 = pagerPage, key2 = pagerCoordsRect) {
coroutineScope {
detectDragGestures(
onDragCancel = {
launch {
state.reset()
onSwipeCancel()
}
},
onDrag = { change, dragAmount ->
launch {
Log.i("SwipableCard", "Drag amount: $dragAmount")
val original = state.offset.targetValue
val summed = original + dragAmount
val newValue = Offset(
x = summed.x.coerceIn(-state.maxWidth, state.maxWidth),
y = summed.y.coerceIn(-state.maxHeight, state.maxHeight)
)
if (change.positionChange() != Offset.Zero) change.consume()
state.drag(newValue.x, newValue.y)
}
},
onDragEnd = {
launch {
val coercedOffset = state.offset.targetValue
.coerceIn(
blockedDirections,
maxHeight = state.maxHeight,
maxWidth = state.maxWidth
)

if (hasNotTravelledEnough(state, coercedOffset)) {
state.reset()
onSwipeCancel()
} else {
val horizontalTravel = abs(state.offset.targetValue.x)
val verticalTravel = abs(state.offset.targetValue.y)

if (horizontalTravel > verticalTravel) {
if (state.offset.targetValue.x > 0) {
state.swipe(Direction.Right)
onSwiped(Direction.Right)
} else {
state.swipe(Direction.Left)
onSwiped(Direction.Left)
}
} else {
if (state.offset.targetValue.y <  0) {
state.swipe(Direction.Up)
onSwiped(Direction.Up)
} else {
state.swipe(Direction.Down)
onSwiped(Direction.Down)
}
}
}
}
},

)
}
}.graphicsLayer {
translationX = state.offset.value.x
translationY = state.offset.value.y
rotationZ = (state.offset.value.x / 60).coerceIn(-40f, 40f)
}

private fun Offset.coerceIn(
blockedDirections: List,
maxHeight: Float,
maxWidth: Float,
): Offset {
return copy(
x = x.coerceIn(
if (blockedDirections.contains(Direction.Left)) {
0f
} else {
-maxWidth
},
if (blockedDirections.contains(Direction.Right)) {
0f
} else {
maxWidth
}
),
y = y.coerceIn(
if (blockedDirections.contains(Direction.Up)) {
0f
} else {
-maxHeight
},
if (blockedDirections.contains(Direction.Down)) {
0f
} else {
maxHeight
}
)
)
}

private fun hasNotTravelledEnough(
state: SwipeableCardState,
offset: Offset,
): Boolean {
return abs(offset.x) < state.maxWidth / 4 &&
abs(offset.y) < state.maxHeight / 4
}

enum class Direction {
Left, Right, Up, Down
}

@Composable
fun rememberSwipeableCardState(): SwipeableCardState {
val screenWidth = with(LocalDensity.current) {
LocalConfiguration.current.screenWidthDp.dp.toPx()
}
val screenHeight = with(LocalDensity.current) {
LocalConfiguration.current.screenHeightDp.dp.toPx()
}
return remember {
SwipeableCardState(screenWidth, screenHeight)
}
}

class SwipeableCardState(
internal val maxWidth: Float,
internal val maxHeight: Float,
) {
val offset = Animatable(offset(0f, 0f), Offset.VectorConverter)

/**
* The [Direction] the card was swiped at.
*
* Null value means the card has not been swiped fully yet.
*/
var swipedDirection: Direction? by mutableStateOf(null)
private set

internal suspend fun reset() {
offset.animateTo(offset(0f, 0f), tween(400))
}

suspend fun swipe(direction: Direction, animationSpec: AnimationSpec = tween(400)) {
val endX = maxWidth * 1.5f
val endY = maxHeight
when (direction) {
Direction.Left -> offset.animateTo(offset(x = -endX), animationSpec)
Direction.Right -> offset.animateTo(offset(x = endX), animationSpec)
Direction.Up -> offset.animateTo(offset(y = -endY), animationSpec)
Direction.Down -> offset.animateTo(offset(y = endY), animationSpec)
}
this.swipedDirection = direction
}

private fun offset(x: Float = offset.value.x, y: Float = offset.value.y): Offset {
return Offset(x, y)
}

internal suspend fun drag(x: Float, y: Float) {
offset.animateTo(offset(x, y))
}
}
Что я пробовал:
Я пытался написать свою собственную функцию расширения defineDragGestures и передать ей координаты HorizontalPager с текущим состоянием страницы (первое, среднее, последнее), но не смог этого сделать. Если я использую ввод указателя awaitFirstDown(), пейджер не получит его и не переместится. Если я подожду, пока touchSlop перейдет к решить, что делать (провести пейджер или провести карточкой) на основе направления positionChange() и координат position, горизонтальный пейджер использует указатель awaitFirstDown() и перетаскивания карты не происходит.
-

Подробнее здесь: https://stackoverflow.com/questions/789 ... ontalpager
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Android»