You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jetpack Compose实现列表点击后右侧滑入页面及左滑关闭方案咨询

Jetpack Compose实现侧滑页面切换(右滑进入+左滑关闭)

方案选择

两种实现方式都可行,取决于你的场景:

  • Navigation Component + 自定义转场动画:适合独立页面级别的跳转,符合Jetpack导航架构规范,便于管理页面栈。
  • 动画Composable:适合页面内的局部面板/弹窗场景,实现更轻量,无需依赖导航组件。

方案一:Navigation Component实现

核心逻辑

  1. 为导航配置自定义的右滑进入、左滑退出动画;
  2. 在详情页添加左滑手势检测,触发页面回退;
  3. 通过导航参数传递列表项数据。

代码示例

@Composable
fun MainScreen(navController: NavHostController) {
    // 模拟列表数据
    val items = List(10) { "Item $it" }

    LazyColumn {
        items(items) { item ->
            ListItem(
                headlineContent = { Text(item) },
                modifier = Modifier
                    .fillMaxWidth()
                    .clickable {
                        // 导航到详情页并传递数据
                        navController.navigate("detail/$item")
                    }
            )
        }
    }
}

@Composable
fun DetailScreen(navController: NavHostController, item: String) {
    var dragOffset by remember { mutableStateOf(0f) }
    val screenWidth = LocalConfiguration.current.screenWidthDp.dp.toPx()
    val dismissThreshold = screenWidth * 0.3f

    // 左滑关闭手势
    val dragModifier = Modifier.detectDragGestures(
        onDrag = { _, dragAmount ->
            // 只处理水平拖动,限制拖动范围在0到屏幕宽度之间
            dragOffset = (dragOffset + dragAmount.x).coerceIn(0f, screenWidth)
        },
        onDragEnd = {
            if (dragOffset >= dismissThreshold) {
                // 超过阈值,关闭页面
                navController.popBackStack()
            } else {
                // 重置位置
                dragOffset = 0f
            }
        }
    )

    Box(
        modifier = Modifier
            .fillMaxSize()
            .offset { IntOffset(dragOffset.toInt(), 0) }
            .then(dragModifier),
        contentAlignment = Alignment.Center
    ) {
        Text(text = "Detail for: $item", fontSize = 20.sp)
    }
}

@Composable
fun AppNavHost(navController: NavHostController) {
    val slideInTransition = remember {
        slideInHorizontally(initialOffsetX = { it }) + fadeIn()
    }
    val slideOutTransition = remember {
        slideOutHorizontally(targetOffsetX = { it }) + fadeOut()
    }

    NavHost(
        navController = navController,
        startDestination = "main"
    ) {
        composable("main") {
            MainScreen(navController)
        }
        composable(
            route = "detail/{item}",
            // 配置自定义转场动画
            enterTransition = { slideInTransition },
            exitTransition = { slideOutTransition }
        ) { backStackEntry ->
            val item = backStackEntry.arguments?.getString("item") ?: ""
            DetailScreen(navController, item)
        }
    }
}

// 在Activity中使用
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            val navController = rememberNavController()
            AppNavHost(navController)
        }
    }
}

方案二:动画Composable实现(无导航)

核心逻辑

  1. 用状态变量控制详情页的显示/隐藏;
  2. 通过animateOffsetAsState实现右滑进入/退出动画;
  3. 添加左滑手势检测,触发关闭状态;
  4. 点击列表项时传递数据并切换显示状态。

代码示例

@Composable
fun SlidePanelDemo() {
    val items = List(10) { "Item $it" }
    var selectedItem by remember { mutableStateOf<String?>(null) }
    var dragOffset by remember { mutableStateOf(0f) }
    val screenWidth = LocalConfiguration.current.screenWidthDp.dp.toPx()
    val dismissThreshold = screenWidth * 0.3f

    // 动画偏移量
    val animatedOffset by animateOffsetAsState(
        targetValue = if (selectedItem != null) Offset.Zero else Offset(screenWidth, 0f),
        animationSpec = tween(durationMillis = 300)
    )

    Box(modifier = Modifier.fillMaxSize()) {
        // 列表页面
        LazyColumn(modifier = Modifier.fillMaxSize()) {
            items(items) { item ->
                ListItem(
                    headlineContent = { Text(item) },
                    modifier = Modifier
                        .fillMaxWidth()
                        .clickable {
                            selectedItem = item
                            dragOffset = 0f
                        }
                )
            }
        }

        // 详情面板(覆盖在列表上)
        selectedItem?.let { item ->
            Box(
                modifier = Modifier
                    .fillMaxSize()
                    .offset { IntOffset((animatedOffset.x + dragOffset).toInt(), animatedOffset.y.toInt()) }
                    .background(Color.White)
                    .detectDragGestures(
                        onDrag = { _, dragAmount ->
                            dragOffset = (dragOffset + dragAmount.x).coerceIn(0f, screenWidth)
                        },
                        onDragEnd = {
                            if (dragOffset >= dismissThreshold) {
                                selectedItem = null
                            } else {
                                dragOffset = 0f
                            }
                        }
                    ),
                contentAlignment = Alignment.Center
            ) {
                Column {
                    Text(text = "Detail: $item", fontSize = 20.sp)
                    Spacer(modifier = Modifier.height(16.dp))
                    Text("左滑可以关闭面板")
                }
            }
        }
    }
}

// 调用示例
@Composable
fun App() {
    SlidePanelDemo()
}

内容的提问来源于stack exchange,提问作者Pawandeep Singh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 11:43:27