当列表项在撰写中可拖动时,如何通过拖动打开导航抽屉? [英] How can Navigation Drawer be opened with a drag when a list item is draggable in compose?

本文介绍了当列表项在撰写中可拖动时,如何通过拖动打开导航抽屉?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我正在使用 Jetpack Compose 重写应用程序的 UI.我已经使用常规 Scaffold 功能实现了一个导航抽屉.开箱即用,这提供了两种打开抽屉的方法:按下导航图标或拖向屏幕末尾.有问题的屏幕是列表项的 LazyColumn.

我后来在这些列表项上实施了 SwipeToDismiss 模式.滑动以关闭工作正常,但不再可能拖动到任何地方以打开导航抽屉.

在旧的基于视图的系统中,导航抽屉会保留一个小宽度,您可以始终在其中拖动以打开抽屉 - 无论子项是否具有拖动支持.我不确定如何使用 Compose 实现相同的目标.处理这个似乎应该是导航抽屉的工作 - 而不是其中的屏幕.

带有导航抽屉的屏幕:

 val coroutineScope = rememberCoroutineScope()val scaffoldState = 记住脚手架状态(记住DrawerState(initialValue = DrawerValue.Closed))脚手架(scaffoldState = scaffoldState,顶栏 = {顶部应用栏(标题 = { 文字(屏幕标题)},导航图标 = {图标按钮(onClick = {coroutineScope.launch {scaffoldState.drawerState.open()}}){图标(Icons.Default.Menu,contentDescription = "抽屉切换按钮";)}},动作 = {...})},抽屉内容 = {//物品清单...},浮动操作按钮 = {...}) { 填充 ->///使用 LazyColumn 布局,元素具有 SwipeToDismiss...}

并滑动以关闭项目(显示在 LazyColumn 内)

@OptIn(ExperimentalFoundationApi::class,实验材料Api::class,实验动画Api::class)@可组合有趣的 SwipeableFeedItemPreview(onSwipe: 暂停 () ->单元,onlyUnread:布尔值,项目:FeedListItem,显示缩略图:布尔值,imagePainter:@Composable(字符串)->单元,onMarkAboveAsRead: () ->单元,onMarkBelowAsRead: () ->单元,onItemClick: () ->单元){val animationVisibilityState = 记住 { MutableTransitionState(true) }val swipeableState = rememberSwipeableState(initialValue = FeedItemSwipeState.NONE)//布局完成后需要设置var itemSize 通过记住 { mutableStateOf(Size(1f, 1f)) }val 锚点 = mapOf(0f 到 FeedItemSwipeState.NONE,-itemSize.width 到 FeedItemSwipeState.LEFT,itemSize.width 到 FeedItemSwipeState.RIGHT)动画可见性(可见状态 = 动画可见状态,输入 = 淡入(1f),退出=shrinkVertically(Alignment.CenterVertically)+fadeOut()){盒子(修饰符 = 修饰符.fillMaxWidth().onGloballyPositioned { layoutCoordinates ->itemSize = layoutCoordinates.size.toSize()}.swipeable(状态 = swipeableState,锚=锚,方向 = 方向.水平,阈值 = { _, _ ->分数阈值(0.25f)})){盒子(contentAlignment = swipeIconAlignment,修饰符 = 修饰符.matchParentSize().背景颜色).padding(水平= 24.dp)){动画可见性(可见 = swipeableState.targetValue != FeedItemSwipeState.NONE,输入 = 淡入(),退出 = 淡出()){图标(当(项目.未读){真的 ->Icons.Default.VisibilityOff假 ->图标.默认.可见性},contentDescription = stringResource(id = R.string.toggle_read_status))}}FeedItemPreview(项目 = 项目,显示缩略图 = 显示缩略图,图像画家 = 图像画家,onMarkAboveAsRead = onMarkAboveAsRead,onMarkBelowAsRead = onMarkBelowAsRead,onItemClick = onItemClick,修饰符 = 修饰符.offset { IntOffset(swipeableState.offset.value.roundToInt(), 0) })}}}

解决方案

您可以使用填充轻松减小可滑动范围,如下所示:

<预><代码>枚举类 FeedItemSwipeState {无,左,右,}@可组合有趣的测试视图(){val scaffoldState = 记住脚手架状态(记住DrawerState(initialValue = DrawerValue.Closed))脚手架(scaffoldState = scaffoldState,抽屉内容 = {},){val swipeableState = rememberSwipeableState(initialValue = FeedItemSwipeState.NONE)//布局完成后需要设置var itemSize 通过记住 { mutableStateOf(Size(1f, 1f)) }val 锚点 = mapOf(0f 到 FeedItemSwipeState.NONE,-itemSize.width 到 FeedItemSwipeState.LEFT,itemSize.width 到 FeedItemSwipeState.RIGHT)盒子(修饰符 = 修饰符.fillMaxWidth()){框(Modifier.fillMaxWidth()){盒子(修饰符 = 修饰符.matchParentSize().clickable {//在整个视图上可点击}.padding(start = 30.dp)//抽屉的左边距离.onGloballyPositioned { layoutCoordinates ->itemSize = layoutCoordinates.size.toSize()}.swipeable(//填充后可滑动以允许 drawerContent 工作状态 = swipeableState,锚点 = 锚点,方向 = 方向.水平,阈值 = { _, _ ->分数阈值(0.25f)}))文本(项目",修饰符 = 修饰符.offset { IntOffset(swipeableState.offset.value.roundToInt(), 0) })}}}}

我不确定 Scaffold 是否应该负责,如果您认为应该负责 - 在撰写问题跟踪器上创建问题

So I am rewriting an app's UI using Jetpack Compose. I have implemented a Navigation Drawer using the regular Scaffold function. Out of the box this provides two ways of opening the drawer: either press the navigationIcon or drag towards End of screen. The screen in question is a LazyColumn of list items.

I have at a later date implemented the SwipeToDismiss pattern on these list items. The swipe to dismiss works fine but it is no longer possible to drag anywhere to open the navigation drawer.

In the old View-based system, the navigation drawer would reserve a small width inside which you could always drag to open the drawer - regardless of child items having drag support. I am unsure how to achieve the same using Compose. It seems like it should be the job of the navigation drawer to handle this - and not a screen inside it.

The screen with navigation drawer:

    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
        ...
    }

and swipe to dismiss item (displayed inside 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) }
            )
        }
    }
}

解决方案

You can easily decrease swipeable range using padding, like this:


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()
        ) {
            Box(Modifier.fillMaxWidth()) {
                Box(
                    modifier = Modifier
                        .matchParentSize()
                        .clickable { // clickable on whole view
                        }
                        .padding(start = 30.dp) // left distance for drawer
                        .onGloballyPositioned { layoutCoordinates ->
                            itemSize = layoutCoordinates.size.toSize()
                        }
                        .swipeable( // swipeable after padding to allow drawerContent work
                            state = swipeableState,
                            anchors = anchors,
                            orientation = Orientation.Horizontal,
                            thresholds = { _, _ ->
                                FractionalThreshold(0.25f)
                            }
                        )
                )
                Text(
                    "item",
                    modifier = Modifier
                        .offset { IntOffset(swipeableState.offset.value.roundToInt(), 0) }
                )
            }
        }
    }
}

I'm not sure if that's something Scaffold should be responsible for, if you think it should - create an issue on compose issue tracker

这篇关于当列表项在撰写中可拖动时,如何通过拖动打开导航抽屉?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆