Перетаскиваемый дочерний элемент предотвращает перетаскивание панели навигации в Jetpack Compose
Поэтому я переписываю пользовательский интерфейс приложения с помощью Jetpack Compose. Я реализовал панель навигации, используя обычную функцию Scaffold. По умолчанию это предоставляет два способа открыть ящик: либо нажмите значок навигации, либо перетащите его в сторону конца экрана. Рассматриваемый экран представляет собой LazyColumn элементов списка.
Позже я реализовал шаблон SwipeToDismiss для этих элементов списка. Смахивание для закрытия работает нормально, но больше невозможно перетащить куда-либо, чтобы открыть панель навигации.
В старой системе, основанной на представлении, панель навигации оставляла за собой небольшую ширину, внутри которой вы всегда могли перетащить, чтобы открыть ящик, независимо от того, есть ли у дочерних элементов поддержку перетаскивания. Я не уверен, как добиться того же с помощью Compose. Похоже, что это должна быть работа навигационного ящика, а не экрана внутри него.
Экран с навигационным ящиком:
val coroutineScope = rememberCoroutineScope()
val scaffoldState = rememberScaffoldState(
rememberDrawerState(initialValue = DrawerValue.Closed)
)
Scaffold(
scaffoldState = scaffoldState,
topBar = {
TopAppBar(
title = { Text(screenTitle) },
navigationIcon = {
IconButton(
onClick = {
coroutineScope.launch {
scaffoldState.drawerState.open()
}
}
) {
Icon(
Icons.Default.Menu,
contentDescription = "Drawer toggle button"
)
}
},
actions = {
...
}
)
},
drawerContent = {
// List of stuff
...
},
floatingActionButton = {
...
}
) { padding ->
/// Layout with a LazyColumn with elements having SwipeToDismiss
...
}
и смахните, чтобы закрыть элемент (отображается внутри LazyColumn)
@OptIn(
ExperimentalFoundationApi::class,
ExperimentalMaterialApi::class,
ExperimentalAnimationApi::class
)
@Composable
fun SwipeableFeedItemPreview(
onSwipe: suspend () -> Unit,
onlyUnread: Boolean,
item: FeedListItem,
showThumbnail: Boolean,
imagePainter: @Composable (String) -> Unit,
onMarkAboveAsRead: () -> Unit,
onMarkBelowAsRead: () -> Unit,
onItemClick: () -> Unit
) {
val animatedVisibilityState = remember { MutableTransitionState(true) }
val swipeableState = rememberSwipeableState(initialValue = FeedItemSwipeState.NONE)
// Needs to be set once layout is complete
var itemSize by remember { mutableStateOf(Size(1f, 1f)) }
val anchors = mapOf(
0f to FeedItemSwipeState.NONE,
-itemSize.width to FeedItemSwipeState.LEFT,
itemSize.width to FeedItemSwipeState.RIGHT
)
AnimatedVisibility(
visibleState = animatedVisibilityState,
enter = fadeIn(1f),
exit = shrinkVertically(Alignment.CenterVertically) + fadeOut()
) {
Box(
modifier = Modifier
.fillMaxWidth()
.onGloballyPositioned { layoutCoordinates ->
itemSize = layoutCoordinates.size.toSize()
}
.swipeable(
state = swipeableState,
anchors = anchors,
orientation = Orientation.Horizontal,
thresholds = { _, _ ->
FractionalThreshold(0.25f)
}
)
) {
Box(
contentAlignment = swipeIconAlignment,
modifier = Modifier
.matchParentSize()
.background(color)
.padding(horizontal = 24.dp)
) {
AnimatedVisibility(
visible = swipeableState.targetValue != FeedItemSwipeState.NONE,
enter = fadeIn(),
exit = fadeOut()
) {
Icon(
when (item.unread) {
true -> Icons.Default.VisibilityOff
false -> Icons.Default.Visibility
},
contentDescription = stringResource(id = R.string.toggle_read_status)
)
}
}
FeedItemPreview(
item = item,
showThumbnail = showThumbnail,
imagePainter = imagePainter,
onMarkAboveAsRead = onMarkAboveAsRead,
onMarkBelowAsRead = onMarkBelowAsRead,
onItemClick = onItemClick,
modifier = Modifier
.offset { IntOffset(swipeableState.offset.value.roundToInt(), 0) }
)
}
}
}
2 ответа
Вы можете легко уменьшить диапазон прокрутки, используя отступы, например:
AnimatedVisibility(
visibleState = animatedVisibilityState,
enter = fadeIn(1f),
exit = shrinkVertically(Alignment.CenterVertically) + fadeOut()
) {
Box(
modifier = Modifier
.fillMaxWidth()
) {
Box(
modifier = Modifier
.matchParentSize()
.padding(start = 30.dp) // left distance for drawer
.onGloballyPositioned { layoutCoordinates ->
itemSize = layoutCoordinates.size.toSize()
}
.swipeable(
state = swipeableState,
anchors = anchors,
orientation = Orientation.Horizontal,
thresholds = { _, _ ->
FractionalThreshold(0.25f)
}
)
)
Box(
contentAlignment = swipeIconAlignment,
modifier = Modifier
.matchParentSize()
.background(color)
.padding(horizontal = 24.dp)
) {
AnimatedVisibility(
visible = swipeableState.targetValue != FeedItemSwipeState.NONE,
enter = fadeIn(),
exit = fadeOut()
) {
Icon(
when (item.unread) {
true -> Icons.Default.VisibilityOff
false -> Icons.Default.Visibility
},
contentDescription = stringResource(id = R.string.toggle_read_status)
)
}
}
FeedItemPreview(
item = item,
showThumbnail = showThumbnail,
imagePainter = imagePainter,
onMarkAboveAsRead = onMarkAboveAsRead,
onMarkBelowAsRead = onMarkBelowAsRead,
onItemClick = onItemClick,
modifier = Modifier
.offset { IntOffset(swipeableState.offset.value.roundToInt(), 0) }
)
}
}
Я не уверен, что это что-то
Scaffold
должен нести ответственность, если вы считаете, что должен - создать проблему в системе отслеживания проблем
Это тот подход, который я в конечном итоге использовал после того, как Филип дал свой ответ. Он немного «менее квадратный». В итоге - ключ был просто в том, чтобы позволить родительскому блоку обрабатывать щелчок - позволяя отдельному блоку сосредоточиться исключительно на смахивании - а сам элемент фида не обрабатывает щелчки
enum class FeedItemSwipeState {
NONE, LEFT, RIGHT,
}
@Composable
fun TestView(
) {
val scaffoldState = rememberScaffoldState(
rememberDrawerState(initialValue = DrawerValue.Closed)
)
Scaffold(
scaffoldState = scaffoldState,
drawerContent = {
},
) {
val swipeableState = rememberSwipeableState(initialValue = FeedItemSwipeState.NONE)
// Needs to be set once layout is complete
var itemSize by remember { mutableStateOf(Size(1f, 1f)) }
val anchors = mapOf(
0f to FeedItemSwipeState.NONE,
-itemSize.width to FeedItemSwipeState.LEFT,
itemSize.width to FeedItemSwipeState.RIGHT
)
Box(
modifier = Modifier
.fillMaxWidth()
.onGloballyPositioned { layoutCoordinates ->
itemSize = layoutCoordinates.size.toSize()
}
.combinedClickable(
onLongClick = { ... },
onClick = { ... },
)
) {
Box(
modifier = Modifier
.padding(start = 48.dp)
.matchParentSize()
.swipeable(
state = swipeableState,
anchors = anchors,
orientation = Orientation.Horizontal,
thresholds = { _, _ ->
FractionalThreshold(0.25f)
}
)
)
FeedItemPreview(
item = "item",
swipeableModifier = Modifier
.padding(start = 30.dp) // left distance for drawer
.onGloballyPositioned { layoutCoordinates ->
itemSize = layoutCoordinates.size.toSize()
}
.swipeable(
state = swipeableState,
anchors = anchors,
orientation = Orientation.Horizontal,
thresholds = { _, _ ->
FractionalThreshold(0.25f)
}
)
,
modifier = Modifier
.offset { IntOffset(swipeableState.offset.value.roundToInt(), 0) }
)
}
}
}
@Composable
fun FeedItemPreview(
item: String,
modifier: Modifier,
) {
Text(
item,
modifier = modifier
)
}
В примере в приложении, где прокручиваемая область выделена рамкой: