Compose 手势抽屉组件

293 阅读1分钟

1.从 Swipeable 迁移到 AnchoredDraggable

Api 变更

效果

录制_2025_04_30_14_28_57_827.gif

使用 anchoredDraggable修饰符

DrawerState 定义抽屉起始状态

sealed interface DrawerState {
    data object Open : DrawerState
    data object Closed : DrawerState
}

定义DrawerStateSaver保持当前抽屉状态

private val DrawerStateSaver = Saver<DrawerState, String>(save = {
    when (it) {
        is DrawerState.Open -> "Open"
        is DrawerState.Closed -> "Closed"
    }
}, restore = {
    when (it) {
        "Open" -> DrawerState.Open
        "Closed" -> DrawerState.Closed
        else -> DrawerState.Closed // 默认值
    }
})

使用offset/anchoredDraggable修饰符来进行拖拽平移效果

摆脱使用Modifier.draggable()

@OptIn(ExperimentalMaterialApi::class)
@Composable
fun MineScreen(modifier: Modifier = Modifier) {
    val context = LocalContext.current
   
    val density = LocalDensity.current

    //关闭状态时,剩余60.dp
    val drawerWidthClosed = remember(context) {
        with(density) { 60.dp.toPx() }
    }
    
    val drawerWidthOpen = remember(context) {
        min(
            context.resources.displayMetrics.widthPixels,
            context.resources.displayMetrics.heightPixels
        ) * 0.6f
    }

    //创建一个拖拽锚点实例
    val anchors = remember {
        DraggableAnchors {
            //完全打开
            DrawerState.Open at 0f
            //关闭剩余
            DrawerState.Closed at drawerWidthOpen - drawerWidthClosed
        }
    }
    
    //保持抽屉状态
    val drawerState = rememberSaveable(saver = DrawerStateSaver) {
        DrawerState.Closed
    }
    //定义AnchoredDraggableState
    val state =
        remember {
            AnchoredDraggableState(
                initialValue = drawerState,
                anchors = anchors
            )
        }
    
    //定义flingBehavior
    //默认位置阈值 50%
    val flingBehavior = AnchoredDraggableDefaults.flingBehavior(
        state = state, animationSpec = spring(dampingRatio = 0.4f, stiffness = 400f)
    )


    val scope = rememberCoroutineScope()
    //抽屉开闭
    fun toggleDrawerState() {
        scope.launch {
            if (state.currentValue == DrawerState.Open) {
                state.animateTo(
                    DrawerState.Closed,
                    //给一个回弹动画
                    animationSpec = spring(dampingRatio = 0.6f)
                )
            } else {
                state.animateTo(DrawerState.Open)
            }
        }
    }

   
    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(MaterialTheme.colorScheme.onSurface),
    ) {
       
       //主界面内容
       Box(){}
       //定义抽屉组件 
        Box(
            modifier = Modifier
                .fillMaxHeight()
                .width(with(density) { drawerWidthOpen.toDp() })
                .then(modifier)
                .offset {
                    //读取偏移量
                    IntOffset(x = state.requireOffset().toInt(), y = 0)
                }
                .graphicsLayer {
                    //打开状态阴影
                    shadowElevation = if (state.currentValue == DrawerState.Open) 16f else 0f
//                        translationX = state.requireOffset()
                    val scale = lerp(0.9f, 0.5f, state.requireOffset() / drawerWidthOpen)
                    scaleY = scale
                }
                .anchoredDraggable(
                    state = state,
                    orientation = Orientation.Horizontal,
                    flingBehavior = flingBehavior
                )
                .align(Alignment.CenterEnd)
                .background(
                    MaterialTheme.colorScheme.tertiary,
                    shape = RoundedCornerShape(topStart = 8.dp, bottomStart = 8.dp)
                )
        ) {
            Column(
                modifier = Modifier
                    .fillMaxSize()
                    .clickable {
                        toggleDrawerState()
                    }
                    .padding(10.dp)
            ) {
                Icon(
                    modifier = Modifier
                        .padding(bottom = 10.dp)
                        .size(20.dp)
                        .clickable {
                            toggleDrawerState()
                        },
                    imageVector = Icons.Default.Menu,
                    contentDescription = "menu",
                    tint = Color.White
                )
                Text(
                    "阿斯埃苏阿斯埃苏阿斯埃苏" + "阿斯埃苏阿斯埃苏阿斯埃苏阿斯埃苏阿斯埃苏" + "阿斯埃苏阿斯埃苏阿斯埃苏阿斯埃苏阿斯埃苏阿斯埃苏阿斯" + "埃苏阿斯埃苏阿斯埃苏阿斯埃苏阿斯埃苏阿斯埃苏阿斯埃苏阿斯埃苏阿斯埃苏",
                    color = Color.White
                )

            }
        }

    }
}